diff --git a/.travis.yml b/.travis.yml index 2c3ffe8..67b6dcc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,3 +5,8 @@ node_js: - "0.12" - "4" - "6" + - "8" + - "10" + - "11" +install: yarn install || npm install +script: yarn test || npm test diff --git a/lib/decoder.js b/lib/decoder.js index 015c311..de557ae 100644 --- a/lib/decoder.js +++ b/lib/decoder.js @@ -539,6 +539,7 @@ var JpegImage = (function jpegImage() { xhr.send(null); }, parse: function parse(data) { + var useConservativeMemoryLimits = this.useConservativeMemoryLimits; var offset = 0, length = data.length; function readUint16() { var value = (data[offset] << 8) | data[offset + 1]; @@ -570,6 +571,11 @@ var JpegImage = (function jpegImage() { var blocksPerColumn = Math.ceil(Math.ceil(frame.scanLines / 8) * component.v / maxV); var blocksPerLineForMcu = mcusPerLine * component.h; var blocksPerColumnForMcu = mcusPerColumn * component.v; + + if (useConservativeMemoryLimits && blocksPerColumnForMcu * blocksPerLineForMcu > 1e6) { + throw new Error('Refusing to allocate more than 1 million blocks'); + } + var blocks = []; for (var i = 0; i < blocksPerColumnForMcu; i++) { var row = []; @@ -979,12 +985,15 @@ module.exports = decode; function decode(jpegData, opts) { var defaultOpts = { + useConservativeMemoryLimits: false, useTArray: false, colorTransform: true }; if (opts) { if (typeof opts === 'object') { opts = { + useConservativeMemoryLimits: (typeof opts.useConservativeMemoryLimits === 'undefined' ? + defaultOpts.useConservativeMemoryLimits : opts.useConservativeMemoryLimits), useTArray: (typeof opts.useTArray === 'undefined' ? defaultOpts.useTArray : opts.useTArray), colorTransform: (typeof opts.colorTransform === 'undefined' ? @@ -1001,6 +1010,7 @@ function decode(jpegData, opts) { var arr = new Uint8Array(jpegData); var decoder = new JpegImage(); + decoder.useConservativeMemoryLimits = opts.useConservativeMemoryLimits; decoder.parse(arr); decoder.colorTransform = opts.colorTransform; diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index f444fcb..0000000 --- a/package-lock.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "name": "jpeg-js", - "version": "0.3.6", - "lockfileVersion": 1, - "dependencies": { - "deep-equal": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.1.2.tgz", - "integrity": "sha1-skbCuApXCkfBG+HZvRBw7IeLh84=", - "dev": true - }, - "defined": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-0.0.0.tgz", - "integrity": "sha1-817qfXBekzuvE7LwOz+D2SFAOz4=", - "dev": true - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true - }, - "redtape": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/redtape/-/redtape-0.1.0.tgz", - "integrity": "sha1-3YsDYH4QiW6eZiTJXDYJxlUExhw=", - "dev": true - }, - "resumer": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", - "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", - "dev": true - }, - "tape": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tape/-/tape-2.3.3.tgz", - "integrity": "sha1-Lnzgox3wn41oUWZKcYQuDKUFevc=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - } - } -} diff --git a/package.json b/package.json index c0ea23c..adcb8b2 100644 --- a/package.json +++ b/package.json @@ -29,5 +29,8 @@ "devDependencies": { "redtape": "~0.1.0", "tape": "~2.3.2" + }, + "resolutions": { + "defined": "1.0.0" } } diff --git a/test/index.js b/test/index.js index 85dd6c8..aaacdb5 100644 --- a/test/index.js +++ b/test/index.js @@ -213,3 +213,19 @@ it('should be able to decode a JPEG with options', function(t) { t.assert(rawImageData.data instanceof Uint8Array, 'data is a typed array'); t.end(); }); + +// See https://github.com/eugeneware/jpeg-js/issues/53 +it('should not infinite loop', function(t) { + try { + jpeg.decode( + Buffer.from( + 'ffd8ffc09dfdb0ffff0e5296bd7fbbc4f9579096bd7fbbfc0e80d50000ffff36fa400100236701bf73ffaf8003a57f097f5e000000008023c4f9579096bd7fbb008000001500b34e8c018fda5212', + 'hex' + ), {useConservativeMemoryLimits: true} + ); + } catch (err) { + t.assert(err, 'threw an error on invalid input'); + } + + t.end(); +}) diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..531ea78 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,52 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +deep-equal@~0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-0.1.2.tgz#b246c2b80a570a47c11be1d9bd1070ec878b87ce" + integrity sha1-skbCuApXCkfBG+HZvRBw7IeLh84= + +defined@1.0.0, defined@~0.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" + integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= + +inherits@~2.0.1: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= + +redtape@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/redtape/-/redtape-0.1.0.tgz#dd8b03607e10896e9e6624c95c3609c65504c61c" + integrity sha1-3YsDYH4QiW6eZiTJXDYJxlUExhw= + +resumer@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/resumer/-/resumer-0.0.0.tgz#f1e8f461e4064ba39e82af3cdc2a8c893d076759" + integrity sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k= + dependencies: + through "~2.3.4" + +tape@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/tape/-/tape-2.3.3.tgz#2e7ce0a31df09f8d6851664a71842e0ca5057af7" + integrity sha1-Lnzgox3wn41oUWZKcYQuDKUFevc= + dependencies: + deep-equal "~0.1.0" + defined "~0.0.0" + inherits "~2.0.1" + jsonify "~0.0.0" + resumer "~0.0.0" + through "~2.3.4" + +through@~2.3.4: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=