From a82b5b0e6208219fa3988afa58471e77f1ae24c7 Mon Sep 17 00:00:00 2001 From: Jill Regan Date: Mon, 23 Mar 2026 09:55:35 -0400 Subject: [PATCH 1/2] create new build --- configure/dist/index.js | 112 ++++++++++++++++++++++++++++++++++----- dist/index.js | 114 +++++++++++++++++++++++++++++++++++----- package-lock.json | 4 +- package.json | 2 +- 4 files changed, 204 insertions(+), 28 deletions(-) diff --git a/configure/dist/index.js b/configure/dist/index.js index 7a8723d..684a6e9 100644 --- a/configure/dist/index.js +++ b/configure/dist/index.js @@ -2942,6 +2942,24 @@ class SecureProxyConnectionError extends UndiciError { [kSecureProxyConnectionError] = true } +const kMessageSizeExceededError = Symbol.for('undici.error.UND_ERR_WS_MESSAGE_SIZE_EXCEEDED') +class MessageSizeExceededError extends UndiciError { + constructor (message) { + super(message) + this.name = 'MessageSizeExceededError' + this.message = message || 'Max decompressed message size exceeded' + this.code = 'UND_ERR_WS_MESSAGE_SIZE_EXCEEDED' + } + + static [Symbol.hasInstance] (instance) { + return instance && instance[kMessageSizeExceededError] === true + } + + get [kMessageSizeExceededError] () { + return true + } +} + module.exports = { AbortError, HTTPParserError, @@ -2965,7 +2983,8 @@ module.exports = { ResponseExceededMaxSizeError, RequestRetryError, ResponseError, - SecureProxyConnectionError + SecureProxyConnectionError, + MessageSizeExceededError } @@ -3043,6 +3062,10 @@ class Request { throw new InvalidArgumentError('upgrade must be a string') } + if (upgrade && !isValidHeaderValue(upgrade)) { + throw new InvalidArgumentError('invalid upgrade header') + } + if (headersTimeout != null && (!Number.isFinite(headersTimeout) || headersTimeout < 0)) { throw new InvalidArgumentError('invalid headersTimeout') } @@ -3337,13 +3360,19 @@ function processHeader (request, key, val) { val = `${val}` } - if (request.host === null && headerName === 'host') { + if (headerName === 'host') { + if (request.host !== null) { + throw new InvalidArgumentError('duplicate host header') + } if (typeof val !== 'string') { throw new InvalidArgumentError('invalid host header') } // Consumed by Client request.host = val - } else if (request.contentLength === null && headerName === 'content-length') { + } else if (headerName === 'content-length') { + if (request.contentLength !== null) { + throw new InvalidArgumentError('duplicate content-length header') + } request.contentLength = parseInt(val, 10) if (!Number.isFinite(request.contentLength)) { throw new InvalidArgumentError('invalid content-length header') @@ -26134,17 +26163,30 @@ module.exports = { const { createInflateRaw, Z_DEFAULT_WINDOWBITS } = __nccwpck_require__(8522) const { isValidClientWindowBits } = __nccwpck_require__(8625) +const { MessageSizeExceededError } = __nccwpck_require__(8707) const tail = Buffer.from([0x00, 0x00, 0xff, 0xff]) const kBuffer = Symbol('kBuffer') const kLength = Symbol('kLength') +// Default maximum decompressed message size: 4 MB +const kDefaultMaxDecompressedSize = 4 * 1024 * 1024 + class PerMessageDeflate { /** @type {import('node:zlib').InflateRaw} */ #inflate #options = {} + /** @type {boolean} */ + #aborted = false + + /** @type {Function|null} */ + #currentCallback = null + + /** + * @param {Map} extensions + */ constructor (extensions) { this.#options.serverNoContextTakeover = extensions.has('server_no_context_takeover') this.#options.serverMaxWindowBits = extensions.get('server_max_window_bits') @@ -26156,6 +26198,11 @@ class PerMessageDeflate { // payload of the message. // 2. Decompress the resulting data using DEFLATE. + if (this.#aborted) { + callback(new MessageSizeExceededError()) + return + } + if (!this.#inflate) { let windowBits = Z_DEFAULT_WINDOWBITS @@ -26168,13 +26215,37 @@ class PerMessageDeflate { windowBits = Number.parseInt(this.#options.serverMaxWindowBits) } - this.#inflate = createInflateRaw({ windowBits }) + try { + this.#inflate = createInflateRaw({ windowBits }) + } catch (err) { + callback(err) + return + } this.#inflate[kBuffer] = [] this.#inflate[kLength] = 0 this.#inflate.on('data', (data) => { - this.#inflate[kBuffer].push(data) + if (this.#aborted) { + return + } + this.#inflate[kLength] += data.length + + if (this.#inflate[kLength] > kDefaultMaxDecompressedSize) { + this.#aborted = true + this.#inflate.removeAllListeners() + this.#inflate.destroy() + this.#inflate = null + + if (this.#currentCallback) { + const cb = this.#currentCallback + this.#currentCallback = null + cb(new MessageSizeExceededError()) + } + return + } + + this.#inflate[kBuffer].push(data) }) this.#inflate.on('error', (err) => { @@ -26183,16 +26254,22 @@ class PerMessageDeflate { }) } + this.#currentCallback = callback this.#inflate.write(chunk) if (fin) { this.#inflate.write(tail) } this.#inflate.flush(() => { + if (this.#aborted || !this.#inflate) { + return + } + const full = Buffer.concat(this.#inflate[kBuffer], this.#inflate[kLength]) this.#inflate[kBuffer].length = 0 this.#inflate[kLength] = 0 + this.#currentCallback = null callback(null, full) }) @@ -26247,6 +26324,10 @@ class ByteParser extends Writable { /** @type {Map} */ #extensions + /** + * @param {import('./websocket').WebSocket} ws + * @param {Map|null} extensions + */ constructor (ws, extensions) { super() @@ -26389,6 +26470,7 @@ class ByteParser extends Writable { const buffer = this.consume(8) const upper = buffer.readUInt32BE(0) + const lower = buffer.readUInt32BE(4) // 2^31 is the maximum bytes an arraybuffer can contain // on 32-bit systems. Although, on 64-bit systems, this is @@ -26396,14 +26478,12 @@ class ByteParser extends Writable { // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Invalid_array_length // https://source.chromium.org/chromium/chromium/src/+/main:v8/src/common/globals.h;drc=1946212ac0100668f14eb9e2843bdd846e510a1e;bpv=1;bpt=1;l=1275 // https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/js-array-buffer.h;l=34;drc=1946212ac0100668f14eb9e2843bdd846e510a1e - if (upper > 2 ** 31 - 1) { + if (upper !== 0 || lower > 2 ** 31 - 1) { failWebsocketConnection(this.ws, 'Received payload length > 2^31 bytes.') return } - const lower = buffer.readUInt32BE(4) - - this.#info.payloadLength = (upper << 8) + lower + this.#info.payloadLength = lower this.#state = parserStates.READ_DATA } else if (this.#state === parserStates.READ_DATA) { if (this.#byteOffset < this.#info.payloadLength) { @@ -26433,7 +26513,7 @@ class ByteParser extends Writable { } else { this.#extensions.get('permessage-deflate').decompress(body, this.#info.fin, (error, data) => { if (error) { - closeWebSocketConnection(this.ws, 1007, error.message, error.message.length) + failWebsocketConnection(this.ws, error.message) return } @@ -27040,6 +27120,12 @@ function parseExtensions (extensions) { * @param {string} value */ function isValidClientWindowBits (value) { + // Must have at least one character + if (value.length === 0) { + return false + } + + // Check all characters are ASCII digits for (let i = 0; i < value.length; i++) { const byte = value.charCodeAt(i) @@ -27048,7 +27134,9 @@ function isValidClientWindowBits (value) { } } - return true + // Check numeric range: zlib requires windowBits in range 8-15 + const num = Number.parseInt(value, 10) + return num >= 8 && num <= 15 } // https://nodejs.org/api/intl.html#detecting-internationalization-support @@ -27527,7 +27615,7 @@ class WebSocket extends EventTarget { * @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol */ #onConnectionEstablished (response, parsedExtensions) { - // processResponse is called when the "response’s header list has been received and initialized." + // processResponse is called when the "response's header list has been received and initialized." // once this happens, the connection is open this[kResponse] = response diff --git a/dist/index.js b/dist/index.js index a564da3..5756c68 100644 --- a/dist/index.js +++ b/dist/index.js @@ -6261,6 +6261,24 @@ class SecureProxyConnectionError extends UndiciError { [kSecureProxyConnectionError] = true } +const kMessageSizeExceededError = Symbol.for('undici.error.UND_ERR_WS_MESSAGE_SIZE_EXCEEDED') +class MessageSizeExceededError extends UndiciError { + constructor (message) { + super(message) + this.name = 'MessageSizeExceededError' + this.message = message || 'Max decompressed message size exceeded' + this.code = 'UND_ERR_WS_MESSAGE_SIZE_EXCEEDED' + } + + static [Symbol.hasInstance] (instance) { + return instance && instance[kMessageSizeExceededError] === true + } + + get [kMessageSizeExceededError] () { + return true + } +} + module.exports = { AbortError, HTTPParserError, @@ -6284,7 +6302,8 @@ module.exports = { ResponseExceededMaxSizeError, RequestRetryError, ResponseError, - SecureProxyConnectionError + SecureProxyConnectionError, + MessageSizeExceededError } @@ -6362,6 +6381,10 @@ class Request { throw new InvalidArgumentError('upgrade must be a string') } + if (upgrade && !isValidHeaderValue(upgrade)) { + throw new InvalidArgumentError('invalid upgrade header') + } + if (headersTimeout != null && (!Number.isFinite(headersTimeout) || headersTimeout < 0)) { throw new InvalidArgumentError('invalid headersTimeout') } @@ -6656,13 +6679,19 @@ function processHeader (request, key, val) { val = `${val}` } - if (request.host === null && headerName === 'host') { + if (headerName === 'host') { + if (request.host !== null) { + throw new InvalidArgumentError('duplicate host header') + } if (typeof val !== 'string') { throw new InvalidArgumentError('invalid host header') } // Consumed by Client request.host = val - } else if (request.contentLength === null && headerName === 'content-length') { + } else if (headerName === 'content-length') { + if (request.contentLength !== null) { + throw new InvalidArgumentError('duplicate content-length header') + } request.contentLength = parseInt(val, 10) if (!Number.isFinite(request.contentLength)) { throw new InvalidArgumentError('invalid content-length header') @@ -29453,17 +29482,30 @@ module.exports = { const { createInflateRaw, Z_DEFAULT_WINDOWBITS } = __nccwpck_require__(8522) const { isValidClientWindowBits } = __nccwpck_require__(8625) +const { MessageSizeExceededError } = __nccwpck_require__(8707) const tail = Buffer.from([0x00, 0x00, 0xff, 0xff]) const kBuffer = Symbol('kBuffer') const kLength = Symbol('kLength') +// Default maximum decompressed message size: 4 MB +const kDefaultMaxDecompressedSize = 4 * 1024 * 1024 + class PerMessageDeflate { /** @type {import('node:zlib').InflateRaw} */ #inflate #options = {} + /** @type {boolean} */ + #aborted = false + + /** @type {Function|null} */ + #currentCallback = null + + /** + * @param {Map} extensions + */ constructor (extensions) { this.#options.serverNoContextTakeover = extensions.has('server_no_context_takeover') this.#options.serverMaxWindowBits = extensions.get('server_max_window_bits') @@ -29475,6 +29517,11 @@ class PerMessageDeflate { // payload of the message. // 2. Decompress the resulting data using DEFLATE. + if (this.#aborted) { + callback(new MessageSizeExceededError()) + return + } + if (!this.#inflate) { let windowBits = Z_DEFAULT_WINDOWBITS @@ -29487,13 +29534,37 @@ class PerMessageDeflate { windowBits = Number.parseInt(this.#options.serverMaxWindowBits) } - this.#inflate = createInflateRaw({ windowBits }) + try { + this.#inflate = createInflateRaw({ windowBits }) + } catch (err) { + callback(err) + return + } this.#inflate[kBuffer] = [] this.#inflate[kLength] = 0 this.#inflate.on('data', (data) => { - this.#inflate[kBuffer].push(data) + if (this.#aborted) { + return + } + this.#inflate[kLength] += data.length + + if (this.#inflate[kLength] > kDefaultMaxDecompressedSize) { + this.#aborted = true + this.#inflate.removeAllListeners() + this.#inflate.destroy() + this.#inflate = null + + if (this.#currentCallback) { + const cb = this.#currentCallback + this.#currentCallback = null + cb(new MessageSizeExceededError()) + } + return + } + + this.#inflate[kBuffer].push(data) }) this.#inflate.on('error', (err) => { @@ -29502,16 +29573,22 @@ class PerMessageDeflate { }) } + this.#currentCallback = callback this.#inflate.write(chunk) if (fin) { this.#inflate.write(tail) } this.#inflate.flush(() => { + if (this.#aborted || !this.#inflate) { + return + } + const full = Buffer.concat(this.#inflate[kBuffer], this.#inflate[kLength]) this.#inflate[kBuffer].length = 0 this.#inflate[kLength] = 0 + this.#currentCallback = null callback(null, full) }) @@ -29566,6 +29643,10 @@ class ByteParser extends Writable { /** @type {Map} */ #extensions + /** + * @param {import('./websocket').WebSocket} ws + * @param {Map|null} extensions + */ constructor (ws, extensions) { super() @@ -29708,6 +29789,7 @@ class ByteParser extends Writable { const buffer = this.consume(8) const upper = buffer.readUInt32BE(0) + const lower = buffer.readUInt32BE(4) // 2^31 is the maximum bytes an arraybuffer can contain // on 32-bit systems. Although, on 64-bit systems, this is @@ -29715,14 +29797,12 @@ class ByteParser extends Writable { // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Invalid_array_length // https://source.chromium.org/chromium/chromium/src/+/main:v8/src/common/globals.h;drc=1946212ac0100668f14eb9e2843bdd846e510a1e;bpv=1;bpt=1;l=1275 // https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/js-array-buffer.h;l=34;drc=1946212ac0100668f14eb9e2843bdd846e510a1e - if (upper > 2 ** 31 - 1) { + if (upper !== 0 || lower > 2 ** 31 - 1) { failWebsocketConnection(this.ws, 'Received payload length > 2^31 bytes.') return } - const lower = buffer.readUInt32BE(4) - - this.#info.payloadLength = (upper << 8) + lower + this.#info.payloadLength = lower this.#state = parserStates.READ_DATA } else if (this.#state === parserStates.READ_DATA) { if (this.#byteOffset < this.#info.payloadLength) { @@ -29752,7 +29832,7 @@ class ByteParser extends Writable { } else { this.#extensions.get('permessage-deflate').decompress(body, this.#info.fin, (error, data) => { if (error) { - closeWebSocketConnection(this.ws, 1007, error.message, error.message.length) + failWebsocketConnection(this.ws, error.message) return } @@ -30359,6 +30439,12 @@ function parseExtensions (extensions) { * @param {string} value */ function isValidClientWindowBits (value) { + // Must have at least one character + if (value.length === 0) { + return false + } + + // Check all characters are ASCII digits for (let i = 0; i < value.length; i++) { const byte = value.charCodeAt(i) @@ -30367,7 +30453,9 @@ function isValidClientWindowBits (value) { } } - return true + // Check numeric range: zlib requires windowBits in range 8-15 + const num = Number.parseInt(value, 10) + return num >= 8 && num <= 15 } // https://nodejs.org/api/intl.html#detecting-internationalization-support @@ -30846,7 +30934,7 @@ class WebSocket extends EventTarget { * @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol */ #onConnectionEstablished (response, parsedExtensions) { - // processResponse is called when the "response’s header list has been received and initialized." + // processResponse is called when the "response's header list has been received and initialized." // once this happens, the connection is open this[kResponse] = response @@ -35385,7 +35473,7 @@ const installCliOnGithubActionRunner = async (version) => { ;// CONCATENATED MODULE: ./package.json -const package_namespaceObject = {"rE":"3.2.1"}; +const package_namespaceObject = {"rE":"4.0.0"}; ;// CONCATENATED MODULE: ./src/constants.ts const envConnectHost = "OP_CONNECT_HOST"; const envConnectToken = "OP_CONNECT_TOKEN"; diff --git a/package-lock.json b/package-lock.json index a781086..8f2791e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "load-secrets-action", - "version": "3.2.1", + "version": "4.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "load-secrets-action", - "version": "3.2.1", + "version": "4.0.0", "license": "MIT", "dependencies": { "@1password/op-js": "^0.1.11", diff --git a/package.json b/package.json index 6ac691c..7d1885b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "load-secrets-action", - "version": "3.2.1", + "version": "4.0.0", "description": "Load Secrets from 1Password", "main": "dist/index.js", "directories": { From 21f21cb3122f5fe7bfdfd67df466dcdb3779307e Mon Sep 17 00:00:00 2001 From: Jill Regan Date: Mon, 23 Mar 2026 15:09:56 -0400 Subject: [PATCH 2/2] Update readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6a0ac5f..a861896 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ jobs: - name: Load secret id: load_secrets - uses: 1password/load-secrets-action@v3 + uses: 1password/load-secrets-action@v4 env: OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} SECRET: op://app-cicd/hello-world/secret @@ -57,7 +57,7 @@ jobs: - uses: actions/checkout@v4 - name: Load secret - uses: 1password/load-secrets-action@v3 + uses: 1password/load-secrets-action@v4 with: # Export loaded secrets as environment variables export-env: true @@ -77,7 +77,7 @@ When loading SSH keys, you can specify the format using the `ssh-format` query p ```yml - name: Load SSH key - uses: 1password/load-secrets-action@v3 + uses: 1password/load-secrets-action@v4 env: OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} # Load SSH private key in OpenSSH format