From 24902841216ac6d24605d6b4530f0ffd5ee60386 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 1 Aug 2013 19:20:41 -0500 Subject: [PATCH 001/525] Fixed cross-domain iFrame Transport settings. --- js/app.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/js/app.js b/js/app.js index 0475ab453..60a3fed49 100644 --- a/js/app.js +++ b/js/app.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload Plugin Angular JS Example 1.2.0 + * jQuery File Upload Plugin Angular JS Example 1.2.1 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2013, Sebastian Tschan @@ -24,9 +24,13 @@ .config([ '$httpProvider', 'fileUploadProvider', function ($httpProvider, fileUploadProvider) { + delete $httpProvider.defaults.headers.common['X-Requested-With']; + fileUploadProvider.defaults.redirect = window.location.href.replace( + /\/[^\/]*$/, + '/cors/result.html?%s' + ); if (isOnGitHub) { // Demo settings: - delete $httpProvider.defaults.headers.common['X-Requested-With']; angular.extend(fileUploadProvider.defaults, { // Enable image resizing, except for Android and Opera, // which actually support image resizing, but fail to @@ -42,12 +46,12 @@ .controller('DemoFileUploadController', [ '$scope', '$http', '$filter', '$window', - function ($scope, $http, $filter, $window) { + function ($scope, $http) { + $scope.options = { + url: url + }; if (!isOnGitHub) { $scope.loadingFiles = true; - $scope.options = { - url: url - }; $http.get(url) .then( function (response) { From 995f0ef38b21c7f1cd1cb833ad4655515df4060d Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 1 Aug 2013 19:39:30 -0500 Subject: [PATCH 002/525] Added an updated jQuery UI example implementation. --- README.md | 31 ++-- angularjs.html | 3 +- basic-plus.html | 3 +- basic.html | 3 +- blueimp-file-upload.jquery.json | 2 +- bower.json | 8 +- css/demo.css | 67 ++++++++ index.html | 7 +- jquery-ui.html | 249 ++++++++++++++++++++++++++++++ js/jquery.fileupload-jquery-ui.js | 138 +++++++++++++++++ package.json | 8 +- 11 files changed, 495 insertions(+), 24 deletions(-) create mode 100644 css/demo.css create mode 100644 jquery-ui.html create mode 100755 js/jquery.fileupload-jquery-ui.js diff --git a/README.md b/README.md index 78266ec19..1f59e45eb 100644 --- a/README.md +++ b/README.md @@ -57,24 +57,37 @@ Please read the [issue guidelines](https://github.com/blueimp/jQuery-File-Upload Works with any server-side platform (PHP, Python, Ruby on Rails, Java, Node.js, Go etc.) that supports standard HTML form file uploads. ## Requirements + +### Mandatory requirements * [jQuery](http://jquery.com/) v. 1.6+ * [jQuery UI widget factory](http://api.jqueryui.com/jQuery.widget/) v. 1.9+ (included) * [jQuery Iframe Transport plugin](https://github.com/blueimp/jQuery-File-Upload/blob/master/js/jquery.iframe-transport.js) (included) -* [JavaScript Templates engine](https://github.com/blueimp/JavaScript-Templates) v. 2.2.1+ (optional) -* [JavaScript Load Image function](https://github.com/blueimp/JavaScript-Load-Image) v. 1.7.3+ (optional) -* [JavaScript Canvas to Blob function](https://github.com/blueimp/JavaScript-Canvas-to-Blob) v. 2.0.6+ (optional) -* [Bootstrap CSS Toolkit](https://github.com/twitter/bootstrap/) v. 2.3+ (optional) -The jQuery UI widget factory is a requirement for the basic File Upload plugin, but very lightweight without any other dependencies. -The jQuery Iframe Transport is required for [browsers without XHR file upload support](https://github.com/blueimp/jQuery-File-Upload/wiki/Browser-support). -The UI version of the File Upload plugin also requires the JavaScript Templates engine as well as the JavaScript Load Image and JavaScript Canvas to Blob functions (for the image previews and resizing functionality). These dependencies are marked as optional, as the basic File Upload plugin can be used without them and the UI version of the plugin can be extended to override these dependencies with alternative solutions. +The jQuery UI widget factory is a requirement for the basic File Upload plugin, but very lightweight without any other dependencies from the jQuery UI suite. + +The jQuery Iframe Transport is required for [browsers without XHR file upload support](https://github.com/blueimp/jQuery-File-Upload/wiki/Browser-support). + +### Optional requirements +* [JavaScript Templates engine](https://github.com/blueimp/JavaScript-Templates) v. 2.3.0+ +* [JavaScript Load Image library](https://github.com/blueimp/JavaScript-Load-Image) v. 1.9.1+ +* [JavaScript Canvas to Blob polyfill](https://github.com/blueimp/JavaScript-Canvas-to-Blob) v. 2.0.7+ +* [blueimp Gallery](https://github.com/blueimp/Gallery) v. 2.7.1+ +* [Bootstrap CSS Toolkit](https://github.com/twitter/bootstrap/) v. 2.3 -The User Interface is built with Twitter's [Bootstrap](https://github.com/twitter/bootstrap/) Toolkit. This enables a CSS based, responsive layout and fancy transition effects on modern browsers. The demo also includes the [blueimp Gallery](https://github.com/blueimp/Gallery). Both of these components are optional and not required. +The JavaScript Templates engine is used to render the selected and uploaded files for the Basic Plus UI and jQuery UI versions. -The repository also includes the [jQuery XDomainRequest Transport plugin](https://github.com/blueimp/jQuery-File-Upload/blob/master/js/cors/jquery.xdr-transport.js), which enables Cross-domain AJAX requests (GET and POST only) in Microsoft Internet Explorer >= 8. However, the XDomainRequest object doesn't support file uploads and the plugin is only used by the [Demo](http://blueimp.github.io/jQuery-File-Upload/) for Cross-domain requests to delete uploaded files from the demo file upload service. +The JavaScript Load Image library and JavaScript Canvas to Blob polyfill are required for the image previews and resizing functionality. +The blueimp Gallery is used to display the uploaded images in a lightbox. + +The user interface of all versions except the jQuery UI version is built with Twitter's [Bootstrap](https://github.com/twitter/bootstrap/) Toolkit. + +### Cross-domain requirements [Cross-domain File Uploads](https://github.com/blueimp/jQuery-File-Upload/wiki/Cross-domain-uploads) using the [Iframe Transport plugin](https://github.com/blueimp/jQuery-File-Upload/blob/master/js/jquery.iframe-transport.js) require a redirect back to the origin server to retrieve the upload results. The [example implementation](https://github.com/blueimp/jQuery-File-Upload/blob/master/js/main.js) makes use of [result.html](https://github.com/blueimp/jQuery-File-Upload/blob/master/cors/result.html) as a static redirect page for the origin server. +The repository also includes the [jQuery XDomainRequest Transport plugin](https://github.com/blueimp/jQuery-File-Upload/blob/master/js/cors/jquery.xdr-transport.js), which enables limited cross-domain AJAX requests in Microsoft Internet Explorer 8 and 9 (IE 10 supports cross-domain XHR requests). +The XDomainRequest object allows GET and POST requests only and doesn't support file uploads. It is used on the [Demo](http://blueimp.github.io/jQuery-File-Upload/) to delete uploaded files from the cross-domain demo file upload service. + ## Browsers ### Desktop browsers diff --git a/angularjs.html b/angularjs.html index ff4cf7973..b0655dc1f 100644 --- a/angularjs.html +++ b/angularjs.html @@ -70,6 +70,7 @@

AngularJS version

  • Basic Plus
  • Basic Plus UI
  • AngularJS
  • +
  • jQuery UI
  • File Upload widget with multiple file selection, drag&drop support, progress bars, validation and preview images, audio and video for AngularJS.
    @@ -156,7 +157,7 @@

    Demo Notes

  • The maximum file size for uploads in this demo is 5 MB (default file size is unlimited).
  • Only image files (JPG, GIF, PNG) are allowed in this demo (by default there is no file type restriction).
  • Uploaded files will be deleted automatically after 5 minutes (demo setting).
  • -
  • You can drag & drop files from your desktop on this webpage with Google Chrome, Mozilla Firefox and Apple Safari.
  • +
  • You can drag & drop files from your desktop on this webpage (see Browser support).
  • Please refer to the project website and documentation for more information.
  • Built with Twitter's Bootstrap toolkit and Icons from Glyphicons.
  • diff --git a/basic-plus.html b/basic-plus.html index a5a8903db..a104fd291 100644 --- a/basic-plus.html +++ b/basic-plus.html @@ -62,6 +62,7 @@

    Basic Plus version

  • Basic Plus
  • Basic Plus UI
  • AngularJS
  • +
  • jQuery UI
  • File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images, audio and video for jQuery.
    @@ -91,7 +92,7 @@

    Demo Notes

  • The maximum file size for uploads in this demo is 5 MB (default file size is unlimited).
  • Only image files (JPG, GIF, PNG) are allowed in this demo (by default there is no file type restriction).
  • Uploaded files will be deleted automatically after 5 minutes (demo setting).
  • -
  • You can drag & drop files from your desktop on this webpage with Google Chrome, Mozilla Firefox and Apple Safari.
  • +
  • You can drag & drop files from your desktop on this webpage (see Browser support).
  • Please refer to the project website and documentation for more information.
  • Built with Twitter's Bootstrap toolkit and Icons from Glyphicons.
  • diff --git a/basic.html b/basic.html index b72ad05ee..ee9205949 100644 --- a/basic.html +++ b/basic.html @@ -62,6 +62,7 @@

    Basic version

  • Basic Plus
  • Basic Plus UI
  • AngularJS
  • +
  • jQuery UI
  • File Upload widget with multiple file selection, drag&drop support and progress bar for jQuery.
    @@ -91,7 +92,7 @@

    Demo Notes

  • The maximum file size for uploads in this demo is 5 MB (default file size is unlimited).
  • Only image files (JPG, GIF, PNG) are allowed in this demo (by default there is no file type restriction).
  • Uploaded files will be deleted automatically after 5 minutes (demo setting).
  • -
  • You can drag & drop files from your desktop on this webpage with Google Chrome, Mozilla Firefox and Apple Safari.
  • +
  • You can drag & drop files from your desktop on this webpage (see Browser support).
  • Please refer to the project website and documentation for more information.
  • Built with Twitter's Bootstrap toolkit and Icons from Glyphicons.
  • diff --git a/blueimp-file-upload.jquery.json b/blueimp-file-upload.jquery.json index 2dd5ef625..fd5d203de 100644 --- a/blueimp-file-upload.jquery.json +++ b/blueimp-file-upload.jquery.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "8.6.1", + "version": "8.7.0", "title": "jQuery File Upload", "author": { "name": "Sebastian Tschan", diff --git a/bower.json b/bower.json index 65545b6e2..5d26a9049 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "8.6.1", + "version": "8.7.0", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images, audio and video for jQuery. Supports cross-domain, chunked and resumable file uploads. Works with any server-side platform (Google App Engine, PHP, Python, Ruby on Rails, Java, etc.) that supports standard HTML form file uploads.", "keywords": [ @@ -48,9 +48,9 @@ ], "dependencies": { "jquery": ">=1.6", - "blueimp-tmpl": ">=2.2.1", - "blueimp-load-image": ">=1.7.3", - "blueimp-canvas-to-blob": ">=2.0.6" + "blueimp-tmpl": ">=2.3.0", + "blueimp-load-image": ">=1.9.1", + "blueimp-canvas-to-blob": ">=2.0.7" }, "main": [ "js/vendor/jquery.ui.widget.js", diff --git a/css/demo.css b/css/demo.css new file mode 100644 index 000000000..1721fcf74 --- /dev/null +++ b/css/demo.css @@ -0,0 +1,67 @@ +@charset "UTF-8"; +/* + * jQuery File Upload Demo CSS 1.0 + * https://github.com/blueimp/jQuery-File-Upload + * + * Copyright 2013, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + */ + +body { + max-width: 750px; + margin: 0 auto; + padding: 1em; + font-family: 'Lucida Grande', 'Lucida Sans Unicode', Arial, sans-serif; + font-size: 1em; + line-height: 1.4em; + background: #222; + color: #fff; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +a { + color: orange; + text-decoration: none; +} +img { + border: 0; + vertical-align: middle; +} +h1 { + line-height: 1em; +} +blockquote { + padding: 0 0 0 15px; + margin: 0 0 20px; + border-left: 5px solid #eee; +} +table { + width: 100%; + margin: 10px 0; +} + +.fileupload-progress { + margin: 10px 0; +} +.fileupload-progress .progress-extended { + margin-top: 5px; +} +.error { + color: red; +} + +@media (min-width: 481px) { + .navigation { + list-style: none; + padding: 0; + } + .navigation li { + display: inline-block; + } + .navigation li:not(:first-child):before { + content: '| '; + } +} diff --git a/index.html b/index.html index c99b96db3..4d6f2e576 100644 --- a/index.html +++ b/index.html @@ -70,6 +70,7 @@

    Basic Plus UI version

  • Basic Plus
  • Basic Plus UI
  • AngularJS
  • +
  • jQuery UI
  • File Upload widget with multiple file selection, drag&drop support, progress bars, validation and preview images, audio and video for jQuery.
    @@ -126,7 +127,7 @@

    Demo Notes

  • The maximum file size for uploads in this demo is 5 MB (default file size is unlimited).
  • Only image files (JPG, GIF, PNG) are allowed in this demo (by default there is no file type restriction).
  • Uploaded files will be deleted automatically after 5 minutes (demo setting).
  • -
  • You can drag & drop files from your desktop on this webpage with Google Chrome, Mozilla Firefox and Apple Safari.
  • +
  • You can drag & drop files from your desktop on this webpage (see Browser support).
  • Please refer to the project website and documentation for more information.
  • Built with Twitter's Bootstrap toolkit and Icons from Glyphicons.
  • @@ -241,8 +242,8 @@

    - - + diff --git a/jquery-ui.html b/jquery-ui.html new file mode 100644 index 000000000..2b3846149 --- /dev/null +++ b/jquery-ui.html @@ -0,0 +1,249 @@ + + + + + + + +Codestin Search App + + + + + + + + + + + + + + + + +

    jQuery File Upload Demo

    +

    jQuery UI version

    +
    + + +
    + +
    +

    File Upload widget with multiple file selection, drag&drop support, progress bars, validation and preview images, audio and video for jQuery UI.
    + Supports cross-domain, chunked and resumable file uploads and client-side image resizing.
    + Works with any server-side platform (PHP, Python, Ruby on Rails, Java, Node.js, Go etc.) that supports standard HTML form file uploads.

    +
    + +
    + + + +
    +
    + + + Add files... + + + + + + + + +
    + + +
    + +
    +
    +
    +

    Demo Notes

    +
      +
    • The maximum file size for uploads in this demo is 5 MB (default file size is unlimited).
    • +
    • Only image files (JPG, GIF, PNG) are allowed in this demo (by default there is no file type restriction).
    • +
    • Uploaded files will be deleted automatically after 5 minutes (demo setting).
    • +
    • You can drag & drop files from your desktop on this webpage (see Browser support).
    • +
    • Please refer to the project website and documentation for more information.
    • +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/js/jquery.fileupload-jquery-ui.js b/js/jquery.fileupload-jquery-ui.js new file mode 100755 index 000000000..05dd7a611 --- /dev/null +++ b/js/jquery.fileupload-jquery-ui.js @@ -0,0 +1,138 @@ +/* + * jQuery File Upload jQuery UI Plugin 8.7.0 + * https://github.com/blueimp/jQuery-File-Upload + * + * Copyright 2013, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + */ + +/*jslint nomen: true, unparam: true */ +/*global define, window */ + +(function (factory) { + 'use strict'; + if (typeof define === 'function' && define.amd) { + // Register as an anonymous AMD module: + define(['jquery', './jquery.fileupload-ui'], factory); + } else { + // Browser globals: + factory(window.jQuery); + } +}(function ($) { + 'use strict'; + + $.widget('blueimp.fileupload', $.blueimp.fileupload, { + + options: { + progress: function (e, data) { + if (data.context) { + data.context.find('.progress').progressbar( + 'option', + 'value', + parseInt(data.loaded / data.total * 100, 10) + ); + } + }, + progressall: function (e, data) { + var $this = $(this); + $this.find('.fileupload-progress') + .find('.progress').progressbar( + 'option', + 'value', + parseInt(data.loaded / data.total * 100, 10) + ).end() + .find('.progress-extended').each(function () { + $(this).html( + ($this.data('blueimp-fileupload') || + $this.data('fileupload')) + ._renderExtendedProgress(data) + ); + }); + } + }, + + _renderUpload: function (func, files) { + var node = this._super(func, files), + showIconText = $(window).width() > 480; + node.find('.progress').empty().progressbar(); + node.find('.start').button({ + icons: {primary: 'ui-icon-circle-arrow-e'}, + text: showIconText + }); + node.find('.cancel').button({ + icons: {primary: 'ui-icon-cancel'}, + text: showIconText + }); + return node; + }, + + _renderDownload: function (func, files) { + var node = this._super(func, files), + showIconText = $(window).width() > 480; + node.find('.delete').button({ + icons: {primary: 'ui-icon-trash'}, + text: showIconText + }); + return node; + }, + + _transition: function (node) { + var deferred = $.Deferred(); + if (node.hasClass('fade')) { + node.fadeToggle( + this.options.transitionDuration, + this.options.transitionEasing, + function () { + deferred.resolveWith(node); + } + ); + } else { + deferred.resolveWith(node); + } + return deferred; + }, + + _create: function () { + this._super(); + this.element + .find('.fileupload-buttonbar') + .find('.fileinput-button').each(function () { + var input = $(this).find('input:file').detach(); + $(this) + .button({icons: {primary: 'ui-icon-plusthick'}}) + .append(input); + }) + .end().find('.start') + .button({icons: {primary: 'ui-icon-circle-arrow-e'}}) + .end().find('.cancel') + .button({icons: {primary: 'ui-icon-cancel'}}) + .end().find('.delete') + .button({icons: {primary: 'ui-icon-trash'}}) + .end().find('.progress').progressbar(); + }, + + _destroy: function () { + this.element + .find('.fileupload-buttonbar') + .find('.fileinput-button').each(function () { + var input = $(this).find('input:file').detach(); + $(this) + .button('destroy') + .append(input); + }) + .end().find('.start') + .button('destroy') + .end().find('.cancel') + .button('destroy') + .end().find('.delete') + .button('destroy') + .end().find('.progress').progressbar('destroy'); + this._super(); + } + + }); + +})); diff --git a/package.json b/package.json index 0e0669638..f57f93a5c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "8.6.1", + "version": "8.7.0", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images, audio and video for jQuery. Supports cross-domain, chunked and resumable file uploads. Works with any server-side platform (Google App Engine, PHP, Python, Ruby on Rails, Java, etc.) that supports standard HTML form file uploads.", "keywords": [ @@ -48,8 +48,8 @@ ], "dependencies": { "jquery": ">=1.6", - "blueimp-tmpl": ">=2.2.1", - "blueimp-load-image": ">=1.7.3", - "blueimp-canvas-to-blob": ">=2.0.6" + "blueimp-tmpl": ">=2.3.0", + "blueimp-load-image": ">=1.9.1", + "blueimp-canvas-to-blob": ">=2.0.7" } } From f8eb073f96d5964cc1c1105049a891e9de7c9553 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 1 Aug 2013 19:51:58 -0500 Subject: [PATCH 003/525] Only include the XDomainRequest Transport for IE 8 and IE 9. --- jquery-ui.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jquery-ui.html b/jquery-ui.html index 2b3846149..46976270e 100644 --- a/jquery-ui.html +++ b/jquery-ui.html @@ -241,8 +241,8 @@

    ); }); - - + From 1eaa045b5778ba85d2657cc3a4f06c0ce7a106c0 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Fri, 2 Aug 2013 12:15:08 -0500 Subject: [PATCH 004/525] Reject the send API call if the given fileInput has not selected files. Fixes #2486. --- js/jquery.fileupload.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/js/jquery.fileupload.js b/js/jquery.fileupload.js index 900867a42..8755c082c 100644 --- a/js/jquery.fileupload.js +++ b/js/jquery.fileupload.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload Plugin 5.32.1 + * jQuery File Upload Plugin 5.32.2 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2010, Sebastian Tschan @@ -1303,6 +1303,10 @@ if (aborted) { return; } + if (!files.length) { + dfd.reject(); + return; + } data.files = files; jqXHR = that._onSend(null, data).then( function (result, textStatus, jqXHR) { From 3b78de198304a82b75fefd4a139ed239cc8d25fa Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Sun, 4 Aug 2013 15:55:16 -0500 Subject: [PATCH 005/525] Respect the prependFiles option in the done and fail callbacks. Fixes #2493 --- blueimp-file-upload.jquery.json | 2 +- bower.json | 2 +- js/jquery.fileupload-ui.js | 16 +++++++++------- package.json | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/blueimp-file-upload.jquery.json b/blueimp-file-upload.jquery.json index fd5d203de..60ca2813a 100644 --- a/blueimp-file-upload.jquery.json +++ b/blueimp-file-upload.jquery.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "8.7.0", + "version": "8.7.1", "title": "jQuery File Upload", "author": { "name": "Sebastian Tschan", diff --git a/bower.json b/bower.json index 5d26a9049..e3cc375c9 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "8.7.0", + "version": "8.7.1", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images, audio and video for jQuery. Supports cross-domain, chunked and resumable file uploads. Works with any server-side platform (Google App Engine, PHP, Python, Ruby on Rails, Java, etc.) that supports standard HTML form file uploads.", "keywords": [ diff --git a/js/jquery.fileupload-ui.js b/js/jquery.fileupload-ui.js index 69f0e2292..575c3a650 100644 --- a/js/jquery.fileupload-ui.js +++ b/js/jquery.fileupload-ui.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload User Interface Plugin 8.5.0 + * jQuery File Upload User Interface Plugin 8.7.1 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2010, Sebastian Tschan @@ -139,8 +139,8 @@ if (data.context) { data.context.each(function (index) { var file = files[index] || - {error: 'Empty file upload result'}, - deferred = that._addFinishedDeferreds(); + {error: 'Empty file upload result'}; + deferred = that._addFinishedDeferreds(); that._transition($(this)).done( function () { var node = $(this); @@ -159,8 +159,9 @@ ); }); } else { - template = that._renderDownload(files) - .appendTo(that.options.filesContainer); + template = that._renderDownload(files)[ + that.options.prependFiles ? 'prependTo' : 'appendTo' + ](that.options.filesContainer); that._forceReflow(template); deferred = that._addFinishedDeferreds(); that._transition(template).done( @@ -215,8 +216,9 @@ } }); } else if (data.errorThrown !== 'abort') { - data.context = that._renderUpload(data.files) - .appendTo(that.options.filesContainer) + data.context = that._renderUpload(data.files)[ + that.options.prependFiles ? 'prependTo' : 'appendTo' + ](that.options.filesContainer) .data('data', data); that._forceReflow(data.context); deferred = that._addFinishedDeferreds(); diff --git a/package.json b/package.json index f57f93a5c..c3434f456 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "8.7.0", + "version": "8.7.1", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images, audio and video for jQuery. Supports cross-domain, chunked and resumable file uploads. Works with any server-side platform (Google App Engine, PHP, Python, Ruby on Rails, Java, etc.) that supports standard HTML form file uploads.", "keywords": [ From c22034ac0f57418b7f47df1c02e2132a7ce79a01 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Wed, 7 Aug 2013 19:17:49 -0500 Subject: [PATCH 006/525] Also check for maxNumberOfFiles if current numer is 0. Closes #2501. --- js/jquery.fileupload-validate.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/js/jquery.fileupload-validate.js b/js/jquery.fileupload-validate.js index 1cc4f1ebb..ee1c2f2ed 100644 --- a/js/jquery.fileupload-validate.js +++ b/js/jquery.fileupload-validate.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload Validation Plugin 1.1 + * jQuery File Upload Validation Plugin 1.1.1 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2013, Sebastian Tschan @@ -83,16 +83,17 @@ } var dfd = $.Deferred(), settings = this.options, - file = data.files[data.index], - numberOfFiles = settings.getNumberOfFiles(); - if (numberOfFiles && $.type(options.maxNumberOfFiles) === 'number' && - numberOfFiles + data.files.length > options.maxNumberOfFiles) { + file = data.files[data.index]; + if ($.type(options.maxNumberOfFiles) === 'number' && + (settings.getNumberOfFiles() || 0) + data.files.length > + options.maxNumberOfFiles) { file.error = settings.i18n('maxNumberOfFiles'); } else if (options.acceptFileTypes && !(options.acceptFileTypes.test(file.type) || options.acceptFileTypes.test(file.name))) { file.error = settings.i18n('acceptFileTypes'); - } else if (options.maxFileSize && file.size > options.maxFileSize) { + } else if (options.maxFileSize && file.size > + options.maxFileSize) { file.error = settings.i18n('maxFileSize'); } else if ($.type(file.size) === 'number' && file.size < options.minFileSize) { From 90c7f736a761aa8005154862cf7af336952dea53 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 8 Aug 2013 20:08:19 -0500 Subject: [PATCH 007/525] Updated to support Bootstrap3. --- angularjs.html | 253 +++++++++++++++++++------------------ basic-plus.html | 139 ++++++++++---------- basic.html | 137 ++++++++++---------- css/style.css | 6 +- index.html | 187 ++++++++++++++------------- js/jquery.fileupload-ui.js | 10 +- test/index.html | 14 +- 7 files changed, 372 insertions(+), 374 deletions(-) diff --git a/angularjs.html b/angularjs.html index b0655dc1f..eaad52954 100644 --- a/angularjs.html +++ b/angularjs.html @@ -1,7 +1,7 @@ - + + + + - - - - +
    -
    + @@ -201,16 +201,16 @@

    {% } %} - - + + - + - + - + - + diff --git a/test/index.html b/test/index.html index 4a9a6f328..0b5cf57b7 100644 --- a/test/index.html +++ b/test/index.html @@ -20,7 +20,7 @@ Codestin Search App - +

    jQuery File Upload Plugin Test

    @@ -145,11 +145,11 @@

    {% } %} - + - - - + + + - + From 7d1d64e4d4e2a30cd087fe27355183db980aa785 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Tue, 23 Jan 2018 12:31:33 +0100 Subject: [PATCH 325/525] 9.20.0 --- bower.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index 8dbd2d446..b3f4265ac 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "9.19.3", + "version": "9.20.0", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images.", "keywords": [ diff --git a/package.json b/package.json index c972cbbeb..e16ed6261 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "9.19.3", + "version": "9.20.0", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images, audio and video for jQuery. Supports cross-domain, chunked and resumable file uploads. Works with any server-side platform (Google App Engine, PHP, Python, Ruby on Rails, Java, etc.) that supports standard HTML form file uploads.", "keywords": [ From e2cda462610c776a1f7856692c98b1baab02231a Mon Sep 17 00:00:00 2001 From: Pat O'Callaghan Date: Sat, 10 Feb 2018 21:59:53 +0000 Subject: [PATCH 326/525] Update jquery.ui.widget.js to 1.12.1 --- js/vendor/jquery.ui.widget.js | 1298 +++++++++++++++++++-------------- 1 file changed, 737 insertions(+), 561 deletions(-) diff --git a/js/vendor/jquery.ui.widget.js b/js/vendor/jquery.ui.widget.js index e08df3fd0..a4131b254 100644 --- a/js/vendor/jquery.ui.widget.js +++ b/js/vendor/jquery.ui.widget.js @@ -1,571 +1,747 @@ -/*! jQuery UI - v1.11.4+CommonJS - 2015-08-28 -* http://jqueryui.com -* Includes: widget.js -* Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */ +/*! jQuery UI - v1.12.1 - 2018-02-10 + * http://jqueryui.com + * Includes: widget.js + * Copyright jQuery Foundation and other contributors; Licensed MIT */ (function( factory ) { - if ( typeof define === "function" && define.amd ) { + if ( typeof define === "function" && define.amd ) { - // AMD. Register as an anonymous module. - define([ "jquery" ], factory ); + // AMD. Register as an anonymous module. + define([ "jquery" ], factory ); + } else { - } else if ( typeof exports === "object" ) { + // Browser globals + factory( jQuery ); + } +}(function( $ ) { - // Node/CommonJS - factory( require( "jquery" ) ); + $.ui = $.ui || {}; - } else { + var version = $.ui.version = "1.12.1"; + + + /*! + * jQuery UI Widget 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + + //>>label: Widget + //>>group: Core + //>>description: Provides a factory for creating stateful widgets with a common API. + //>>docs: http://api.jqueryui.com/jQuery.widget/ + //>>demos: http://jqueryui.com/widget/ + + + + var widgetUuid = 0; + var widgetSlice = Array.prototype.slice; + + $.cleanData = ( function( orig ) { + return function( elems ) { + var events, elem, i; + for ( i = 0; ( elem = elems[ i ] ) != null; i++ ) { + try { + + // Only trigger remove when necessary to save time + events = $._data( elem, "events" ); + if ( events && events.remove ) { + $( elem ).triggerHandler( "remove" ); + } + + // Http://bugs.jquery.com/ticket/8235 + } catch ( e ) {} + } + orig( elems ); + }; + } )( $.cleanData ); + + $.widget = function( name, base, prototype ) { + var existingConstructor, constructor, basePrototype; + + // ProxiedPrototype allows the provided prototype to remain unmodified + // so that it can be used as a mixin for multiple widgets (#8876) + var proxiedPrototype = {}; + + var namespace = name.split( "." )[ 0 ]; + name = name.split( "." )[ 1 ]; + var fullName = namespace + "-" + name; + + if ( !prototype ) { + prototype = base; + base = $.Widget; + } + + if ( $.isArray( prototype ) ) { + prototype = $.extend.apply( null, [ {} ].concat( prototype ) ); + } + + // Create selector for plugin + $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { + return !!$.data( elem, fullName ); + }; + + $[ namespace ] = $[ namespace ] || {}; + existingConstructor = $[ namespace ][ name ]; + constructor = $[ namespace ][ name ] = function( options, element ) { + + // Allow instantiation without "new" keyword + if ( !this._createWidget ) { + return new constructor( options, element ); + } + + // Allow instantiation without initializing for simple inheritance + // must use "new" keyword (the code above always passes args) + if ( arguments.length ) { + this._createWidget( options, element ); + } + }; + + // Extend with the existing constructor to carry over any static properties + $.extend( constructor, existingConstructor, { + version: prototype.version, + + // Copy the object used to create the prototype in case we need to + // redefine the widget later + _proto: $.extend( {}, prototype ), + + // Track widgets that inherit from this widget in case this widget is + // redefined after a widget inherits from it + _childConstructors: [] + } ); + + basePrototype = new base(); + + // We need to make the options hash a property directly on the new instance + // otherwise we'll modify the options hash on the prototype that we're + // inheriting from + basePrototype.options = $.widget.extend( {}, basePrototype.options ); + $.each( prototype, function( prop, value ) { + if ( !$.isFunction( value ) ) { + proxiedPrototype[ prop ] = value; + return; + } + proxiedPrototype[ prop ] = ( function() { + function _super() { + return base.prototype[ prop ].apply( this, arguments ); + } + + function _superApply( args ) { + return base.prototype[ prop ].apply( this, args ); + } + + return function() { + var __super = this._super; + var __superApply = this._superApply; + var returnValue; + + this._super = _super; + this._superApply = _superApply; + + returnValue = value.apply( this, arguments ); + + this._super = __super; + this._superApply = __superApply; + + return returnValue; + }; + } )(); + } ); + constructor.prototype = $.widget.extend( basePrototype, { + + // TODO: remove support for widgetEventPrefix + // always use the name + a colon as the prefix, e.g., draggable:start + // don't prefix for widgets that aren't DOM-based + widgetEventPrefix: existingConstructor ? ( basePrototype.widgetEventPrefix || name ) : name + }, proxiedPrototype, { + constructor: constructor, + namespace: namespace, + widgetName: name, + widgetFullName: fullName + } ); + + // If this widget is being redefined then we need to find all widgets that + // are inheriting from it and redefine all of them so that they inherit from + // the new version of this widget. We're essentially trying to replace one + // level in the prototype chain. + if ( existingConstructor ) { + $.each( existingConstructor._childConstructors, function( i, child ) { + var childPrototype = child.prototype; + + // Redefine the child widget using the same prototype that was + // originally used, but inherit from the new version of the base + $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, + child._proto ); + } ); + + // Remove the list of existing child constructors from the old constructor + // so the old child constructors can be garbage collected + delete existingConstructor._childConstructors; + } else { + base._childConstructors.push( constructor ); + } + + $.widget.bridge( name, constructor ); + + return constructor; + }; + + $.widget.extend = function( target ) { + var input = widgetSlice.call( arguments, 1 ); + var inputIndex = 0; + var inputLength = input.length; + var key; + var value; + + for ( ; inputIndex < inputLength; inputIndex++ ) { + for ( key in input[ inputIndex ] ) { + value = input[ inputIndex ][ key ]; + if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { + + // Clone objects + if ( $.isPlainObject( value ) ) { + target[ key ] = $.isPlainObject( target[ key ] ) ? + $.widget.extend( {}, target[ key ], value ) : + + // Don't extend strings, arrays, etc. with objects + $.widget.extend( {}, value ); + + // Copy everything else by reference + } else { + target[ key ] = value; + } + } + } + } + return target; + }; + + $.widget.bridge = function( name, object ) { + var fullName = object.prototype.widgetFullName || name; + $.fn[ name ] = function( options ) { + var isMethodCall = typeof options === "string"; + var args = widgetSlice.call( arguments, 1 ); + var returnValue = this; + + if ( isMethodCall ) { + + // If this is an empty collection, we need to have the instance method + // return undefined instead of the jQuery instance + if ( !this.length && options === "instance" ) { + returnValue = undefined; + } else { + this.each( function() { + var methodValue; + var instance = $.data( this, fullName ); + + if ( options === "instance" ) { + returnValue = instance; + return false; + } + + if ( !instance ) { + return $.error( "cannot call methods on " + name + + " prior to initialization; " + + "attempted to call method '" + options + "'" ); + } + + if ( !$.isFunction( instance[ options ] ) || options.charAt( 0 ) === "_" ) { + return $.error( "no such method '" + options + "' for " + name + + " widget instance" ); + } + + methodValue = instance[ options ].apply( instance, args ); + + if ( methodValue !== instance && methodValue !== undefined ) { + returnValue = methodValue && methodValue.jquery ? + returnValue.pushStack( methodValue.get() ) : + methodValue; + return false; + } + } ); + } + } else { + + // Allow multiple hashes to be passed on init + if ( args.length ) { + options = $.widget.extend.apply( null, [ options ].concat( args ) ); + } + + this.each( function() { + var instance = $.data( this, fullName ); + if ( instance ) { + instance.option( options || {} ); + if ( instance._init ) { + instance._init(); + } + } else { + $.data( this, fullName, new object( options, this ) ); + } + } ); + } + + return returnValue; + }; + }; + + $.Widget = function( /* options, element */ ) {}; + $.Widget._childConstructors = []; + + $.Widget.prototype = { + widgetName: "widget", + widgetEventPrefix: "", + defaultElement: "
    ", + + options: { + classes: {}, + disabled: false, + + // Callbacks + create: null + }, + + _createWidget: function( options, element ) { + element = $( element || this.defaultElement || this )[ 0 ]; + this.element = $( element ); + this.uuid = widgetUuid++; + this.eventNamespace = "." + this.widgetName + this.uuid; + + this.bindings = $(); + this.hoverable = $(); + this.focusable = $(); + this.classesElementLookup = {}; + + if ( element !== this ) { + $.data( element, this.widgetFullName, this ); + this._on( true, this.element, { + remove: function( event ) { + if ( event.target === element ) { + this.destroy(); + } + } + } ); + this.document = $( element.style ? + + // Element within the document + element.ownerDocument : + + // Element is window or document + element.document || element ); + this.window = $( this.document[ 0 ].defaultView || this.document[ 0 ].parentWindow ); + } + + this.options = $.widget.extend( {}, + this.options, + this._getCreateOptions(), + options ); + + this._create(); + + if ( this.options.disabled ) { + this._setOptionDisabled( this.options.disabled ); + } + + this._trigger( "create", null, this._getCreateEventData() ); + this._init(); + }, + + _getCreateOptions: function() { + return {}; + }, + + _getCreateEventData: $.noop, + + _create: $.noop, + + _init: $.noop, + + destroy: function() { + var that = this; + + this._destroy(); + $.each( this.classesElementLookup, function( key, value ) { + that._removeClass( value, key ); + } ); + + // We can probably remove the unbind calls in 2.0 + // all event bindings should go through this._on() + this.element + .off( this.eventNamespace ) + .removeData( this.widgetFullName ); + this.widget() + .off( this.eventNamespace ) + .removeAttr( "aria-disabled" ); + + // Clean up events and states + this.bindings.off( this.eventNamespace ); + }, + + _destroy: $.noop, + + widget: function() { + return this.element; + }, + + option: function( key, value ) { + var options = key; + var parts; + var curOption; + var i; + + if ( arguments.length === 0 ) { + + // Don't return a reference to the internal hash + return $.widget.extend( {}, this.options ); + } + + if ( typeof key === "string" ) { + + // Handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } + options = {}; + parts = key.split( "." ); + key = parts.shift(); + if ( parts.length ) { + curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); + for ( i = 0; i < parts.length - 1; i++ ) { + curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; + curOption = curOption[ parts[ i ] ]; + } + key = parts.pop(); + if ( arguments.length === 1 ) { + return curOption[ key ] === undefined ? null : curOption[ key ]; + } + curOption[ key ] = value; + } else { + if ( arguments.length === 1 ) { + return this.options[ key ] === undefined ? null : this.options[ key ]; + } + options[ key ] = value; + } + } + + this._setOptions( options ); + + return this; + }, + + _setOptions: function( options ) { + var key; + + for ( key in options ) { + this._setOption( key, options[ key ] ); + } + + return this; + }, + + _setOption: function( key, value ) { + if ( key === "classes" ) { + this._setOptionClasses( value ); + } + + this.options[ key ] = value; + + if ( key === "disabled" ) { + this._setOptionDisabled( value ); + } + + return this; + }, + + _setOptionClasses: function( value ) { + var classKey, elements, currentElements; + + for ( classKey in value ) { + currentElements = this.classesElementLookup[ classKey ]; + if ( value[ classKey ] === this.options.classes[ classKey ] || + !currentElements || + !currentElements.length ) { + continue; + } + + // We are doing this to create a new jQuery object because the _removeClass() call + // on the next line is going to destroy the reference to the current elements being + // tracked. We need to save a copy of this collection so that we can add the new classes + // below. + elements = $( currentElements.get() ); + this._removeClass( currentElements, classKey ); + + // We don't use _addClass() here, because that uses this.options.classes + // for generating the string of classes. We want to use the value passed in from + // _setOption(), this is the new value of the classes option which was passed to + // _setOption(). We pass this value directly to _classes(). + elements.addClass( this._classes( { + element: elements, + keys: classKey, + classes: value, + add: true + } ) ); + } + }, + + _setOptionDisabled: function( value ) { + this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null, !!value ); + + // If the widget is becoming disabled, then nothing is interactive + if ( value ) { + this._removeClass( this.hoverable, null, "ui-state-hover" ); + this._removeClass( this.focusable, null, "ui-state-focus" ); + } + }, + + enable: function() { + return this._setOptions( { disabled: false } ); + }, + + disable: function() { + return this._setOptions( { disabled: true } ); + }, + + _classes: function( options ) { + var full = []; + var that = this; + + options = $.extend( { + element: this.element, + classes: this.options.classes || {} + }, options ); + + function processClassString( classes, checkOption ) { + var current, i; + for ( i = 0; i < classes.length; i++ ) { + current = that.classesElementLookup[ classes[ i ] ] || $(); + if ( options.add ) { + current = $( $.unique( current.get().concat( options.element.get() ) ) ); + } else { + current = $( current.not( options.element ).get() ); + } + that.classesElementLookup[ classes[ i ] ] = current; + full.push( classes[ i ] ); + if ( checkOption && options.classes[ classes[ i ] ] ) { + full.push( options.classes[ classes[ i ] ] ); + } + } + } + + this._on( options.element, { + "remove": "_untrackClassesElement" + } ); + + if ( options.keys ) { + processClassString( options.keys.match( /\S+/g ) || [], true ); + } + if ( options.extra ) { + processClassString( options.extra.match( /\S+/g ) || [] ); + } + + return full.join( " " ); + }, + + _untrackClassesElement: function( event ) { + var that = this; + $.each( that.classesElementLookup, function( key, value ) { + if ( $.inArray( event.target, value ) !== -1 ) { + that.classesElementLookup[ key ] = $( value.not( event.target ).get() ); + } + } ); + }, + + _removeClass: function( element, keys, extra ) { + return this._toggleClass( element, keys, extra, false ); + }, + + _addClass: function( element, keys, extra ) { + return this._toggleClass( element, keys, extra, true ); + }, + + _toggleClass: function( element, keys, extra, add ) { + add = ( typeof add === "boolean" ) ? add : extra; + var shift = ( typeof element === "string" || element === null ), + options = { + extra: shift ? keys : extra, + keys: shift ? element : keys, + element: shift ? this.element : element, + add: add + }; + options.element.toggleClass( this._classes( options ), add ); + return this; + }, + + _on: function( suppressDisabledCheck, element, handlers ) { + var delegateElement; + var instance = this; + + // No suppressDisabledCheck flag, shuffle arguments + if ( typeof suppressDisabledCheck !== "boolean" ) { + handlers = element; + element = suppressDisabledCheck; + suppressDisabledCheck = false; + } + + // No element argument, shuffle and use this.element + if ( !handlers ) { + handlers = element; + element = this.element; + delegateElement = this.widget(); + } else { + element = delegateElement = $( element ); + this.bindings = this.bindings.add( element ); + } + + $.each( handlers, function( event, handler ) { + function handlerProxy() { + + // Allow widgets to customize the disabled handling + // - disabled as an array instead of boolean + // - disabled class as method for disabling individual parts + if ( !suppressDisabledCheck && + ( instance.options.disabled === true || + $( this ).hasClass( "ui-state-disabled" ) ) ) { + return; + } + return ( typeof handler === "string" ? instance[ handler ] : handler ) + .apply( instance, arguments ); + } + + // Copy the guid so direct unbinding works + if ( typeof handler !== "string" ) { + handlerProxy.guid = handler.guid = + handler.guid || handlerProxy.guid || $.guid++; + } + + var match = event.match( /^([\w:-]*)\s*(.*)$/ ); + var eventName = match[ 1 ] + instance.eventNamespace; + var selector = match[ 2 ]; + + if ( selector ) { + delegateElement.on( eventName, selector, handlerProxy ); + } else { + element.on( eventName, handlerProxy ); + } + } ); + }, + + _off: function( element, eventName ) { + eventName = ( eventName || "" ).split( " " ).join( this.eventNamespace + " " ) + + this.eventNamespace; + element.off( eventName ).off( eventName ); + + // Clear the stack to avoid memory leaks (#10056) + this.bindings = $( this.bindings.not( element ).get() ); + this.focusable = $( this.focusable.not( element ).get() ); + this.hoverable = $( this.hoverable.not( element ).get() ); + }, + + _delay: function( handler, delay ) { + function handlerProxy() { + return ( typeof handler === "string" ? instance[ handler ] : handler ) + .apply( instance, arguments ); + } + var instance = this; + return setTimeout( handlerProxy, delay || 0 ); + }, + + _hoverable: function( element ) { + this.hoverable = this.hoverable.add( element ); + this._on( element, { + mouseenter: function( event ) { + this._addClass( $( event.currentTarget ), null, "ui-state-hover" ); + }, + mouseleave: function( event ) { + this._removeClass( $( event.currentTarget ), null, "ui-state-hover" ); + } + } ); + }, + + _focusable: function( element ) { + this.focusable = this.focusable.add( element ); + this._on( element, { + focusin: function( event ) { + this._addClass( $( event.currentTarget ), null, "ui-state-focus" ); + }, + focusout: function( event ) { + this._removeClass( $( event.currentTarget ), null, "ui-state-focus" ); + } + } ); + }, + + _trigger: function( type, event, data ) { + var prop, orig; + var callback = this.options[ type ]; + + data = data || {}; + event = $.Event( event ); + event.type = ( type === this.widgetEventPrefix ? + type : + this.widgetEventPrefix + type ).toLowerCase(); + + // The original event may come from any element + // so we need to reset the target on the new event + event.target = this.element[ 0 ]; + + // Copy original event properties over to the new event + orig = event.originalEvent; + if ( orig ) { + for ( prop in orig ) { + if ( !( prop in event ) ) { + event[ prop ] = orig[ prop ]; + } + } + } + + this.element.trigger( event, data ); + return !( $.isFunction( callback ) && + callback.apply( this.element[ 0 ], [ event ].concat( data ) ) === false || + event.isDefaultPrevented() ); + } + }; + + $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { + $.Widget.prototype[ "_" + method ] = function( element, options, callback ) { + if ( typeof options === "string" ) { + options = { effect: options }; + } + + var hasOptions; + var effectName = !options ? + method : + options === true || typeof options === "number" ? + defaultEffect : + options.effect || defaultEffect; + + options = options || {}; + if ( typeof options === "number" ) { + options = { duration: options }; + } + + hasOptions = !$.isEmptyObject( options ); + options.complete = callback; + + if ( options.delay ) { + element.delay( options.delay ); + } + + if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) { + element[ method ]( options ); + } else if ( effectName !== method && element[ effectName ] ) { + element[ effectName ]( options.duration, options.easing, callback ); + } else { + element.queue( function( next ) { + $( this )[ method ](); + if ( callback ) { + callback.call( element[ 0 ] ); + } + next(); + } ); + } + }; + } ); + + var widget = $.widget; - // Browser globals - factory( jQuery ); - } -}(function( $ ) { -/*! - * jQuery UI Widget 1.11.4 - * http://jqueryui.com - * - * Copyright jQuery Foundation and other contributors - * Released under the MIT license. - * http://jquery.org/license - * - * http://api.jqueryui.com/jQuery.widget/ - */ - - -var widget_uuid = 0, - widget_slice = Array.prototype.slice; - -$.cleanData = (function( orig ) { - return function( elems ) { - var events, elem, i; - for ( i = 0; (elem = elems[i]) != null; i++ ) { - try { - - // Only trigger remove when necessary to save time - events = $._data( elem, "events" ); - if ( events && events.remove ) { - $( elem ).triggerHandler( "remove" ); - } - - // http://bugs.jquery.com/ticket/8235 - } catch ( e ) {} - } - orig( elems ); - }; -})( $.cleanData ); - -$.widget = function( name, base, prototype ) { - var fullName, existingConstructor, constructor, basePrototype, - // proxiedPrototype allows the provided prototype to remain unmodified - // so that it can be used as a mixin for multiple widgets (#8876) - proxiedPrototype = {}, - namespace = name.split( "." )[ 0 ]; - - name = name.split( "." )[ 1 ]; - fullName = namespace + "-" + name; - - if ( !prototype ) { - prototype = base; - base = $.Widget; - } - - // create selector for plugin - $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { - return !!$.data( elem, fullName ); - }; - - $[ namespace ] = $[ namespace ] || {}; - existingConstructor = $[ namespace ][ name ]; - constructor = $[ namespace ][ name ] = function( options, element ) { - // allow instantiation without "new" keyword - if ( !this._createWidget ) { - return new constructor( options, element ); - } - - // allow instantiation without initializing for simple inheritance - // must use "new" keyword (the code above always passes args) - if ( arguments.length ) { - this._createWidget( options, element ); - } - }; - // extend with the existing constructor to carry over any static properties - $.extend( constructor, existingConstructor, { - version: prototype.version, - // copy the object used to create the prototype in case we need to - // redefine the widget later - _proto: $.extend( {}, prototype ), - // track widgets that inherit from this widget in case this widget is - // redefined after a widget inherits from it - _childConstructors: [] - }); - - basePrototype = new base(); - // we need to make the options hash a property directly on the new instance - // otherwise we'll modify the options hash on the prototype that we're - // inheriting from - basePrototype.options = $.widget.extend( {}, basePrototype.options ); - $.each( prototype, function( prop, value ) { - if ( !$.isFunction( value ) ) { - proxiedPrototype[ prop ] = value; - return; - } - proxiedPrototype[ prop ] = (function() { - var _super = function() { - return base.prototype[ prop ].apply( this, arguments ); - }, - _superApply = function( args ) { - return base.prototype[ prop ].apply( this, args ); - }; - return function() { - var __super = this._super, - __superApply = this._superApply, - returnValue; - - this._super = _super; - this._superApply = _superApply; - - returnValue = value.apply( this, arguments ); - - this._super = __super; - this._superApply = __superApply; - - return returnValue; - }; - })(); - }); - constructor.prototype = $.widget.extend( basePrototype, { - // TODO: remove support for widgetEventPrefix - // always use the name + a colon as the prefix, e.g., draggable:start - // don't prefix for widgets that aren't DOM-based - widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : name - }, proxiedPrototype, { - constructor: constructor, - namespace: namespace, - widgetName: name, - widgetFullName: fullName - }); - - // If this widget is being redefined then we need to find all widgets that - // are inheriting from it and redefine all of them so that they inherit from - // the new version of this widget. We're essentially trying to replace one - // level in the prototype chain. - if ( existingConstructor ) { - $.each( existingConstructor._childConstructors, function( i, child ) { - var childPrototype = child.prototype; - - // redefine the child widget using the same prototype that was - // originally used, but inherit from the new version of the base - $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto ); - }); - // remove the list of existing child constructors from the old constructor - // so the old child constructors can be garbage collected - delete existingConstructor._childConstructors; - } else { - base._childConstructors.push( constructor ); - } - - $.widget.bridge( name, constructor ); - - return constructor; -}; - -$.widget.extend = function( target ) { - var input = widget_slice.call( arguments, 1 ), - inputIndex = 0, - inputLength = input.length, - key, - value; - for ( ; inputIndex < inputLength; inputIndex++ ) { - for ( key in input[ inputIndex ] ) { - value = input[ inputIndex ][ key ]; - if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { - // Clone objects - if ( $.isPlainObject( value ) ) { - target[ key ] = $.isPlainObject( target[ key ] ) ? - $.widget.extend( {}, target[ key ], value ) : - // Don't extend strings, arrays, etc. with objects - $.widget.extend( {}, value ); - // Copy everything else by reference - } else { - target[ key ] = value; - } - } - } - } - return target; -}; - -$.widget.bridge = function( name, object ) { - var fullName = object.prototype.widgetFullName || name; - $.fn[ name ] = function( options ) { - var isMethodCall = typeof options === "string", - args = widget_slice.call( arguments, 1 ), - returnValue = this; - - if ( isMethodCall ) { - this.each(function() { - var methodValue, - instance = $.data( this, fullName ); - if ( options === "instance" ) { - returnValue = instance; - return false; - } - if ( !instance ) { - return $.error( "cannot call methods on " + name + " prior to initialization; " + - "attempted to call method '" + options + "'" ); - } - if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) { - return $.error( "no such method '" + options + "' for " + name + " widget instance" ); - } - methodValue = instance[ options ].apply( instance, args ); - if ( methodValue !== instance && methodValue !== undefined ) { - returnValue = methodValue && methodValue.jquery ? - returnValue.pushStack( methodValue.get() ) : - methodValue; - return false; - } - }); - } else { - - // Allow multiple hashes to be passed on init - if ( args.length ) { - options = $.widget.extend.apply( null, [ options ].concat(args) ); - } - - this.each(function() { - var instance = $.data( this, fullName ); - if ( instance ) { - instance.option( options || {} ); - if ( instance._init ) { - instance._init(); - } - } else { - $.data( this, fullName, new object( options, this ) ); - } - }); - } - - return returnValue; - }; -}; - -$.Widget = function( /* options, element */ ) {}; -$.Widget._childConstructors = []; - -$.Widget.prototype = { - widgetName: "widget", - widgetEventPrefix: "", - defaultElement: "
    ", - options: { - disabled: false, - - // callbacks - create: null - }, - _createWidget: function( options, element ) { - element = $( element || this.defaultElement || this )[ 0 ]; - this.element = $( element ); - this.uuid = widget_uuid++; - this.eventNamespace = "." + this.widgetName + this.uuid; - - this.bindings = $(); - this.hoverable = $(); - this.focusable = $(); - - if ( element !== this ) { - $.data( element, this.widgetFullName, this ); - this._on( true, this.element, { - remove: function( event ) { - if ( event.target === element ) { - this.destroy(); - } - } - }); - this.document = $( element.style ? - // element within the document - element.ownerDocument : - // element is window or document - element.document || element ); - this.window = $( this.document[0].defaultView || this.document[0].parentWindow ); - } - - this.options = $.widget.extend( {}, - this.options, - this._getCreateOptions(), - options ); - - this._create(); - this._trigger( "create", null, this._getCreateEventData() ); - this._init(); - }, - _getCreateOptions: $.noop, - _getCreateEventData: $.noop, - _create: $.noop, - _init: $.noop, - - destroy: function() { - this._destroy(); - // we can probably remove the unbind calls in 2.0 - // all event bindings should go through this._on() - this.element - .unbind( this.eventNamespace ) - .removeData( this.widgetFullName ) - // support: jquery <1.6.3 - // http://bugs.jquery.com/ticket/9413 - .removeData( $.camelCase( this.widgetFullName ) ); - this.widget() - .unbind( this.eventNamespace ) - .removeAttr( "aria-disabled" ) - .removeClass( - this.widgetFullName + "-disabled " + - "ui-state-disabled" ); - - // clean up events and states - this.bindings.unbind( this.eventNamespace ); - this.hoverable.removeClass( "ui-state-hover" ); - this.focusable.removeClass( "ui-state-focus" ); - }, - _destroy: $.noop, - - widget: function() { - return this.element; - }, - - option: function( key, value ) { - var options = key, - parts, - curOption, - i; - - if ( arguments.length === 0 ) { - // don't return a reference to the internal hash - return $.widget.extend( {}, this.options ); - } - - if ( typeof key === "string" ) { - // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } - options = {}; - parts = key.split( "." ); - key = parts.shift(); - if ( parts.length ) { - curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); - for ( i = 0; i < parts.length - 1; i++ ) { - curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; - curOption = curOption[ parts[ i ] ]; - } - key = parts.pop(); - if ( arguments.length === 1 ) { - return curOption[ key ] === undefined ? null : curOption[ key ]; - } - curOption[ key ] = value; - } else { - if ( arguments.length === 1 ) { - return this.options[ key ] === undefined ? null : this.options[ key ]; - } - options[ key ] = value; - } - } - - this._setOptions( options ); - - return this; - }, - _setOptions: function( options ) { - var key; - - for ( key in options ) { - this._setOption( key, options[ key ] ); - } - - return this; - }, - _setOption: function( key, value ) { - this.options[ key ] = value; - - if ( key === "disabled" ) { - this.widget() - .toggleClass( this.widgetFullName + "-disabled", !!value ); - - // If the widget is becoming disabled, then nothing is interactive - if ( value ) { - this.hoverable.removeClass( "ui-state-hover" ); - this.focusable.removeClass( "ui-state-focus" ); - } - } - - return this; - }, - - enable: function() { - return this._setOptions({ disabled: false }); - }, - disable: function() { - return this._setOptions({ disabled: true }); - }, - - _on: function( suppressDisabledCheck, element, handlers ) { - var delegateElement, - instance = this; - - // no suppressDisabledCheck flag, shuffle arguments - if ( typeof suppressDisabledCheck !== "boolean" ) { - handlers = element; - element = suppressDisabledCheck; - suppressDisabledCheck = false; - } - - // no element argument, shuffle and use this.element - if ( !handlers ) { - handlers = element; - element = this.element; - delegateElement = this.widget(); - } else { - element = delegateElement = $( element ); - this.bindings = this.bindings.add( element ); - } - - $.each( handlers, function( event, handler ) { - function handlerProxy() { - // allow widgets to customize the disabled handling - // - disabled as an array instead of boolean - // - disabled class as method for disabling individual parts - if ( !suppressDisabledCheck && - ( instance.options.disabled === true || - $( this ).hasClass( "ui-state-disabled" ) ) ) { - return; - } - return ( typeof handler === "string" ? instance[ handler ] : handler ) - .apply( instance, arguments ); - } - - // copy the guid so direct unbinding works - if ( typeof handler !== "string" ) { - handlerProxy.guid = handler.guid = - handler.guid || handlerProxy.guid || $.guid++; - } - - var match = event.match( /^([\w:-]*)\s*(.*)$/ ), - eventName = match[1] + instance.eventNamespace, - selector = match[2]; - if ( selector ) { - delegateElement.delegate( selector, eventName, handlerProxy ); - } else { - element.bind( eventName, handlerProxy ); - } - }); - }, - - _off: function( element, eventName ) { - eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + - this.eventNamespace; - element.unbind( eventName ).undelegate( eventName ); - - // Clear the stack to avoid memory leaks (#10056) - this.bindings = $( this.bindings.not( element ).get() ); - this.focusable = $( this.focusable.not( element ).get() ); - this.hoverable = $( this.hoverable.not( element ).get() ); - }, - - _delay: function( handler, delay ) { - function handlerProxy() { - return ( typeof handler === "string" ? instance[ handler ] : handler ) - .apply( instance, arguments ); - } - var instance = this; - return setTimeout( handlerProxy, delay || 0 ); - }, - - _hoverable: function( element ) { - this.hoverable = this.hoverable.add( element ); - this._on( element, { - mouseenter: function( event ) { - $( event.currentTarget ).addClass( "ui-state-hover" ); - }, - mouseleave: function( event ) { - $( event.currentTarget ).removeClass( "ui-state-hover" ); - } - }); - }, - - _focusable: function( element ) { - this.focusable = this.focusable.add( element ); - this._on( element, { - focusin: function( event ) { - $( event.currentTarget ).addClass( "ui-state-focus" ); - }, - focusout: function( event ) { - $( event.currentTarget ).removeClass( "ui-state-focus" ); - } - }); - }, - - _trigger: function( type, event, data ) { - var prop, orig, - callback = this.options[ type ]; - - data = data || {}; - event = $.Event( event ); - event.type = ( type === this.widgetEventPrefix ? - type : - this.widgetEventPrefix + type ).toLowerCase(); - // the original event may come from any element - // so we need to reset the target on the new event - event.target = this.element[ 0 ]; - - // copy original event properties over to the new event - orig = event.originalEvent; - if ( orig ) { - for ( prop in orig ) { - if ( !( prop in event ) ) { - event[ prop ] = orig[ prop ]; - } - } - } - - this.element.trigger( event, data ); - return !( $.isFunction( callback ) && - callback.apply( this.element[0], [ event ].concat( data ) ) === false || - event.isDefaultPrevented() ); - } -}; - -$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { - $.Widget.prototype[ "_" + method ] = function( element, options, callback ) { - if ( typeof options === "string" ) { - options = { effect: options }; - } - var hasOptions, - effectName = !options ? - method : - options === true || typeof options === "number" ? - defaultEffect : - options.effect || defaultEffect; - options = options || {}; - if ( typeof options === "number" ) { - options = { duration: options }; - } - hasOptions = !$.isEmptyObject( options ); - options.complete = callback; - if ( options.delay ) { - element.delay( options.delay ); - } - if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) { - element[ method ]( options ); - } else if ( effectName !== method && element[ effectName ] ) { - element[ effectName ]( options.duration, options.easing, callback ); - } else { - element.queue(function( next ) { - $( this )[ method ](); - if ( callback ) { - callback.call( element[ 0 ] ); - } - next(); - }); - } - }; -}); - -var widget = $.widget; From e6f059023afdae3efe9ce66970745254caa80256 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Mon, 19 Feb 2018 13:12:29 +0100 Subject: [PATCH 327/525] 9.21.0 --- bower.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index b3f4265ac..4b95a320b 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "9.20.0", + "version": "9.21.0", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images.", "keywords": [ diff --git a/package.json b/package.json index e16ed6261..11f1fb430 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "9.20.0", + "version": "9.21.0", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images, audio and video for jQuery. Supports cross-domain, chunked and resumable file uploads. Works with any server-side platform (Google App Engine, PHP, Python, Ruby on Rails, Java, etc.) that supports standard HTML form file uploads.", "keywords": [ From 1d733f8af3528c081d309c8d649ab0b1bdbad242 Mon Sep 17 00:00:00 2001 From: Andrii Lundiak Date: Sat, 17 Mar 2018 13:29:34 +0100 Subject: [PATCH 328/525] Small typo fix --- js/jquery.fileupload.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/jquery.fileupload.js b/js/jquery.fileupload.js index 7929daaaf..629f57a25 100644 --- a/js/jquery.fileupload.js +++ b/js/jquery.fileupload.js @@ -1130,7 +1130,7 @@ dirReader = entry.createReader(); readEntries(); } else { - // Return an empy list for file system items + // Return an empty list for file system items // other than files or directories: dfd.resolve([]); } From b69c7db7604650df40d08f63705cdc7db3aa4080 Mon Sep 17 00:00:00 2001 From: Bertrand Bonnefoy-Claudet Date: Mon, 18 Jun 2018 18:59:27 +0200 Subject: [PATCH 329/525] Bring back support for CommonJS Browserify, which follows CommonJS module conventions, parses the AST for `require` calls to determine the dependencies of a project. jQuery File Upload used to support this; it was removed in e2cda462610c776a1f7856692c98b1baab02231a when a new version of jquery.ui.widget.js was vendored. --- js/vendor/jquery.ui.widget.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/js/vendor/jquery.ui.widget.js b/js/vendor/jquery.ui.widget.js index a4131b254..914b8ffb8 100644 --- a/js/vendor/jquery.ui.widget.js +++ b/js/vendor/jquery.ui.widget.js @@ -1,4 +1,4 @@ -/*! jQuery UI - v1.12.1 - 2018-02-10 +/*! jQuery UI - v1.12.1+CommonJS - 2018-02-10 * http://jqueryui.com * Includes: widget.js * Copyright jQuery Foundation and other contributors; Licensed MIT */ @@ -8,6 +8,10 @@ // AMD. Register as an anonymous module. define([ "jquery" ], factory ); + } else if ( typeof exports === "object" ) { + + // Node/CommonJS + factory( require( "jquery" ) ); } else { // Browser globals From 39607fdaaba0dc11ba8c116ac2968e28e796f153 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Tue, 19 Jun 2018 09:33:35 +0200 Subject: [PATCH 330/525] 9.22.0 --- bower.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index 4b95a320b..c1152560a 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "9.21.0", + "version": "9.22.0", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images.", "keywords": [ diff --git a/package.json b/package.json index 11f1fb430..465458069 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "9.21.0", + "version": "9.22.0", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images, audio and video for jQuery. Supports cross-domain, chunked and resumable file uploads. Works with any server-side platform (Google App Engine, PHP, Python, Ruby on Rails, Java, etc.) that supports standard HTML form file uploads.", "keywords": [ From 55d64d8af80c227323f858dac396c7f6957d1790 Mon Sep 17 00:00:00 2001 From: philayres Date: Thu, 19 Jul 2018 13:04:19 +0100 Subject: [PATCH 331/525] Add beforechunksend handler --- js/jquery.fileupload-angular.js | 1 + js/jquery.fileupload.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/js/jquery.fileupload-angular.js b/js/jquery.fileupload-angular.js index 1c2055276..1d059ab01 100644 --- a/js/jquery.fileupload-angular.js +++ b/js/jquery.fileupload-angular.js @@ -315,6 +315,7 @@ 'fileuploadpaste', 'fileuploaddrop', 'fileuploaddragover', + 'fileuploadbeforechunksend', 'fileuploadchunksend', 'fileuploadchunkdone', 'fileuploadchunkfail', diff --git a/js/jquery.fileupload.js b/js/jquery.fileupload.js index 629f57a25..aa2effe5c 100644 --- a/js/jquery.fileupload.js +++ b/js/jquery.fileupload.js @@ -766,6 +766,8 @@ // Expose the chunk bytes position range: o.contentRange = 'bytes ' + ub + '-' + (ub + o.chunkSize - 1) + '/' + fs; + // Trigger beforechunksend to allow form data to be updated for this chunk + that._trigger('beforechunksend', null, o); // Process the upload data (the blob and potential form data): that._initXHRData(o); // Add progress listeners for this chunk upload: From 8335871e2a21e0eda57791343dea09edda8f6ede Mon Sep 17 00:00:00 2001 From: philayres Date: Thu, 19 Jul 2018 13:21:45 +0100 Subject: [PATCH 332/525] Provide chunkbeforesend event handler --- js/jquery.fileupload-angular.js | 2 +- js/jquery.fileupload.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/js/jquery.fileupload-angular.js b/js/jquery.fileupload-angular.js index 1d059ab01..185907d36 100644 --- a/js/jquery.fileupload-angular.js +++ b/js/jquery.fileupload-angular.js @@ -315,7 +315,7 @@ 'fileuploadpaste', 'fileuploaddrop', 'fileuploaddragover', - 'fileuploadbeforechunksend', + 'fileuploadchunkbeforesend', 'fileuploadchunksend', 'fileuploadchunkdone', 'fileuploadchunkfail', diff --git a/js/jquery.fileupload.js b/js/jquery.fileupload.js index aa2effe5c..096c29b92 100644 --- a/js/jquery.fileupload.js +++ b/js/jquery.fileupload.js @@ -766,8 +766,8 @@ // Expose the chunk bytes position range: o.contentRange = 'bytes ' + ub + '-' + (ub + o.chunkSize - 1) + '/' + fs; - // Trigger beforechunksend to allow form data to be updated for this chunk - that._trigger('beforechunksend', null, o); + // Trigger chunkbeforesend to allow form data to be updated for this chunk + that._trigger('chunkbeforesend', null, o); // Process the upload data (the blob and potential form data): that._initXHRData(o); // Add progress listeners for this chunk upload: From aeb47e51c67df8a504b7726595576c1c66b5dc2f Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Sat, 13 Oct 2018 16:27:32 +0200 Subject: [PATCH 333/525] SECURITY FIX: Only allow image file types by default. This prevents remote code execution in Apache servers version 2.3.9+ with the default configuration (AllowOverride None). Since Apache version 2.3.9, .htaccess support is disabled by default: https://httpd.apache.org/docs/current/mod/core.html#allowoverride Without the configuration in the .htaccess file, allowing uploads of all file types allows remote code execution. Thanks to @lcashdol for reporting the vulnerability (Closes #3514). --- server/php/index.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/server/php/index.php b/server/php/index.php index 6caabb710..c1dbcaf66 100644 --- a/server/php/index.php +++ b/server/php/index.php @@ -12,4 +12,17 @@ error_reporting(E_ALL | E_STRICT); require('UploadHandler.php'); -$upload_handler = new UploadHandler(); +$upload_handler = new UploadHandler(array( + + // SECURITY NOTICE: + // Only change the accept_file_types setting after making sure that any + // allowed file types cannot be executed by the webserver in the files + // directory (e.g. PHP scripts), nor executed by the browser when downloaded + // (e.g. HTML files with embedded JavaScript code). + // e.g. in Apache, make sure the provided .htaccess file is present in the + // files directory and .htaccess support has been enabled: + // https://httpd.apache.org/docs/current/howto/htaccess.html + + // By default, only allow file uploads with image file extensions: + 'accept_file_types' => '/\.(gif|jpe?g|png)$/i' +)); From e3687064b91955fa4558cdb9bd307f7361e7e236 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Sat, 13 Oct 2018 16:28:06 +0200 Subject: [PATCH 334/525] 9.22.1 --- bower.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index c1152560a..f31b6b61e 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "9.22.0", + "version": "9.22.1", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images.", "keywords": [ diff --git a/package.json b/package.json index 465458069..ebaf51037 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "9.22.0", + "version": "9.22.1", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images, audio and video for jQuery. Supports cross-domain, chunked and resumable file uploads. Works with any server-side platform (Google App Engine, PHP, Python, Ruby on Rails, Java, etc.) that supports standard HTML form file uploads.", "keywords": [ From 3dd18f9318d6fd9b7d26beac10fa79c2db883e1f Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Sun, 14 Oct 2018 18:49:27 +0200 Subject: [PATCH 335/525] Explicit deinitialization of progress listeners. This addresses a memory leak with Microsoft Edge. Thanks @butonic for the report, investigation and fix. Closes #3508 --- js/jquery.fileupload.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/js/jquery.fileupload.js b/js/jquery.fileupload.js index 629f57a25..8f00b0dc0 100644 --- a/js/jquery.fileupload.js +++ b/js/jquery.fileupload.js @@ -434,6 +434,13 @@ } }, + _deinitProgressListener: function (options) { + var xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr(); + if (xhr.upload) { + $(xhr.upload).unbind('progress'); + } + }, + _isInstanceOf: function (type, obj) { // Cross-frame instanceof check return Object.prototype.toString.call(obj) === '[object ' + type + ']'; @@ -812,6 +819,9 @@ o.context, [jqXHR, textStatus, errorThrown] ); + }) + .always(function () { + that._deinitProgressListener(o); }); }; this._enhancePromise(promise); @@ -913,6 +923,7 @@ }).fail(function (jqXHR, textStatus, errorThrown) { that._onFail(jqXHR, textStatus, errorThrown, options); }).always(function (jqXHRorResult, textStatus, jqXHRorError) { + that._deinitProgressListener(options); that._onAlways( jqXHRorResult, textStatus, From 965e300db3cf5218e0d4bebb3f4dd229bc24e865 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Sun, 14 Oct 2018 18:50:41 +0200 Subject: [PATCH 336/525] 9.22.2 --- bower.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index f31b6b61e..9ebf2e77f 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "9.22.1", + "version": "9.22.2", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images.", "keywords": [ diff --git a/package.json b/package.json index ebaf51037..aa45d7746 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "9.22.1", + "version": "9.22.2", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images, audio and video for jQuery. Supports cross-domain, chunked and resumable file uploads. Works with any server-side platform (Google App Engine, PHP, Python, Ruby on Rails, Java, etc.) that supports standard HTML form file uploads.", "keywords": [ From 6d8e0792443ac1d99b68c1b8485d60c593cc4b4e Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Sun, 14 Oct 2018 18:59:03 +0200 Subject: [PATCH 337/525] Add chunkbeforesend to callback list. --- js/jquery.fileupload.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/js/jquery.fileupload.js b/js/jquery.fileupload.js index 978f89f09..700f9013c 100644 --- a/js/jquery.fileupload.js +++ b/js/jquery.fileupload.js @@ -261,6 +261,9 @@ // Callback for dragover events of the dropZone(s): // dragover: function (e) {}, // .bind('fileuploaddragover', func); + // Callback before the start of each chunk upload request (before form data initialization): + // chunkbeforesend: function (e, data) {}, // .bind('fileuploadchunkbeforesend', func); + // Callback for the start of each chunk upload request: // chunksend: function (e, data) {}, // .bind('fileuploadchunksend', func); From 0ab8ffb49c23640c730881056e26c17c76b95491 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Sun, 14 Oct 2018 18:59:17 +0200 Subject: [PATCH 338/525] 9.23.0 --- bower.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index 9ebf2e77f..4f4cd8adb 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "9.22.2", + "version": "9.23.0", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images.", "keywords": [ diff --git a/package.json b/package.json index aa45d7746..4801d7913 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "9.22.2", + "version": "9.23.0", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images, audio and video for jQuery. Supports cross-domain, chunked and resumable file uploads. Works with any server-side platform (Google App Engine, PHP, Python, Ruby on Rails, Java, etc.) that supports standard HTML form file uploads.", "keywords": [ From eec742cf7dedf31e7659577da7f2e307f1498ead Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Sun, 14 Oct 2018 20:03:55 +0200 Subject: [PATCH 339/525] Update for compatibility with the gcloud CLI. --- server/gae-go/app.yaml | 2 -- server/gae-go/{app => }/main.go | 0 server/gae-python/app.yaml | 2 -- 3 files changed, 4 deletions(-) rename server/gae-go/{app => }/main.go (100%) diff --git a/server/gae-go/app.yaml b/server/gae-go/app.yaml index 2d09daa56..b5ac1a2e1 100644 --- a/server/gae-go/app.yaml +++ b/server/gae-go/app.yaml @@ -1,5 +1,3 @@ -application: jquery-file-upload -version: 2 runtime: go api_version: go1 diff --git a/server/gae-go/app/main.go b/server/gae-go/main.go similarity index 100% rename from server/gae-go/app/main.go rename to server/gae-go/main.go diff --git a/server/gae-python/app.yaml b/server/gae-python/app.yaml index 764449b74..0c49462fa 100644 --- a/server/gae-python/app.yaml +++ b/server/gae-python/app.yaml @@ -1,5 +1,3 @@ -application: jquery-file-upload -version: 1 runtime: python27 api_version: 1 threadsafe: true From 5d7a84ec86c7e83e02cf1f017c4693482996ed13 Mon Sep 17 00:00:00 2001 From: Alan Hogan Date: Sun, 21 Oct 2018 20:22:12 -0700 Subject: [PATCH 340/525] Alert visitors to security problem A highly visible notice seems warranted. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 76bdf89d5..dffe1655e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # jQuery File Upload Plugin +## ⚠️ Versions Before 9.22.1 are Vulnerable + +A serious exploit, [CVE-2018-9206](https://nvd.nist.gov/vuln/detail/CVE-2018-9206), exists in versions released before October 2018. [[MISC](http://www.vapidlabs.com/advisory.php?v=204)] + ## Demo [Demo File Upload](https://blueimp.github.io/jQuery-File-Upload/) From d3f323ffb7a455e0c3c99292ef12ffefb63ef4c3 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Mon, 22 Oct 2018 13:11:53 +0200 Subject: [PATCH 341/525] Add security guidelines and list of fixed vulnerabilities. --- README.md | 12 +++++- SECURITY.md | 93 ++++++++++++++++++++++++++++++++++++++++++++++ VULNERABILITIES.md | 42 +++++++++++++++++++++ 3 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 SECURITY.md create mode 100644 VULNERABILITIES.md diff --git a/README.md b/README.md index dffe1655e..1776c733e 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,13 @@ # jQuery File Upload Plugin -## ⚠️ Versions Before 9.22.1 are Vulnerable +## ⚠️ Security Notice +Security related releases: -A serious exploit, [CVE-2018-9206](https://nvd.nist.gov/vuln/detail/CVE-2018-9206), exists in versions released before October 2018. [[MISC](http://www.vapidlabs.com/advisory.php?v=204)] +* [v9.22.1](https://github.com/blueimp/jQuery-File-Upload/releases/tag/v9.22.1) Fixes a [Remote code execution vulnerability in the PHP component](VULNERABILITIES.md#remote-code-execution-vulnerability-in-the-php-component). +* v[9.10.1](https://github.com/blueimp/jQuery-File-Upload/releases/tag/9.10.1) Fixes an [Open redirect vulnerability in the GAE components](VULNERABILITIES.md#open-redirect-vulnerability-in-the-gae-components). +* Commit [4175032](https://github.com/blueimp/jQuery-File-Upload/commit/41750323a464e848856dc4c5c940663498beb74a) (*fixed in all tagged releases*) Fixes a [Cross-site scripting vulnerability in the Iframe Transport](VULNERABILITIES.md#cross-site-scripting-vulnerability-in-the-iframe-transport). + +Please read the [SECURITY](SECURITY.md) document for instructions on how to securely configure your Webserver for file uploads. ## Demo [Demo File Upload](https://blueimp.github.io/jQuery-File-Upload/) @@ -12,6 +17,9 @@ File Upload widget with multiple file selection, drag&drop support, progress Supports cross-domain, chunked and resumable file uploads and client-side image resizing. Works with any server-side platform (PHP, Python, Ruby on Rails, Java, Node.js, Go etc.) that supports standard HTML form file uploads. ## Setup +⚠️ **Notice:** +Please read the [Security recommendations](SECURITY.md) before setting up the project. + * [How to setup the plugin on your website](https://github.com/blueimp/jQuery-File-Upload/wiki/Setup) * [How to use only the basic plugin (minimal setup guide).](https://github.com/blueimp/jQuery-File-Upload/wiki/Basic-plugin) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..cdafd5e89 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,93 @@ +# File Upload Security +For an in-depth understanding of the potential security risks of providing file uploads and possible mitigations, please refer to the [OWASP - Unrestricted File Upload](https://www.owasp.org/index.php/Unrestricted_File_Upload) documentation. + +To securely setup the project to serve uploaded files, please refer to the sample [Secure file upload serving configurations](#secure-file-upload-serving-configurations). + +By default, all sample upload handlers allow only upload of image files, which mitigates some attack vectors, but should not be relied on as the only protection. + +Please also have a look at the [list of fixed vulnerabilities](VULNERABILITIES.md) in jQuery File Upload. + +## Mitigations against file upload risks + +### Prevent code execution on the server +To prevent execution of scripts or binaries on server-side, the upload directory must be configured to not execute files in the upload directory (e.g. `server/php/files` as the default for the PHP upload handler) and only treat uploaded files as static content. + +The recommended way to do this is to configure the upload directory path to point outside of the web application root. +Then the Webserver can be configured to serve files from the upload directory with their default static files handler only. + +Limiting file uploads to a whitelist of safe file types (e.g. image files) also mitigates this issue, but should not be the only protection. + +### Prevent code execution in the browser +To prevent execution of scripts on client-side, the following headers must +be sent when delivering generic uploaded files to the client: + +``` +Content-Type: application/octet-stream +X-Content-Type-Options: nosniff +``` + +The `Content-Type: application/octet-stream` header instructs browsers to display a download dialog instead of parsing it and possibly executing script content e.g. in HTML files. + +The `X-Content-Type-Options: nosniff` header prevents browsers to try to detect the file mime type despite the given content-type header. + +For known safe files, the content-type header can be adjusted using a **whitelist**, e.g. sending `Content-Type: image/png` for PNG files. + +### Prevent distribution of malware +To prevent attackers from uploading and distributing malware (e.g. computer viruses), it is recommended to limit file uploads only to a whitelist of safe file types. + +Please note that the detection of file types in the sample file upload handlers is based on the file extension and not the actual file content. This makes it still possible for attackers to upload malware by giving their files an image file extension, but should prevent automatic execution on client computers when opening those files. + +It does not protect at all from exploiting vulnerabilities in image display programs, nor from users renaming file extensions to inadvertently execute the contained malicious code. + +## Secure file upload serving configurations + +### Apache config +Add the following directive to the Apache config, replacing the directory path with the absolute path to the upload directory: + +```ApacheConf + + # To enable the Headers module, execute the following command and reload Apache: + # sudo a2enmod headers + + # The following directives prevent the execution of script files + # in the context of the website. + # They also force the content-type application/octet-stream and + # force browsers to display a download dialog for non-image files. + SetHandler default-handler + ForceType application/octet-stream + Header set Content-Disposition attachment + + # The following unsets the forced type and Content-Disposition headers + # for known image files: + + ForceType none + Header unset Content-Disposition + + + # The following directive prevents browsers from MIME-sniffing the content-type. + # This is an important complement to the ForceType directive above: + Header set X-Content-Type-Options nosniff + +``` + +### NGINX config +Add the following directive to the NGINX config, replacing the directory path with the absolute path to the upload directory: + +```Nginx +location ^~ /path/to/project/server/php/files { + root html; + default_type application/octet-stream; + types { + image/gif gif; + image/jpeg jpg; + image/png png; + } + add_header X-Content-Type-Options 'nosniff'; + if ($request_filename ~ /(((?!\.(jpg)|(png)|(gif)$)[^/])+$)) { + add_header Content-Disposition 'attachment; filename="$1"'; + # Add X-Content-Type-Options again, as using add_header in a new context + # dismisses all previous add_header calls: + add_header X-Content-Type-Options 'nosniff'; + } +} +``` diff --git a/VULNERABILITIES.md b/VULNERABILITIES.md new file mode 100644 index 000000000..8659a157a --- /dev/null +++ b/VULNERABILITIES.md @@ -0,0 +1,42 @@ +# ⚠️ List of fixed vulnerabilities + +## Remote code execution vulnerability in the PHP component +> Fixed: 2018-10-13 + +The sample [PHP upload handler](server/php/index.php) before [v9.22.1](https://github.com/blueimp/jQuery-File-Upload/releases/tag/v9.22.1) allowed to upload all file types by default. +This opens up a remote code execution vulnerability, unless the server is configured to not execute (PHP) files in the upload directory (`server/php/files`). + +The provided [.htaccess](server/php/files/.htaccess) file includes instructions for Apache to disable script execution, however [.htaccess support](https://httpd.apache.org/docs/current/howto/htaccess.html) is disabled by default since Apache `v2.3.9` via [AllowOverride Directive](https://httpd.apache.org/docs/current/mod/core.html#allowoverride). + +**You are affected if you:** +1. Uploaded jQuery File Upload `version < 9.22.1` on a Webserver that executes all PHP scripts in the project directory, e.g. Apache with `mod_php` enabled. +2. Did not actively configure your Webserver to not execute files in the upload directory (`server/php/files`). +3. Are running Apache `v2.3.9+` with the default `AllowOverride` Directive set to `None` or another Webserver with no `.htaccess` support. + +**How to fix it:** +1. Upgrade to the latest version of jQuery File Upload or limit file uploads to image file types - see [sample PHP code](server/php/index.php). +2. Configure your Webserver to not execute files in the upload directory, e.g. with the [sample Apache configuration](SECURITY.md#apache-config) + +**Further information:** +* Commit containing the security fix: [aeb47e5](https://github.com/blueimp/jQuery-File-Upload/commit/aeb47e51c67df8a504b7726595576c1c66b5dc2f) +* [Full disclosure post on Hacker News](https://news.ycombinator.com/item?id=18267309). +* [CVE-2018-9206](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-9206) +* [OWASP - Unrestricted File Upload](https://www.owasp.org/index.php/Unrestricted_File_Upload) + +## Open redirect vulnerability in the GAE components +> Fixed: 2015-06-12 + +The sample Google App Engine upload handlers before v[9.10.1](https://github.com/blueimp/jQuery-File-Upload/releases/tag/9.10.1) accepted any URL as redirect target, making it possible to use the Webserver's domain for phishing attacks. + +**Further information:** +* Commit containing the security fix: [f74d2a8](https://github.com/blueimp/jQuery-File-Upload/commit/f74d2a8c3e3b1e8e336678d2899facd5bcdb589f) +* [OWASP - Unvalidated Redirects and Forwards Cheat Sheet](https://www.owasp.org/index.php/Unvalidated_Redirects_and_Forwards_Cheat_Sheet) + +## Cross-site scripting vulnerability in the Iframe Transport +> Fixed: 2012-08-09 + +The [redirect page](cors/result.html) for the [Iframe Transport](js/jquery.iframe-transport.js) before commit [4175032](https://github.com/blueimp/jQuery-File-Upload/commit/41750323a464e848856dc4c5c940663498beb74a) (*fixed in all tagged releases*) allowed executing arbitrary JavaScript in the context of the Webserver. + +**Further information:** +* Commit containing the security fix: [4175032](https://github.com/blueimp/jQuery-File-Upload/commit/41750323a464e848856dc4c5c940663498beb74a) +* [OWASP - Cross-site Scripting (XSS)](https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)) From 3e828564324cf5aea2b0d0c7f3a7a17996cb9a9a Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Mon, 22 Oct 2018 13:27:52 +0200 Subject: [PATCH 342/525] 9.24.0 --- bower.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index 4f4cd8adb..59e7a398e 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "9.23.0", + "version": "9.24.0", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images.", "keywords": [ diff --git a/package.json b/package.json index 4801d7913..79155a973 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "9.23.0", + "version": "9.24.0", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images, audio and video for jQuery. Supports cross-domain, chunked and resumable file uploads. Works with any server-side platform (Google App Engine, PHP, Python, Ruby on Rails, Java, etc.) that supports standard HTML form file uploads.", "keywords": [ From ad4aefd96e4056deab6fea2690f0d8cf56bb2d7d Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Tue, 23 Oct 2018 12:50:28 +0200 Subject: [PATCH 343/525] SECURITY FIX: Only allow image file types by default. This moves the image file types limit in the library file. This also adds a default setting to replace dots in filenames. --- server/php/UploadHandler.php | 31 +++++++++++++++++++++++++++++-- server/php/index.php | 15 +-------------- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/server/php/UploadHandler.php b/server/php/UploadHandler.php index 285d046aa..f218f3f62 100755 --- a/server/php/UploadHandler.php +++ b/server/php/UploadHandler.php @@ -89,8 +89,25 @@ public function __construct($options = null, $initialize = true, $error_messages 'readfile_chunk_size' => 10 * 1024 * 1024, // 10 MiB // Defines which files can be displayed inline when downloaded: 'inline_file_types' => '/\.(gif|jpe?g|png)$/i', - // Defines which files (based on their names) are accepted for upload: - 'accept_file_types' => '/.+$/i', + // Defines which files (based on their names) are accepted for upload. + // By default, only allows file uploads with image file extensions. + // Only change this setting after making sure that any allowed file + // types cannot be executed by the webserver in the files directory, + // e.g. PHP scripts, nor executed by the browser when downloaded, + // e.g. HTML files with embedded JavaScript code. + // Please also read the SECURITY.md document in this repository. + 'accept_file_types' => '/\.(gif|jpe?g|png)$/i', + // Replaces dots in filenames with the given string. + // Can be disabled by setting it to false or an empty string. + // Note that this is a security feature for servers that support + // multiple file extensions, e.g. the Apache AddHandler Directive: + // https://httpd.apache.org/docs/current/mod/mod_mime.html#addhandler + // Before disabling it, make sure that files uploaded with multiple + // extensions cannot be executed by the webserver, e.g. + // "example.php.png" with embedded PHP code, nor executed by the + // browser when downloaded, e.g. "example.html.gif" with embedded + // JavaScript code. + 'replace_dots_in_filenames' => '-', // The php.ini settings upload_max_filesize and post_max_size // take precedence over the following max_file_size setting: 'max_file_size' => null, @@ -527,6 +544,16 @@ protected function trim_file_name($file_path, $name, $size, $type, $error, // into different directories or replacing hidden system files. // Also remove control characters and spaces (\x00..\x20) around the filename: $name = trim($this->basename(stripslashes($name)), ".\x00..\x20"); + // Replace dots in filenames to avoid security issues with servers + // that interpret multiple file extensions, e.g. "example.php.png": + $replacement = $this->options['replace_dots_in_filenames']; + if (!empty($replacement)) { + $parts = explode('.', $name); + if (count($parts) > 2) { + $ext = array_pop($parts); + $name = implode($replacement, $parts).'.'.$ext; + } + } // Use a timestamp for empty filenames: if (!$name) { $name = str_replace('.', '-', microtime(true)); diff --git a/server/php/index.php b/server/php/index.php index c1dbcaf66..6caabb710 100644 --- a/server/php/index.php +++ b/server/php/index.php @@ -12,17 +12,4 @@ error_reporting(E_ALL | E_STRICT); require('UploadHandler.php'); -$upload_handler = new UploadHandler(array( - - // SECURITY NOTICE: - // Only change the accept_file_types setting after making sure that any - // allowed file types cannot be executed by the webserver in the files - // directory (e.g. PHP scripts), nor executed by the browser when downloaded - // (e.g. HTML files with embedded JavaScript code). - // e.g. in Apache, make sure the provided .htaccess file is present in the - // files directory and .htaccess support has been enabled: - // https://httpd.apache.org/docs/current/howto/htaccess.html - - // By default, only allow file uploads with image file extensions: - 'accept_file_types' => '/\.(gif|jpe?g|png)$/i' -)); +$upload_handler = new UploadHandler(); From a513d514c390f8b4c149ff1720ee27054791f80d Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Tue, 23 Oct 2018 13:18:36 +0200 Subject: [PATCH 344/525] Update VULNERABILITIES documentation. --- README.md | 2 +- VULNERABILITIES.md | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1776c733e..f8a2b532a 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ## ⚠️ Security Notice Security related releases: -* [v9.22.1](https://github.com/blueimp/jQuery-File-Upload/releases/tag/v9.22.1) Fixes a [Remote code execution vulnerability in the PHP component](VULNERABILITIES.md#remote-code-execution-vulnerability-in-the-php-component). +* [v9.24.1](https://github.com/blueimp/jQuery-File-Upload/releases/tag/v9.24.1) Fixes a [Remote code execution vulnerability in the PHP component](VULNERABILITIES.md#remote-code-execution-vulnerability-in-the-php-component). * v[9.10.1](https://github.com/blueimp/jQuery-File-Upload/releases/tag/9.10.1) Fixes an [Open redirect vulnerability in the GAE components](VULNERABILITIES.md#open-redirect-vulnerability-in-the-gae-components). * Commit [4175032](https://github.com/blueimp/jQuery-File-Upload/commit/41750323a464e848856dc4c5c940663498beb74a) (*fixed in all tagged releases*) Fixes a [Cross-site scripting vulnerability in the Iframe Transport](VULNERABILITIES.md#cross-site-scripting-vulnerability-in-the-iframe-transport). diff --git a/VULNERABILITIES.md b/VULNERABILITIES.md index 8659a157a..256e8f992 100644 --- a/VULNERABILITIES.md +++ b/VULNERABILITIES.md @@ -1,24 +1,33 @@ # ⚠️ List of fixed vulnerabilities ## Remote code execution vulnerability in the PHP component -> Fixed: 2018-10-13 +> Fixed: 2018-10-23 -The sample [PHP upload handler](server/php/index.php) before [v9.22.1](https://github.com/blueimp/jQuery-File-Upload/releases/tag/v9.22.1) allowed to upload all file types by default. +The sample [PHP upload handler](server/php/UploadHandler.php) before [v9.24.1](https://github.com/blueimp/jQuery-File-Upload/releases/tag/v9.24.1) allowed to upload all file types by default. This opens up a remote code execution vulnerability, unless the server is configured to not execute (PHP) files in the upload directory (`server/php/files`). The provided [.htaccess](server/php/files/.htaccess) file includes instructions for Apache to disable script execution, however [.htaccess support](https://httpd.apache.org/docs/current/howto/htaccess.html) is disabled by default since Apache `v2.3.9` via [AllowOverride Directive](https://httpd.apache.org/docs/current/mod/core.html#allowoverride). **You are affected if you:** -1. Uploaded jQuery File Upload `version < 9.22.1` on a Webserver that executes all PHP scripts in the project directory, e.g. Apache with `mod_php` enabled. +1. A) Uploaded jQuery File Upload < `v9.24.1` on a Webserver that executes files with `.php` as part of the file extension (e.g. "example.php.png"), e.g. Apache with `mod_php` enabled and the following directive (*not a recommended configuration*): + ```ApacheConf + AddHandler php5-script .php + ``` + B) Uploaded jQuery File Upload < `v9.22.1` on a Webserver that executes files with the file extension `.php`, e.g. Apache with `mod_php` enabled and the following directive: + ```ApacheConf + + SetHandler application/x-httpd-php + + ``` 2. Did not actively configure your Webserver to not execute files in the upload directory (`server/php/files`). 3. Are running Apache `v2.3.9+` with the default `AllowOverride` Directive set to `None` or another Webserver with no `.htaccess` support. **How to fix it:** -1. Upgrade to the latest version of jQuery File Upload or limit file uploads to image file types - see [sample PHP code](server/php/index.php). +1. Upgrade to the latest version of jQuery File Upload. 2. Configure your Webserver to not execute files in the upload directory, e.g. with the [sample Apache configuration](SECURITY.md#apache-config) **Further information:** -* Commit containing the security fix: [aeb47e5](https://github.com/blueimp/jQuery-File-Upload/commit/aeb47e51c67df8a504b7726595576c1c66b5dc2f) +* Commits containing the security fix: [aeb47e5](https://github.com/blueimp/jQuery-File-Upload/commit/aeb47e51c67df8a504b7726595576c1c66b5dc2f), [3e82856](https://github.com/blueimp/jQuery-File-Upload/commit/3e828564324cf5aea2b0d0c7f3a7a17996cb9a9a) * [Full disclosure post on Hacker News](https://news.ycombinator.com/item?id=18267309). * [CVE-2018-9206](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-9206) * [OWASP - Unrestricted File Upload](https://www.owasp.org/index.php/Unrestricted_File_Upload) From d17e60162b89810ba4096cac1549b8636b289a59 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Tue, 23 Oct 2018 13:18:50 +0200 Subject: [PATCH 345/525] 9.24.1 --- bower.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index 59e7a398e..6ed453c24 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "9.24.0", + "version": "9.24.1", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images.", "keywords": [ diff --git a/package.json b/package.json index 79155a973..6356810aa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "9.24.0", + "version": "9.24.1", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images, audio and video for jQuery. Supports cross-domain, chunked and resumable file uploads. Works with any server-side platform (Google App Engine, PHP, Python, Ruby on Rails, Java, etc.) that supports standard HTML form file uploads.", "keywords": [ From 286f25ce9646b7f699110ef877e346930b1b9cad Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Tue, 23 Oct 2018 13:22:36 +0200 Subject: [PATCH 346/525] Fix commit ID. --- VULNERABILITIES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VULNERABILITIES.md b/VULNERABILITIES.md index 256e8f992..f1fe261be 100644 --- a/VULNERABILITIES.md +++ b/VULNERABILITIES.md @@ -27,7 +27,7 @@ The provided [.htaccess](server/php/files/.htaccess) file includes instructions 2. Configure your Webserver to not execute files in the upload directory, e.g. with the [sample Apache configuration](SECURITY.md#apache-config) **Further information:** -* Commits containing the security fix: [aeb47e5](https://github.com/blueimp/jQuery-File-Upload/commit/aeb47e51c67df8a504b7726595576c1c66b5dc2f), [3e82856](https://github.com/blueimp/jQuery-File-Upload/commit/3e828564324cf5aea2b0d0c7f3a7a17996cb9a9a) +* Commits containing the security fix: [aeb47e5](https://github.com/blueimp/jQuery-File-Upload/commit/aeb47e51c67df8a504b7726595576c1c66b5dc2f), [ad4aefd](https://github.com/blueimp/jQuery-File-Upload/commit/ad4aefd96e4056deab6fea2690f0d8cf56bb2d7d) * [Full disclosure post on Hacker News](https://news.ycombinator.com/item?id=18267309). * [CVE-2018-9206](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-9206) * [OWASP - Unrestricted File Upload](https://www.owasp.org/index.php/Unrestricted_File_Upload) From 6fb12e3b5dbe58fb5c167f6e1fc3cea79ac44b9c Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Wed, 24 Oct 2018 12:49:13 +0200 Subject: [PATCH 347/525] Add info on purpose of this project. Add note to test security of configurations. --- SECURITY.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/SECURITY.md b/SECURITY.md index cdafd5e89..afa1da13c 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -5,7 +5,19 @@ To securely setup the project to serve uploaded files, please refer to the sampl By default, all sample upload handlers allow only upload of image files, which mitigates some attack vectors, but should not be relied on as the only protection. -Please also have a look at the [list of fixed vulnerabilities](VULNERABILITIES.md) in jQuery File Upload. +Please also have a look at the [list of fixed vulnerabilities](VULNERABILITIES.md) in jQuery File Upload, which relates mostly to the sample server-side upload handlers and how they have been configured. + +## Purpose of this project +Please note that this project is not a complete file management product, but foremost a client-side file upload library for [jQuery](https://jquery.com/). +The server-side sample upload handlers are just examples to demonstrate the client-side file upload functionality. + +To make this very clear, there is **no user authentication** by default: +* **everyone can upload files** +* **everyone can delete uploaded files** + +In some cases this can be acceptable, but for most projects you will want to extend the sample upload handlers to integrate user authentication, or implement your own. + +It is also up to you to configure your Webserver to securely serve the uploaded files, e.g. using the [sample server configurations](#secure-file-upload-serving-configurations). ## Mitigations against file upload risks @@ -40,6 +52,12 @@ Please note that the detection of file types in the sample file upload handlers It does not protect at all from exploiting vulnerabilities in image display programs, nor from users renaming file extensions to inadvertently execute the contained malicious code. ## Secure file upload serving configurations +The following configurations serve uploaded files as static files with the proper headers as [mitigation against file upload risks](#mitigations-against-file-upload-risks). +Please do not simply copy&paste these configurations, but make sure you understand what they are doing and that you have implemented them correctly. + +> Always test your own setup and make sure that it is secure! + +e.g. try uploading PHP scripts (as "example.php", "example.php.png" and "example.png") to see if they get executed by your Webserver. ### Apache config Add the following directive to the Apache config, replacing the directory path with the absolute path to the upload directory: From 56ebf0f4589c2b9116876f8f0ff26fe875d181e1 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Wed, 24 Oct 2018 12:49:28 +0200 Subject: [PATCH 348/525] 9.25.0 --- bower.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index 6ed453c24..9e31381a8 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "9.24.1", + "version": "9.25.0", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images.", "keywords": [ diff --git a/package.json b/package.json index 6356810aa..6f5bfef40 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "9.24.1", + "version": "9.25.0", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images, audio and video for jQuery. Supports cross-domain, chunked and resumable file uploads. Works with any server-side platform (Google App Engine, PHP, Python, Ruby on Rails, Java, etc.) that supports standard HTML form file uploads.", "keywords": [ From fe44d34be43be32c6b8d507932f318dababb25dd Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Fri, 26 Oct 2018 00:32:14 +0200 Subject: [PATCH 349/525] SECURITY: Verify file signatures before image processing. This mitigates potential vulnerabilities in ImageMagick when handling input files other than GIF/JPEG/PNG. However this does not prevent all potential vulnerabilities with ImageMagick. It is therefore recommended to disable all non-required ImageMagick coders via policy.xml. See also: https://imagetragick.com/ https://www.kb.cert.org/vuls/id/332928 https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=imagemagick --- server/php/UploadHandler.php | 51 ++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/server/php/UploadHandler.php b/server/php/UploadHandler.php index f218f3f62..e44004395 100755 --- a/server/php/UploadHandler.php +++ b/server/php/UploadHandler.php @@ -38,6 +38,10 @@ class UploadHandler 'image_resize' => 'Failed to resize image' ); + protected const IMAGETYPE_GIF = 1; + protected const IMAGETYPE_JPEG = 2; + protected const IMAGETYPE_PNG = 3; + protected $image_objects = array(); public function __construct($options = null, $initialize = true, $error_messages = null) { @@ -114,9 +118,7 @@ public function __construct($options = null, $initialize = true, $error_messages 'min_file_size' => 1, // The maximum number of files for the upload directory: 'max_number_of_files' => null, - // Defines which files are handled as image files: - 'image_file_types' => '/\.(gif|jpe?g|png)$/i', - // Use exif_imagetype on all files to correct file extensions: + // Reads first file bytes to identify and correct file extensions: 'correct_image_extensions' => false, // Image resolution restrictions: 'max_width' => null, @@ -163,7 +165,7 @@ public function __construct($options = null, $initialize = true, $error_messages 'max_width' => 800, 'max_height' => 600 ), - */ + */ 'thumbnail' => array( // Uncomment the following to use a defined directory for the thumbnails // instead of a subdirectory based on the version identifier. @@ -433,9 +435,8 @@ protected function validate($uploaded_file, $file, $error, $index) { $min_width = @$this->options['min_width']; $min_height = @$this->options['min_height']; if (($max_width || $max_height || $min_width || $min_height) - && preg_match($this->options['image_file_types'], $file->name)) { + && $this->is_valid_image_file($uploaded_file)) { list($img_width, $img_height) = $this->get_image_size($uploaded_file); - // If we are auto rotating the image by default, do the checks on // the correct orientation if ( @@ -449,7 +450,6 @@ function_exists('exif_read_data') && $img_height = $tmp; unset($tmp); } - } if (!empty($img_width)) { if ($max_width && $img_width > $max_width) { @@ -511,16 +511,15 @@ protected function fix_file_extension($file_path, $name, $size, $type, $error, preg_match('/^image\/(gif|jpe?g|png)/', $type, $matches)) { $name .= '.'.$matches[1]; } - if ($this->options['correct_image_extensions'] && - function_exists('exif_imagetype')) { - switch (@exif_imagetype($file_path)){ - case IMAGETYPE_JPEG: + if ($this->options['correct_image_extensions']) { + switch ($this->imagetype($file_path)) { + case self::IMAGETYPE_JPEG: $extensions = array('jpg', 'jpeg'); break; - case IMAGETYPE_PNG: + case self::IMAGETYPE_PNG: $extensions = array('png'); break; - case IMAGETYPE_GIF: + case self::IMAGETYPE_GIF: $extensions = array('gif'); break; } @@ -1063,15 +1062,27 @@ protected function destroy_image_object($file_path) { } } - protected function is_valid_image_file($file_path) { - if (!preg_match($this->options['image_file_types'], $file_path)) { - return false; + protected function imagetype($file_path) { + $fp = fopen($file_path, 'r'); + $data = fread($fp, 4); + fclose($fp); + // GIF: 47 49 46 + if (substr($data, 0, 3) === 'GIF') { + return self::IMAGETYPE_GIF; } - if (function_exists('exif_imagetype')) { - return @exif_imagetype($file_path); + // JPG: FF D8 + if (bin2hex(substr($data, 0, 2)) === 'ffd8') { + return self::IMAGETYPE_JPEG; } - $image_info = $this->get_image_size($file_path); - return $image_info && $image_info[0] && $image_info[1]; + // PNG: 89 50 4E 47 + if (bin2hex(@$data[0]).substr($data, 1, 4) === '89PNG') { + return self::IMAGETYPE_PNG; + } + return false; + } + + protected function is_valid_image_file($file_path) { + return !!$this->imagetype($file_path); } protected function handle_image_file($file_path, $file) { From 26bcba1f5e541163389aa955583bc4bc0f450421 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Fri, 26 Oct 2018 01:21:31 +0200 Subject: [PATCH 350/525] Add info on mitigations of image processing vulnerabilities. --- README.md | 20 +++++++++----------- SECURITY.md | 22 ++++++++++++++++++++++ VULNERABILITIES.md | 20 +++++++++++++++++--- 3 files changed, 48 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index f8a2b532a..d2f104d5c 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,23 @@ # jQuery File Upload Plugin +## Description +File Upload widget with multiple file selection, drag&drop support, progress bars, validation and preview images, audio and video for jQuery. +Supports cross-domain, chunked and resumable file uploads and client-side image resizing. Works with any server-side platform (PHP, Python, Ruby on Rails, Java, Node.js, Go etc.) that supports standard HTML form file uploads. + +## Demo +[Demo File Upload](https://blueimp.github.io/jQuery-File-Upload/) + ## ⚠️ Security Notice Security related releases: +* [v9.25.1](https://github.com/blueimp/jQuery-File-Upload/releases/tag/v9.25.1) Mitigates some [Potential vulnerabilities with PHP+ImageMagick](VULNERABILITIES.md#potential-vulnerabilities-with-php+imagemagick). * [v9.24.1](https://github.com/blueimp/jQuery-File-Upload/releases/tag/v9.24.1) Fixes a [Remote code execution vulnerability in the PHP component](VULNERABILITIES.md#remote-code-execution-vulnerability-in-the-php-component). * v[9.10.1](https://github.com/blueimp/jQuery-File-Upload/releases/tag/9.10.1) Fixes an [Open redirect vulnerability in the GAE components](VULNERABILITIES.md#open-redirect-vulnerability-in-the-gae-components). * Commit [4175032](https://github.com/blueimp/jQuery-File-Upload/commit/41750323a464e848856dc4c5c940663498beb74a) (*fixed in all tagged releases*) Fixes a [Cross-site scripting vulnerability in the Iframe Transport](VULNERABILITIES.md#cross-site-scripting-vulnerability-in-the-iframe-transport). Please read the [SECURITY](SECURITY.md) document for instructions on how to securely configure your Webserver for file uploads. -## Demo -[Demo File Upload](https://blueimp.github.io/jQuery-File-Upload/) - -## Description -File Upload widget with multiple file selection, drag&drop support, progress bars, validation and preview images, audio and video for jQuery. -Supports cross-domain, chunked and resumable file uploads and client-side image resizing. Works with any server-side platform (PHP, Python, Ruby on Rails, Java, Node.js, Go etc.) that supports standard HTML form file uploads. - ## Setup -⚠️ **Notice:** -Please read the [Security recommendations](SECURITY.md) before setting up the project. - * [How to setup the plugin on your website](https://github.com/blueimp/jQuery-File-Upload/wiki/Setup) * [How to use only the basic plugin (minimal setup guide).](https://github.com/blueimp/jQuery-File-Upload/wiki/Basic-plugin) @@ -27,7 +25,7 @@ Please read the [Security recommendations](SECURITY.md) before setting up the pr * **Multiple file upload:** Allows to select multiple files at once and upload them simultaneously. * **Drag & Drop support:** - Allows to upload files by dragging them from your desktop or filemanager and dropping them on your browser window. + Allows to upload files by dragging them from your desktop or file manager and dropping them on your browser window. * **Upload progress bar:** Shows a progress bar indicating the upload progress for individual files and for all uploads combined. * **Cancelable uploads:** diff --git a/SECURITY.md b/SECURITY.md index afa1da13c..db58f7dd7 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -3,6 +3,8 @@ For an in-depth understanding of the potential security risks of providing file To securely setup the project to serve uploaded files, please refer to the sample [Secure file upload serving configurations](#secure-file-upload-serving-configurations). +To mitigate potential vulnerabilities in image processing libraries, please refer to the [Secure image processing configurations](#secure-image-processing-configurations). + By default, all sample upload handlers allow only upload of image files, which mitigates some attack vectors, but should not be relied on as the only protection. Please also have a look at the [list of fixed vulnerabilities](VULNERABILITIES.md) in jQuery File Upload, which relates mostly to the sample server-side upload handlers and how they have been configured. @@ -109,3 +111,23 @@ location ^~ /path/to/project/server/php/files { } } ``` + +## Secure image processing configurations +The following configuration mitigates [potential image processing vulnerabilities with ImageMagick](VULNERABILITIES.md#potential-vulnerabilities-with-php+imagemagick) by limiting the attack vectors to a small subset of image types (`GIF/JPEG/PNG`). + +Please also consider using alternative, safer image processing libraries like [libvips](https://github.com/libvips/libvips) or [imageflow](https://github.com/imazen/imageflow). + +## ImageMagick config +It is recommended to disable all non-required ImageMagick coders via [policy.xml](https://wiki.debian.org/imagemagick/security). +To do so, locate the ImageMagick `policy.xml` configuration file and add the following policies: + +```xml + + + + + + + + +``` diff --git a/VULNERABILITIES.md b/VULNERABILITIES.md index f1fe261be..1561a1401 100644 --- a/VULNERABILITIES.md +++ b/VULNERABILITIES.md @@ -1,7 +1,21 @@ # ⚠️ List of fixed vulnerabilities +## Potential vulnerabilities with PHP+ImageMagick +> Mitigated: 2018-10-25 (GMT) + +The sample [PHP upload handler](server/php/UploadHandler.php) before [v9.25.1](https://github.com/blueimp/jQuery-File-Upload/releases/tag/v9.25.1) did not validate file signatures before invoking [ImageMagick](https://www.imagemagick.org/) (via [Imagick](http://php.net/manual/en/book.imagick.php)). +Verifying those [magic bytes](https://en.wikipedia.org/wiki/List_of_file_signatures) mitigates potential vulnerabilities when handling input files other than `GIF/JPEG/PNG`. + +Please also configure ImageMagick to only enable the coders required for `GIF/JPEG/PNG` processing, e.g. with the sample [ImageMagick config](SECURITY.md#imagemagick-config). + +**Further information:** +* Commit containing the mitigation: [fe44d34](https://github.com/blueimp/jQuery-File-Upload/commit/fe44d34be43be32c6b8d507932f318dababb25dd) +* [ImageTragick](https://imagetragick.com/) +* [CERT Vulnerability Note VU#332928](https://www.kb.cert.org/vuls/id/332928) +* [ImageMagick CVE entries](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=imagemagick) + ## Remote code execution vulnerability in the PHP component -> Fixed: 2018-10-23 +> Fixed: 2018-10-23 (GMT) The sample [PHP upload handler](server/php/UploadHandler.php) before [v9.24.1](https://github.com/blueimp/jQuery-File-Upload/releases/tag/v9.24.1) allowed to upload all file types by default. This opens up a remote code execution vulnerability, unless the server is configured to not execute (PHP) files in the upload directory (`server/php/files`). @@ -33,7 +47,7 @@ The provided [.htaccess](server/php/files/.htaccess) file includes instructions * [OWASP - Unrestricted File Upload](https://www.owasp.org/index.php/Unrestricted_File_Upload) ## Open redirect vulnerability in the GAE components -> Fixed: 2015-06-12 +> Fixed: 2015-06-12 (GMT) The sample Google App Engine upload handlers before v[9.10.1](https://github.com/blueimp/jQuery-File-Upload/releases/tag/9.10.1) accepted any URL as redirect target, making it possible to use the Webserver's domain for phishing attacks. @@ -42,7 +56,7 @@ The sample Google App Engine upload handlers before v[9.10.1](https://github.com * [OWASP - Unvalidated Redirects and Forwards Cheat Sheet](https://www.owasp.org/index.php/Unvalidated_Redirects_and_Forwards_Cheat_Sheet) ## Cross-site scripting vulnerability in the Iframe Transport -> Fixed: 2012-08-09 +> Fixed: 2012-08-09 (GMT) The [redirect page](cors/result.html) for the [Iframe Transport](js/jquery.iframe-transport.js) before commit [4175032](https://github.com/blueimp/jQuery-File-Upload/commit/41750323a464e848856dc4c5c940663498beb74a) (*fixed in all tagged releases*) allowed executing arbitrary JavaScript in the context of the Webserver. From 3750d3a10c40814e57edfb77d04857e8f3389c59 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Fri, 26 Oct 2018 01:21:47 +0200 Subject: [PATCH 351/525] 9.25.1 --- bower.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index 9e31381a8..34594a32f 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "9.25.0", + "version": "9.25.1", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images.", "keywords": [ diff --git a/package.json b/package.json index 6f5bfef40..4b33a0362 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "9.25.0", + "version": "9.25.1", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images, audio and video for jQuery. Supports cross-domain, chunked and resumable file uploads. Works with any server-side platform (Google App Engine, PHP, Python, Ruby on Rails, Java, etc.) that supports standard HTML form file uploads.", "keywords": [ From a356654bef180821578b8cf971ca5d5c636670f3 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Fri, 26 Oct 2018 01:25:32 +0200 Subject: [PATCH 352/525] Fix link anchor. --- README.md | 2 +- SECURITY.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d2f104d5c..d9e16ed18 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Supports cross-domain, chunked and resumable file uploads and client-side image ## ⚠️ Security Notice Security related releases: -* [v9.25.1](https://github.com/blueimp/jQuery-File-Upload/releases/tag/v9.25.1) Mitigates some [Potential vulnerabilities with PHP+ImageMagick](VULNERABILITIES.md#potential-vulnerabilities-with-php+imagemagick). +* [v9.25.1](https://github.com/blueimp/jQuery-File-Upload/releases/tag/v9.25.1) Mitigates some [Potential vulnerabilities with PHP+ImageMagick](VULNERABILITIES.md#potential-vulnerabilities-with-php-imagemagick). * [v9.24.1](https://github.com/blueimp/jQuery-File-Upload/releases/tag/v9.24.1) Fixes a [Remote code execution vulnerability in the PHP component](VULNERABILITIES.md#remote-code-execution-vulnerability-in-the-php-component). * v[9.10.1](https://github.com/blueimp/jQuery-File-Upload/releases/tag/9.10.1) Fixes an [Open redirect vulnerability in the GAE components](VULNERABILITIES.md#open-redirect-vulnerability-in-the-gae-components). * Commit [4175032](https://github.com/blueimp/jQuery-File-Upload/commit/41750323a464e848856dc4c5c940663498beb74a) (*fixed in all tagged releases*) Fixes a [Cross-site scripting vulnerability in the Iframe Transport](VULNERABILITIES.md#cross-site-scripting-vulnerability-in-the-iframe-transport). diff --git a/SECURITY.md b/SECURITY.md index db58f7dd7..86ffc163b 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -113,7 +113,7 @@ location ^~ /path/to/project/server/php/files { ``` ## Secure image processing configurations -The following configuration mitigates [potential image processing vulnerabilities with ImageMagick](VULNERABILITIES.md#potential-vulnerabilities-with-php+imagemagick) by limiting the attack vectors to a small subset of image types (`GIF/JPEG/PNG`). +The following configuration mitigates [potential image processing vulnerabilities with ImageMagick](VULNERABILITIES.md#potential-vulnerabilities-with-php-imagemagick) by limiting the attack vectors to a small subset of image types (`GIF/JPEG/PNG`). Please also consider using alternative, safer image processing libraries like [libvips](https://github.com/libvips/libvips) or [imageflow](https://github.com/imazen/imageflow). From 92921ef9f3cc67645aef275afd7eabde9f76dd90 Mon Sep 17 00:00:00 2001 From: edent Date: Wed, 31 Oct 2018 10:45:02 +0000 Subject: [PATCH 353/525] Add SRI to external JavaScript --- angularjs.html | 12 ++++++------ basic-plus.html | 8 ++++---- basic.html | 4 ++-- cors/postmessage.html | 2 +- index.html | 12 ++++++------ jquery-ui.html | 12 ++++++------ test/index.html | 10 +++++----- 7 files changed, 30 insertions(+), 30 deletions(-) diff --git a/angularjs.html b/angularjs.html index 2051bbf79..54ee0a10f 100644 --- a/angularjs.html +++ b/angularjs.html @@ -177,18 +177,18 @@

      - - + + - + - + - + - + diff --git a/basic-plus.html b/basic-plus.html index acee24843..acf715cc3 100644 --- a/basic-plus.html +++ b/basic-plus.html @@ -96,15 +96,15 @@

      Demo Notes

      - + - + - + - + diff --git a/basic.html b/basic.html index 232a24624..d67e8e919 100644 --- a/basic.html +++ b/basic.html @@ -96,7 +96,7 @@

      Demo Notes

      - + @@ -104,7 +104,7 @@

      Demo Notes

      - + + - + - + - + - + - + - + diff --git a/jquery-ui.html b/jquery-ui.html index 842dd4ca7..0bdaa3f59 100644 --- a/jquery-ui.html +++ b/jquery-ui.html @@ -201,16 +201,16 @@

      {% } %} - - + + - + - + - + - + diff --git a/test/index.html b/test/index.html index 0b5cf57b7..73eca84f8 100644 --- a/test/index.html +++ b/test/index.html @@ -145,11 +145,11 @@

      {% } %} - + - - - + + + - + From fa9ddd2a4c0e686f659ffd7476691303012ea8ee Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 1 Nov 2018 23:09:53 +0100 Subject: [PATCH 354/525] Add Subresource Integrity checksums to external CSS. --- angularjs.html | 4 ++-- basic-plus.html | 2 +- basic.html | 2 +- index.html | 4 ++-- jquery-ui.html | 2 +- test/index.html | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/angularjs.html b/angularjs.html index 54ee0a10f..f571bd187 100644 --- a/angularjs.html +++ b/angularjs.html @@ -22,11 +22,11 @@ - + - + diff --git a/basic-plus.html b/basic-plus.html index acf715cc3..e63aab41b 100644 --- a/basic-plus.html +++ b/basic-plus.html @@ -20,7 +20,7 @@ - + diff --git a/basic.html b/basic.html index d67e8e919..a9ef6273e 100644 --- a/basic.html +++ b/basic.html @@ -20,7 +20,7 @@ - + diff --git a/index.html b/index.html index cf449d60e..f0f396550 100644 --- a/index.html +++ b/index.html @@ -22,11 +22,11 @@ - + - + diff --git a/jquery-ui.html b/jquery-ui.html index 0bdaa3f59..bc17318a8 100644 --- a/jquery-ui.html +++ b/jquery-ui.html @@ -22,7 +22,7 @@ - + diff --git a/test/index.html b/test/index.html index 73eca84f8..fe5f44a34 100644 --- a/test/index.html +++ b/test/index.html @@ -20,7 +20,7 @@ Codestin Search App - +

      jQuery File Upload Plugin Test

      From 10a66db5f3bf5ea69ea3d7751e02ad37c841dd62 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 1 Nov 2018 23:10:05 +0100 Subject: [PATCH 355/525] 9.26.0 --- bower.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index 34594a32f..4569ae78c 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "9.25.1", + "version": "9.26.0", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images.", "keywords": [ diff --git a/package.json b/package.json index 4b33a0362..b76544548 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "9.25.1", + "version": "9.26.0", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images, audio and video for jQuery. Supports cross-domain, chunked and resumable file uploads. Works with any server-side platform (Google App Engine, PHP, Python, Ruby on Rails, Java, etc.) that supports standard HTML form file uploads.", "keywords": [ From 7a4bf4824b23bd3a23f5a8ebf35d471252d198b5 Mon Sep 17 00:00:00 2001 From: Silicon Forks Date: Tue, 6 Nov 2018 19:09:01 -0400 Subject: [PATCH 356/525] Remove "protected" visibility modifier from const This restores compatibility with PHP 5.6 and PHP 7.0. --- server/php/UploadHandler.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/php/UploadHandler.php b/server/php/UploadHandler.php index e44004395..492ba56cc 100755 --- a/server/php/UploadHandler.php +++ b/server/php/UploadHandler.php @@ -38,9 +38,9 @@ class UploadHandler 'image_resize' => 'Failed to resize image' ); - protected const IMAGETYPE_GIF = 1; - protected const IMAGETYPE_JPEG = 2; - protected const IMAGETYPE_PNG = 3; + const IMAGETYPE_GIF = 1; + const IMAGETYPE_JPEG = 2; + const IMAGETYPE_PNG = 3; protected $image_objects = array(); From ff55388c021057778c026f31b8184815d547c862 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Wed, 7 Nov 2018 11:41:14 +0100 Subject: [PATCH 357/525] Catch image scaling exceptions. --- server/php/UploadHandler.php | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/server/php/UploadHandler.php b/server/php/UploadHandler.php index 492ba56cc..12ff5ec30 100755 --- a/server/php/UploadHandler.php +++ b/server/php/UploadHandler.php @@ -1047,13 +1047,18 @@ protected function get_image_size($file_path) { } protected function create_scaled_image($file_name, $version, $options) { - if ($this->options['image_library'] === 2) { - return $this->imagemagick_create_scaled_image($file_name, $version, $options); - } - if ($this->options['image_library'] && extension_loaded('imagick')) { - return $this->imagick_create_scaled_image($file_name, $version, $options); + try { + if ($this->options['image_library'] === 2) { + return $this->imagemagick_create_scaled_image($file_name, $version, $options); + } + if ($this->options['image_library'] && extension_loaded('imagick')) { + return $this->imagick_create_scaled_image($file_name, $version, $options); + } + return $this->gd_create_scaled_image($file_name, $version, $options); + } catch (\Exception $e) { + error_log($e->getMessage()); + return false; } - return $this->gd_create_scaled_image($file_name, $version, $options); } protected function destroy_image_object($file_path) { From 1da893f232434e5bcbb32f81f6d23a5b622b239e Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Wed, 7 Nov 2018 12:04:49 +0100 Subject: [PATCH 358/525] Use all 4 bytes to validate GIF headers. --- server/php/UploadHandler.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/php/UploadHandler.php b/server/php/UploadHandler.php index 12ff5ec30..2b03b8406 100755 --- a/server/php/UploadHandler.php +++ b/server/php/UploadHandler.php @@ -1071,8 +1071,8 @@ protected function imagetype($file_path) { $fp = fopen($file_path, 'r'); $data = fread($fp, 4); fclose($fp); - // GIF: 47 49 46 - if (substr($data, 0, 3) === 'GIF') { + // GIF: 47 49 46 38 + if ($data === 'GIF8') { return self::IMAGETYPE_GIF; } // JPG: FF D8 From fac6b5f5a72ede558ff60960f2c6c2943235bef7 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Wed, 7 Nov 2018 12:18:33 +0100 Subject: [PATCH 359/525] Use the first 3 bytes to validate JPEG headers. --- server/php/UploadHandler.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/php/UploadHandler.php b/server/php/UploadHandler.php index 2b03b8406..459375f06 100755 --- a/server/php/UploadHandler.php +++ b/server/php/UploadHandler.php @@ -1075,8 +1075,8 @@ protected function imagetype($file_path) { if ($data === 'GIF8') { return self::IMAGETYPE_GIF; } - // JPG: FF D8 - if (bin2hex(substr($data, 0, 2)) === 'ffd8') { + // JPG: FF D8 FF + if (bin2hex(substr($data, 0, 3)) === 'ffd8ff') { return self::IMAGETYPE_JPEG; } // PNG: 89 50 4E 47 From b96fd5a53dfdb7081db1635934581697f8a8c97d Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Wed, 7 Nov 2018 12:20:14 +0100 Subject: [PATCH 360/525] Also validate image file names. --- server/php/UploadHandler.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/php/UploadHandler.php b/server/php/UploadHandler.php index 459375f06..5215e4c0f 100755 --- a/server/php/UploadHandler.php +++ b/server/php/UploadHandler.php @@ -1087,6 +1087,9 @@ protected function imagetype($file_path) { } protected function is_valid_image_file($file_path) { + if (!preg_match('/\.(gif|jpe?g|png)$/i', $file_path)) { + return false; + } return !!$this->imagetype($file_path); } From 44f0f4c4d688031dc793ae0c705217ea7e54f82a Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Wed, 7 Nov 2018 12:32:50 +0100 Subject: [PATCH 361/525] 9.27.0 --- bower.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index 4569ae78c..599dfc7ec 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "9.26.0", + "version": "9.27.0", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images.", "keywords": [ diff --git a/package.json b/package.json index b76544548..ee13d126a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "blueimp-file-upload", - "version": "9.26.0", + "version": "9.27.0", "title": "jQuery File Upload", "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images, audio and video for jQuery. Supports cross-domain, chunked and resumable file uploads. Works with any server-side platform (Google App Engine, PHP, Python, Ruby on Rails, Java, etc.) that supports standard HTML form file uploads.", "keywords": [ From 23b3bb36f30d41819b43c023ed4e78284a388323 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Mon, 12 Nov 2018 18:45:02 +0100 Subject: [PATCH 362/525] Remove SRI from non-versioned resources. --- angularjs.html | 8 ++++---- basic-plus.html | 4 ++-- index.html | 10 +++++----- jquery-ui.html | 8 ++++---- test/index.html | 6 +++--- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/angularjs.html b/angularjs.html index f571bd187..e8b05e77f 100644 --- a/angularjs.html +++ b/angularjs.html @@ -26,7 +26,7 @@ - + @@ -182,13 +182,13 @@

      - + - + - + diff --git a/basic-plus.html b/basic-plus.html index e63aab41b..6d53acdc1 100644 --- a/basic-plus.html +++ b/basic-plus.html @@ -100,9 +100,9 @@

      Demo Notes

      - + - + diff --git a/index.html b/index.html index f0f396550..37e08a7cb 100644 --- a/index.html +++ b/index.html @@ -26,7 +26,7 @@ - + @@ -220,15 +220,15 @@

      - + - + - + - + diff --git a/jquery-ui.html b/jquery-ui.html index bc17318a8..e44d41e89 100644 --- a/jquery-ui.html +++ b/jquery-ui.html @@ -204,13 +204,13 @@

      - + - + - + - + diff --git a/test/index.html b/test/index.html index fe5f44a34..b8c585df5 100644 --- a/test/index.html +++ b/test/index.html @@ -147,9 +147,9 @@

      - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/basic-plus.html b/basic-plus.html index 59019a3eb..93a917b14 100644 --- a/basic-plus.html +++ b/basic-plus.html @@ -1,4 +1,4 @@ - + - - - - -Codestin Search App - - - - - - - - - - -