From 8c2d24ee400bc4567335e97ee6004c3baa6ef66f Mon Sep 17 00:00:00 2001 From: Alexander Akait <4567934+alexander-akait@users.noreply.github.com> Date: Thu, 4 Nov 2021 18:05:31 +0300 Subject: [PATCH 1/4] fix: base64 generation and unicode characters (#197) --- lib/getHashDigest.js | 10 ++++-- lib/hash/BatchedHash.js | 64 ++++++++++++++++++++++++++++++++++++ lib/hash/wasm-hash.js | 2 +- test/getHashDigest.test.js | 4 +-- test/interpolateName.test.js | 10 +++--- 5 files changed, 79 insertions(+), 11 deletions(-) create mode 100644 lib/hash/BatchedHash.js diff --git a/lib/getHashDigest.js b/lib/getHashDigest.js index 820ae1d..7098f06 100644 --- a/lib/getHashDigest.js +++ b/lib/getHashDigest.js @@ -40,6 +40,7 @@ function encodeBufferToBase(buffer, base) { } let createMd4 = undefined; +let BatchedHash = undefined; function getHashDigest(buffer, hashType, digestType, maxLength) { hashType = hashType || 'md4'; @@ -53,9 +54,13 @@ function getHashDigest(buffer, hashType, digestType, maxLength) { if (error.code === 'ERR_OSSL_EVP_UNSUPPORTED' && hashType === 'md4') { if (createMd4 === undefined) { createMd4 = require('./hash/md4'); + + if (BatchedHash === undefined) { + BatchedHash = require('./hash/BatchedHash'); + } } - hash = createMd4(); + hash = new BatchedHash(createMd4()); } if (!hash) { @@ -72,8 +77,7 @@ function getHashDigest(buffer, hashType, digestType, maxLength) { digestType === 'base49' || digestType === 'base52' || digestType === 'base58' || - digestType === 'base62' || - digestType === 'base64' + digestType === 'base62' ) { return encodeBufferToBase(hash.digest(), digestType.substr(4)).substr( 0, diff --git a/lib/hash/BatchedHash.js b/lib/hash/BatchedHash.js new file mode 100644 index 0000000..6ec6a44 --- /dev/null +++ b/lib/hash/BatchedHash.js @@ -0,0 +1,64 @@ +const MAX_SHORT_STRING = require('./wasm-hash').MAX_SHORT_STRING; + +class BatchedHash { + constructor(hash) { + this.string = undefined; + this.encoding = undefined; + this.hash = hash; + } + + /** + * Update hash {@link https://nodejs.org/api/crypto.html#crypto_hash_update_data_inputencoding} + * @param {string|Buffer} data data + * @param {string=} inputEncoding data encoding + * @returns {this} updated hash + */ + update(data, inputEncoding) { + if (this.string !== undefined) { + if ( + typeof data === 'string' && + inputEncoding === this.encoding && + this.string.length + data.length < MAX_SHORT_STRING + ) { + this.string += data; + + return this; + } + + this.hash.update(this.string, this.encoding); + this.string = undefined; + } + + if (typeof data === 'string') { + if ( + data.length < MAX_SHORT_STRING && + // base64 encoding is not valid since it may contain padding chars + (!inputEncoding || !inputEncoding.startsWith('ba')) + ) { + this.string = data; + this.encoding = inputEncoding; + } else { + this.hash.update(data, inputEncoding); + } + } else { + this.hash.update(data); + } + + return this; + } + + /** + * Calculates the digest {@link https://nodejs.org/api/crypto.html#crypto_hash_digest_encoding} + * @param {string=} encoding encoding of the return value + * @returns {string|Buffer} digest + */ + digest(encoding) { + if (this.string !== undefined) { + this.hash.update(this.string, this.encoding); + } + + return this.hash.digest(encoding); + } +} + +module.exports = BatchedHash; diff --git a/lib/hash/wasm-hash.js b/lib/hash/wasm-hash.js index c2c2bd6..d8f2817 100644 --- a/lib/hash/wasm-hash.js +++ b/lib/hash/wasm-hash.js @@ -82,7 +82,7 @@ class WasmHash { endPos += 2; } else { // bail-out for weird chars - endPos += mem.write(data.slice(endPos), endPos, encoding); + endPos += mem.write(data.slice(i), endPos, encoding); break; } } diff --git a/test/getHashDigest.test.js b/test/getHashDigest.test.js index 28f2fc3..9fa1355 100644 --- a/test/getHashDigest.test.js +++ b/test/getHashDigest.test.js @@ -12,7 +12,7 @@ describe('getHashDigest()', () => { '6f8db599de986fab7a21625b7916589c', ], ['test string', 'md5', 'hex', 4, '6f8d'], - ['test string', 'md5', 'base64', undefined, '2sm1pVmS8xuGJLCdWpJoRL'], + ['test string', 'md5', 'base64', undefined, 'b421md6Yb6t6IWJbeRZYnA=='], ['test string', 'md5', 'base52', undefined, 'dJnldHSAutqUacjgfBQGLQx'], ['test string', 'md5', 'base26', 6, 'bhtsgu'], [ @@ -20,7 +20,7 @@ describe('getHashDigest()', () => { 'sha512', 'base64', undefined, - '2IS-kbfIPnVflXb9CzgoNESGCkvkb0urMmucPD9z8q6HuYz8RShY1-tzSUpm5-Ivx_u4H1MEzPgAhyhaZ7RKog', + 'EObWR69EYkRC84jCwUp4f/ixfmFluD12fsBHdo2MvLcaGjIm58x4Frx5wEJ9lKnaaIxBo5kse/Xk18w+C+XbrA==', ], [ 'test string', diff --git a/test/interpolateName.test.js b/test/interpolateName.test.js index c56e298..4ed7c5b 100644 --- a/test/interpolateName.test.js +++ b/test/interpolateName.test.js @@ -62,13 +62,13 @@ describe('interpolateName()', () => { '/app/img/image.png', '[sha512:hash:base64:7].[ext]', 'test content', - '2BKDTjl.png', + 'DL9MrvO.png', ], [ '/app/img/image.png', '[sha512:contenthash:base64:7].[ext]', 'test content', - '2BKDTjl.png', + 'DL9MrvO.png', ], [ '/app/dir/file.png', @@ -116,13 +116,13 @@ describe('interpolateName()', () => { '/lib/components/modal/modal.css', '[name].[md5:hash:base64:20].[ext]', 'test content', - 'modal.1n8osQznuT8jOAwdzg_n.css', + 'modal.lHP90NiApDwht3eNNIch.css', ], [ '/lib/components/modal/modal.css', '[name].[md5:contenthash:base64:20].[ext]', 'test content', - 'modal.1n8osQznuT8jOAwdzg_n.css', + 'modal.lHP90NiApDwht3eNNIch.css', ], // Should not interpret without `hash` or `contenthash` [ @@ -265,7 +265,7 @@ describe('interpolateName()', () => { ], [ [{}, '[hash:base64]', { content: 'test string' }], - '2LIG3oc1uBNmwOoL7kXgoK', + 'Lgbt1PFiMmjFpRcw2KCyrw==', 'should interpolate [hash] token with options', ], [ From 90c7c4be17e3e0b2f6091a69c67db7a6df9fd044 Mon Sep 17 00:00:00 2001 From: evilebottnawi Date: Thu, 4 Nov 2021 18:06:58 +0300 Subject: [PATCH 2/4] chore(release): 2.0.2 --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14b41a4..60f15e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [2.0.2](https://github.com/webpack/loader-utils/compare/v2.0.1...v2.0.2) (2021-11-04) + + +### Bug Fixes + +* base64 generation and unicode characters ([#197](https://github.com/webpack/loader-utils/issues/197)) ([8c2d24e](https://github.com/webpack/loader-utils/commit/8c2d24ee400bc4567335e97ee6004c3baa6ef66f)) + ### [2.0.1](https://github.com/webpack/loader-utils/compare/v2.0.0...v2.0.1) (2021-10-29) diff --git a/package.json b/package.json index 7648bee..7c357c9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "loader-utils", - "version": "2.0.1", + "version": "2.0.2", "author": "Tobias Koppers @sokra", "description": "utils for webpack loaders", "dependencies": { From a93cf6f4702012030f6b5ee8340d5c95ec1c7d4c Mon Sep 17 00:00:00 2001 From: Mike Cebrian Date: Thu, 20 Oct 2022 15:54:33 -0400 Subject: [PATCH 3/4] fix(security): prototype polution exploit (#217) --- lib/parseQuery.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/parseQuery.js b/lib/parseQuery.js index fdca007..4a201a2 100644 --- a/lib/parseQuery.js +++ b/lib/parseQuery.js @@ -26,7 +26,7 @@ function parseQuery(query) { } const queryArgs = query.split(/[,&]/g); - const result = {}; + const result = Object.create(null); queryArgs.forEach((arg) => { const idx = arg.indexOf('='); From 7162619fb982c394ed75098a0a0ed7e7f3177c70 Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Thu, 20 Oct 2022 22:58:39 +0300 Subject: [PATCH 4/4] chore(release): 2.0.3 --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60f15e7..ae27c68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [2.0.3](https://github.com/webpack/loader-utils/compare/v2.0.1...v2.0.3) (2022-10-20) + + +### Bug Fixes + +* **security:** prototype pollution exploit ([#217](https://github.com/webpack/loader-utils/issues/217)) ([a93cf6f](https://github.com/webpack/loader-utils/commit/a93cf6f4702012030f6b5ee8340d5c95ec1c7d4c)) + ### [2.0.2](https://github.com/webpack/loader-utils/compare/v2.0.1...v2.0.2) (2021-11-04) diff --git a/package.json b/package.json index 7c357c9..7c597b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "loader-utils", - "version": "2.0.2", + "version": "2.0.3", "author": "Tobias Koppers @sokra", "description": "utils for webpack loaders", "dependencies": {