diff --git a/CHANGELOG.md b/CHANGELOG.md index 14b41a4..ae27c68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,20 @@ 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) + + +### 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/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/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('='); diff --git a/package.json b/package.json index 7648bee..7c597b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "loader-utils", - "version": "2.0.1", + "version": "2.0.3", "author": "Tobias Koppers @sokra", "description": "utils for webpack loaders", "dependencies": { 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', ], [ pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy