From 1b91d88d2f8233f3477a5f4579aa5f8057b2ee8b Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Thu, 7 Mar 2024 11:02:01 +0100 Subject: [PATCH] fix: add a maxOutputLength option to zlib inflate --- .../util_errors.JWEDecompressionFailed.md | 82 +++++++++++++++++++ docs/modules/util_errors.md | 1 + src/runtime/node/zlib.ts | 3 +- test/jwe/flattened.decrypt.test.mjs | 36 ++++++++ 4 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 docs/classes/util_errors.JWEDecompressionFailed.md diff --git a/docs/classes/util_errors.JWEDecompressionFailed.md b/docs/classes/util_errors.JWEDecompressionFailed.md new file mode 100644 index 0000000000..d94b781ef6 --- /dev/null +++ b/docs/classes/util_errors.JWEDecompressionFailed.md @@ -0,0 +1,82 @@ +# Class: JWEDecompressionFailed + +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- + +An error subclass thrown when a JWE ciphertext decompression fails. + +**`Example`** + +Checking thrown error is this one using a stable error code + +```js +if (err.code === 'ERR_JWE_DECOMPRESSION_FAILED') { + // ... +} +``` + +**`Example`** + +Checking thrown error is this one using `instanceof` + +```js +if (err instanceof jose.errors.JWEDecompressionFailed) { + // ... +} +``` + +## Table of contents + +### Constructors + +- [constructor](util_errors.JWEDecompressionFailed.md#constructor) + +### Properties + +- [code](util_errors.JWEDecompressionFailed.md#code) +- [message](util_errors.JWEDecompressionFailed.md#message) + +### Accessors + +- [code](util_errors.JWEDecompressionFailed.md#code-1) + +## Constructors + +### constructor + +• **new JWEDecompressionFailed**(`message?`) + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `message?` | `string` | + +## Properties + +### code + +• **code**: `string` = `'ERR_JWE_DECOMPRESSION_FAILED'` + +A unique error code for the particular error subclass. + +___ + +### message + +• **message**: `string` = `'decompression operation failed'` + +## Accessors + +### code + +• `Static` `get` **code**(): ``"ERR_JWE_DECOMPRESSION_FAILED"`` + +A unique error code for the particular error subclass. + +#### Returns + +``"ERR_JWE_DECOMPRESSION_FAILED"`` diff --git a/docs/modules/util_errors.md b/docs/modules/util_errors.md index 07bba6fda3..5398334497 100644 --- a/docs/modules/util_errors.md +++ b/docs/modules/util_errors.md @@ -13,6 +13,7 @@ Support from the community to continue maintaining and improving this module is - [JOSEAlgNotAllowed](../classes/util_errors.JOSEAlgNotAllowed.md) - [JOSEError](../classes/util_errors.JOSEError.md) - [JOSENotSupported](../classes/util_errors.JOSENotSupported.md) +- [JWEDecompressionFailed](../classes/util_errors.JWEDecompressionFailed.md) - [JWEDecryptionFailed](../classes/util_errors.JWEDecryptionFailed.md) - [JWEInvalid](../classes/util_errors.JWEInvalid.md) - [JWKInvalid](../classes/util_errors.JWKInvalid.md) diff --git a/src/runtime/node/zlib.ts b/src/runtime/node/zlib.ts index 30801a95bd..440a487028 100644 --- a/src/runtime/node/zlib.ts +++ b/src/runtime/node/zlib.ts @@ -6,5 +6,6 @@ import type { InflateFunction, DeflateFunction } from '../../types.d' const inflateRaw = promisify(inflateRawCb) const deflateRaw = promisify(deflateRawCb) -export const inflate: InflateFunction = (input: Uint8Array) => inflateRaw(input) +export const inflate: InflateFunction = (input: Uint8Array) => + inflateRaw(input, { maxOutputLength: 250_000 }) export const deflate: DeflateFunction = (input: Uint8Array) => deflateRaw(input) diff --git a/test/jwe/flattened.decrypt.test.mjs b/test/jwe/flattened.decrypt.test.mjs index 8b0fb2dcbe..c379fb09f4 100644 --- a/test/jwe/flattened.decrypt.test.mjs +++ b/test/jwe/flattened.decrypt.test.mjs @@ -1,5 +1,7 @@ import test from 'ava' import * as crypto from 'crypto' +import { promisify } from 'node:util' +import { inflateRaw as inflateRawCb } from 'node:zlib' const { FlattenedEncrypt, flattenedDecrypt } = await import('#dist') @@ -228,3 +230,37 @@ test('decrypt PBES2 p2c limit', async (t) => { code: 'ERR_JWE_INVALID', }) }) + +test('decrypt inflate output length limit', async (t) => { + { + const jwe = await new FlattenedEncrypt(new Uint8Array(250000)) + .setProtectedHeader({ alg: 'dir', enc: 'A128CBC-HS256', zip: 'DEF' }) + .encrypt(new Uint8Array(32)) + + await flattenedDecrypt(jwe, new Uint8Array(32)) + } + + { + const jwe = await new FlattenedEncrypt(new Uint8Array(250000 + 1)) + .setProtectedHeader({ alg: 'dir', enc: 'A128CBC-HS256', zip: 'DEF' }) + .encrypt(new Uint8Array(32)) + + await t.throwsAsync(flattenedDecrypt(jwe, new Uint8Array(32)), { + message: 'decompression operation failed', + code: 'ERR_JWE_DECOMPRESSION_FAILED', + }) + } + + { + const jwe = await new FlattenedEncrypt(new Uint8Array(1000 + 1)) + .setProtectedHeader({ alg: 'dir', enc: 'A128CBC-HS256', zip: 'DEF' }) + .encrypt(new Uint8Array(32)) + + const inflateRawPromise = promisify(inflateRawCb) + const inflateRaw = async (buffer) => inflateRawPromise(buffer, { maxOutputLength: 1000 }) + + await t.throwsAsync(flattenedDecrypt(jwe, new Uint8Array(32), { inflateRaw }), { + code: 'ERR_BUFFER_TOO_LARGE', + }) + } +})