aboutsummaryrefslogtreecommitdiffstats
path: root/meta-oe/recipes-devtools/nodejs/nodejs
diff options
context:
space:
mode:
Diffstat (limited to 'meta-oe/recipes-devtools/nodejs/nodejs')
-rw-r--r--meta-oe/recipes-devtools/nodejs/nodejs/CVE-2022-25883.patch262
-rw-r--r--meta-oe/recipes-devtools/nodejs/nodejs/CVE-2023-46809.patch625
-rw-r--r--meta-oe/recipes-devtools/nodejs/nodejs/CVE-2024-22019.patch556
-rw-r--r--meta-oe/recipes-devtools/nodejs/nodejs/CVE-2024-22025.patch148
4 files changed, 1591 insertions, 0 deletions
diff --git a/meta-oe/recipes-devtools/nodejs/nodejs/CVE-2022-25883.patch b/meta-oe/recipes-devtools/nodejs/nodejs/CVE-2022-25883.patch
new file mode 100644
index 0000000000..4c73b556f9
--- /dev/null
+++ b/meta-oe/recipes-devtools/nodejs/nodejs/CVE-2022-25883.patch
@@ -0,0 +1,262 @@
+From 717534ee353682f3bcf33e60a8af4292626d4441 Mon Sep 17 00:00:00 2001
+From: Luke Karrys <luke@lukekarrys.com>
+Date: Thu, 15 Jun 2023 12:21:14 -0700
+Subject: [PATCH] fix: better handling of whitespace (#564)
+
+CVE: CVE-2022-25883
+
+Upstream-Status: Backport [https://github.com/npm/node-semver/commit/717534ee353682f3bcf33e60a8af4292626d4441]
+
+Signed-off-by: Archana Polampalli <archana.polampalli@windriver.com>
+---
+ .../node_modules/semver/classes/comparator.js | 3 +-
+ deps/npm/node_modules/semver/classes/range.js | 64 +++++++++++--------
+ .../npm/node_modules/semver/classes/semver.js | 2 +-
+ .../node_modules/semver/functions/coerce.js | 2 +-
+ deps/npm/node_modules/semver/internal/re.js | 11 ++++
+ deps/npm/node_modules/semver/package.json | 2 +-
+ 6 files changed, 53 insertions(+), 31 deletions(-)
+
+diff --git a/deps/npm/node_modules/semver/classes/comparator.js b/deps/npm/node_modules/semver/classes/comparator.js
+index 62cd204..c909446 100644
+--- a/deps/npm/node_modules/semver/classes/comparator.js
++++ b/deps/npm/node_modules/semver/classes/comparator.js
+@@ -16,6 +16,7 @@ class Comparator {
+ }
+ }
+
++ comp = comp.trim().split(/\s+/).join(' ')
+ debug('comparator', comp, options)
+ this.options = options
+ this.loose = !!options.loose
+@@ -129,7 +130,7 @@ class Comparator {
+ module.exports = Comparator
+
+ const parseOptions = require('../internal/parse-options')
+-const { re, t } = require('../internal/re')
++const { safeRe: re, t } = require('../internal/re')
+ const cmp = require('../functions/cmp')
+ const debug = require('../internal/debug')
+ const SemVer = require('./semver')
+diff --git a/deps/npm/node_modules/semver/classes/range.js b/deps/npm/node_modules/semver/classes/range.js
+index 7dc24bc..8e2e1f9 100644
+--- a/deps/npm/node_modules/semver/classes/range.js
++++ b/deps/npm/node_modules/semver/classes/range.js
+@@ -26,19 +26,26 @@ class Range {
+ this.loose = !!options.loose
+ this.includePrerelease = !!options.includePrerelease
+
+- // First, split based on boolean or ||
++ // First reduce all whitespace as much as possible so we do not have to rely
++ // on potentially slow regexes like \s*. This is then stored and used for
++ // future error messages as well.
+ this.raw = range
+- this.set = range
++ .trim()
++ .split(/\s+/)
++ .join(' ')
++
++ // First, split on ||
++ this.set = this.raw
+ .split('||')
+ // map the range to a 2d array of comparators
+- .map(r => this.parseRange(r.trim()))
++ .map(r => this.parseRange(r))
+ // throw out any comparator lists that are empty
+ // this generally means that it was not a valid range, which is allowed
+ // in loose mode, but will still throw if the WHOLE range is invalid.
+ .filter(c => c.length)
+
+ if (!this.set.length) {
+- throw new TypeError(`Invalid SemVer Range: ${range}`)
++ throw new TypeError(`Invalid SemVer Range: ${this.raw}`)
+ }
+
+ // if we have any that are not the null set, throw out null sets.
+@@ -64,9 +71,7 @@ class Range {
+
+ format () {
+ this.range = this.set
+- .map((comps) => {
+- return comps.join(' ').trim()
+- })
++ .map((comps) => comps.join(' ').trim())
+ .join('||')
+ .trim()
+ return this.range
+@@ -77,8 +82,6 @@ class Range {
+ }
+
+ parseRange (range) {
+- range = range.trim()
+-
+ // memoize range parsing for performance.
+ // this is a very hot path, and fully deterministic.
+ const memoOpts = Object.keys(this.options).join(',')
+@@ -103,9 +106,6 @@ class Range {
+ // `^ 1.2.3` => `^1.2.3`
+ range = range.replace(re[t.CARETTRIM], caretTrimReplace)
+
+- // normalize spaces
+- range = range.split(/\s+/).join(' ')
+-
+ // At this point, the range is completely trimmed and
+ // ready to be split into comparators.
+
+@@ -200,7 +200,7 @@ const Comparator = require('./comparator')
+ const debug = require('../internal/debug')
+ const SemVer = require('./semver')
+ const {
+- re,
++ safeRe: re,
+ t,
+ comparatorTrimReplace,
+ tildeTrimReplace,
+@@ -252,10 +252,13 @@ const isX = id => !id || id.toLowerCase() === 'x' || id === '*'
+ // ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0-0
+ // ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0-0
+ // ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0-0
+-const replaceTildes = (comp, options) =>
+- comp.trim().split(/\s+/).map((c) => {
+- return replaceTilde(c, options)
+- }).join(' ')
++const replaceTildes = (comp, options) => {
++ return comp
++ .trim()
++ .split(/\s+/)
++ .map((c) => replaceTilde(c, options))
++ .join(' ')
++}
+
+ const replaceTilde = (comp, options) => {
+ const r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE]
+@@ -291,10 +294,13 @@ const replaceTilde = (comp, options) => {
+ // ^1.2, ^1.2.x --> >=1.2.0 <2.0.0-0
+ // ^1.2.3 --> >=1.2.3 <2.0.0-0
+ // ^1.2.0 --> >=1.2.0 <2.0.0-0
+-const replaceCarets = (comp, options) =>
+- comp.trim().split(/\s+/).map((c) => {
+- return replaceCaret(c, options)
+- }).join(' ')
++const replaceCarets = (comp, options) => {
++ return comp
++ .trim()
++ .split(/\s+/)
++ .map((c) => replaceCaret(c, options))
++ .join(' ')
++}
+
+ const replaceCaret = (comp, options) => {
+ debug('caret', comp, options)
+@@ -351,9 +357,10 @@ const replaceCaret = (comp, options) => {
+
+ const replaceXRanges = (comp, options) => {
+ debug('replaceXRanges', comp, options)
+- return comp.split(/\s+/).map((c) => {
+- return replaceXRange(c, options)
+- }).join(' ')
++ return comp
++ .split(/\s+/)
++ .map((c) => replaceXRange(c, options))
++ .join(' ')
+ }
+
+ const replaceXRange = (comp, options) => {
+@@ -436,12 +443,15 @@ const replaceXRange = (comp, options) => {
+ const replaceStars = (comp, options) => {
+ debug('replaceStars', comp, options)
+ // Looseness is ignored here. star is always as loose as it gets!
+- return comp.trim().replace(re[t.STAR], '')
++ return comp
++ .trim()
++ .replace(re[t.STAR], '')
+ }
+
+ const replaceGTE0 = (comp, options) => {
+ debug('replaceGTE0', comp, options)
+- return comp.trim()
++ return comp
++ .trim()
+ .replace(re[options.includePrerelease ? t.GTE0PRE : t.GTE0], '')
+ }
+
+@@ -479,7 +489,7 @@ const hyphenReplace = incPr => ($0,
+ to = `<=${to}`
+ }
+
+- return (`${from} ${to}`).trim()
++ return `${from} ${to}`.trim()
+ }
+
+ const testSet = (set, version, options) => {
+diff --git a/deps/npm/node_modules/semver/classes/semver.js b/deps/npm/node_modules/semver/classes/semver.js
+index af62955..ad4e877 100644
+--- a/deps/npm/node_modules/semver/classes/semver.js
++++ b/deps/npm/node_modules/semver/classes/semver.js
+@@ -1,6 +1,6 @@
+ const debug = require('../internal/debug')
+ const { MAX_LENGTH, MAX_SAFE_INTEGER } = require('../internal/constants')
+-const { re, t } = require('../internal/re')
++const { safeRe: re, t } = require('../internal/re')
+
+ const parseOptions = require('../internal/parse-options')
+ const { compareIdentifiers } = require('../internal/identifiers')
+diff --git a/deps/npm/node_modules/semver/functions/coerce.js b/deps/npm/node_modules/semver/functions/coerce.js
+index 2e01452..febbff9 100644
+--- a/deps/npm/node_modules/semver/functions/coerce.js
++++ b/deps/npm/node_modules/semver/functions/coerce.js
+@@ -1,6 +1,6 @@
+ const SemVer = require('../classes/semver')
+ const parse = require('./parse')
+-const { re, t } = require('../internal/re')
++const { safeRe: re, t } = require('../internal/re')
+
+ const coerce = (version, options) => {
+ if (version instanceof SemVer) {
+diff --git a/deps/npm/node_modules/semver/internal/re.js b/deps/npm/node_modules/semver/internal/re.js
+index ed88398..f73ef1a 100644
+--- a/deps/npm/node_modules/semver/internal/re.js
++++ b/deps/npm/node_modules/semver/internal/re.js
+@@ -4,16 +4,27 @@ exports = module.exports = {}
+
+ // The actual regexps go on exports.re
+ const re = exports.re = []
++const safeRe = exports.safeRe = []
+ const src = exports.src = []
+ const t = exports.t = {}
+ let R = 0
+
+ const createToken = (name, value, isGlobal) => {
++ // Replace all greedy whitespace to prevent regex dos issues. These regex are
++ // used internally via the safeRe object since all inputs in this library get
++ // normalized first to trim and collapse all extra whitespace. The original
++ // regexes are exported for userland consumption and lower level usage. A
++ // future breaking change could export the safer regex only with a note that
++ // all input should have extra whitespace removed.
++ const safe = value
++ .split('\\s*').join('\\s{0,1}')
++ .split('\\s+').join('\\s')
+ const index = R++
+ debug(name, index, value)
+ t[name] = index
+ src[index] = value
+ re[index] = new RegExp(value, isGlobal ? 'g' : undefined)
++ safeRe[index] = new RegExp(safe, isGlobal ? 'g' : undefined)
+ }
+
+ // The following Regular Expressions can be used for tokenizing,
+diff --git a/deps/npm/node_modules/semver/package.json b/deps/npm/node_modules/semver/package.json
+index 7898f59..d8ae619 100644
+--- a/deps/npm/node_modules/semver/package.json
++++ b/deps/npm/node_modules/semver/package.json
+@@ -40,7 +40,7 @@
+ "range.bnf"
+ ],
+ "tap": {
+- "check-coverage": true,
++ "timeout": 30,
+ "coverage-map": "map.js"
+ },
+ "engines": {
+--
+2.40.0
diff --git a/meta-oe/recipes-devtools/nodejs/nodejs/CVE-2023-46809.patch b/meta-oe/recipes-devtools/nodejs/nodejs/CVE-2023-46809.patch
new file mode 100644
index 0000000000..991d39fcf9
--- /dev/null
+++ b/meta-oe/recipes-devtools/nodejs/nodejs/CVE-2023-46809.patch
@@ -0,0 +1,625 @@
+From d3d357ab096884f10f5d2f164149727eea875635 Mon Sep 17 00:00:00 2001
+From: Michael Dawson <midawson@redhat.com>
+Date: Thu, 4 Jan 2024 21:32:51 +0000
+Subject: [PATCH] crypto: disable PKCS#1 padding for privateDecrypt
+
+Refs: https://hackerone.com/bugs?subject=nodejs&report_id=2269177
+
+Disable RSA_PKCS1_PADDING for crypto.privateDecrypt() in order
+to protect against the Marvin attack.
+
+Includes a security revert flag that can be used to restore
+support.
+
+Signed-off-by: Michael Dawson <midawson@redhat.com>
+PR-URL: https://github.com/nodejs-private/node-private/pull/525
+Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
+Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
+
+CVE-ID: CVE-2023-46809
+
+Upstream-Status: Backport [https://github.com/nodejs/node/commit/d3d357ab096884f1]
+Signed-off-by: Archana Polampalli <archana.polampalli@windriver.com>
+---
+ src/crypto/crypto_cipher.cc | 28 ++
+ src/node_revert.h | 1 +
+ test/parallel/test-crypto-rsa-dsa-revert.js | 475 ++++++++++++++++++++
+ test/parallel/test-crypto-rsa-dsa.js | 42 +-
+ 4 files changed, 533 insertions(+), 13 deletions(-)
+ create mode 100644 test/parallel/test-crypto-rsa-dsa-revert.js
+
+diff --git a/src/crypto/crypto_cipher.cc b/src/crypto/crypto_cipher.cc
+index 10579ce..0311c68 100644
+--- a/src/crypto/crypto_cipher.cc
++++ b/src/crypto/crypto_cipher.cc
+@@ -6,6 +6,7 @@
+ #include "node_buffer.h"
+ #include "node_internals.h"
+ #include "node_process-inl.h"
++#include "node_revert.h"
+ #include "v8.h"
+
+ namespace node {
+@@ -1061,6 +1062,33 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo<Value>& args) {
+ uint32_t padding;
+ if (!args[offset + 1]->Uint32Value(env->context()).To(&padding)) return;
+
++ if (EVP_PKEY_cipher == EVP_PKEY_decrypt &&
++ operation == PublicKeyCipher::kPrivate && padding == RSA_PKCS1_PADDING &&
++ !IsReverted(SECURITY_REVERT_CVE_2023_46809)) {
++ EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(pkey.get(), nullptr));
++ CHECK(ctx);
++
++ if (EVP_PKEY_decrypt_init(ctx.get()) <= 0) {
++ return ThrowCryptoError(env, ERR_get_error());
++ }
++
++ int rsa_pkcs1_implicit_rejection =
++ EVP_PKEY_CTX_ctrl_str(ctx.get(), "rsa_pkcs1_implicit_rejection", "1");
++ // From the doc -2 means that the option is not supported.
++ // The default for the option is enabled and if it has been
++ // specifically disabled we want to respect that so we will
++ // not throw an error if the option is supported regardless
++ // of how it is set. The call to set the value
++ // will not affect what is used since a different context is
++ // used in the call if the option is supported
++ if (rsa_pkcs1_implicit_rejection <= 0) {
++ return THROW_ERR_INVALID_ARG_VALUE(
++ env,
++ "RSA_PKCS1_PADDING is no longer supported for private decryption,"
++ " this can be reverted with --security-revert=CVE-2023-46809");
++ }
++ }
++
+ const EVP_MD* digest = nullptr;
+ if (args[offset + 2]->IsString()) {
+ const Utf8Value oaep_str(env->isolate(), args[offset + 2]);
+diff --git a/src/node_revert.h b/src/node_revert.h
+index 83dcb62..bc2a288 100644
+--- a/src/node_revert.h
++++ b/src/node_revert.h
+@@ -18,6 +18,7 @@ namespace node {
+ #define SECURITY_REVERSIONS(XX) \
+ XX(CVE_2021_44531, "CVE-2021-44531", "Cert Verif Bypass via URI SAN") \
+ XX(CVE_2021_44532, "CVE-2021-44532", "Cert Verif Bypass via Str Inject") \
++ XX(CVE_2023_46809, "CVE-2023-46809", "Marvin attack on PKCS#1 padding") \
+ // XX(CVE_2016_PEND, "CVE-2016-PEND", "Vulnerability Title")
+
+ enum reversion {
+diff --git a/test/parallel/test-crypto-rsa-dsa-revert.js b/test/parallel/test-crypto-rsa-dsa-revert.js
+new file mode 100644
+index 0000000..84ec8f6
+--- /dev/null
++++ b/test/parallel/test-crypto-rsa-dsa-revert.js
+@@ -0,0 +1,475 @@
++'use strict';
++// Flags: --security-revert=CVE-2023-46809
++const common = require('../common');
++if (!common.hasCrypto)
++ common.skip('missing crypto');
++
++const assert = require('assert');
++const crypto = require('crypto');
++
++const constants = crypto.constants;
++
++const fixtures = require('../common/fixtures');
++
++// Test certificates
++const certPem = fixtures.readKey('rsa_cert.crt');
++const keyPem = fixtures.readKey('rsa_private.pem');
++const rsaKeySize = 2048;
++const rsaPubPem = fixtures.readKey('rsa_public.pem', 'ascii');
++const rsaKeyPem = fixtures.readKey('rsa_private.pem', 'ascii');
++const rsaKeyPemEncrypted = fixtures.readKey('rsa_private_encrypted.pem',
++ 'ascii');
++const dsaPubPem = fixtures.readKey('dsa_public.pem', 'ascii');
++const dsaKeyPem = fixtures.readKey('dsa_private.pem', 'ascii');
++const dsaKeyPemEncrypted = fixtures.readKey('dsa_private_encrypted.pem',
++ 'ascii');
++const rsaPkcs8KeyPem = fixtures.readKey('rsa_private_pkcs8.pem');
++const dsaPkcs8KeyPem = fixtures.readKey('dsa_private_pkcs8.pem');
++
++const ec = new TextEncoder();
++
++const openssl1DecryptError = {
++ message: 'error:06065064:digital envelope routines:EVP_DecryptFinal_ex:' +
++ 'bad decrypt',
++ code: 'ERR_OSSL_EVP_BAD_DECRYPT',
++ reason: 'bad decrypt',
++ function: 'EVP_DecryptFinal_ex',
++ library: 'digital envelope routines',
++};
++
++const decryptError = common.hasOpenSSL3 ?
++ { message: 'error:1C800064:Provider routines::bad decrypt' } :
++ openssl1DecryptError;
++
++const decryptPrivateKeyError = common.hasOpenSSL3 ? {
++ message: 'error:1C800064:Provider routines::bad decrypt',
++} : openssl1DecryptError;
++
++function getBufferCopy(buf) {
++ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
++}
++
++// Test RSA encryption/decryption
++{
++ const input = 'I AM THE WALRUS';
++ const bufferToEncrypt = Buffer.from(input);
++ const bufferPassword = Buffer.from('password');
++
++ let encryptedBuffer = crypto.publicEncrypt(rsaPubPem, bufferToEncrypt);
++
++ // Test other input types
++ let otherEncrypted;
++ {
++ const ab = getBufferCopy(ec.encode(rsaPubPem));
++ const ab2enc = getBufferCopy(bufferToEncrypt);
++
++ crypto.publicEncrypt(ab, ab2enc);
++ crypto.publicEncrypt(new Uint8Array(ab), new Uint8Array(ab2enc));
++ crypto.publicEncrypt(new DataView(ab), new DataView(ab2enc));
++ otherEncrypted = crypto.publicEncrypt({
++ key: Buffer.from(ab).toString('hex'),
++ encoding: 'hex'
++ }, Buffer.from(ab2enc).toString('hex'));
++ }
++
++ let decryptedBuffer = crypto.privateDecrypt(rsaKeyPem, encryptedBuffer);
++ const otherDecrypted = crypto.privateDecrypt(rsaKeyPem, otherEncrypted);
++ assert.strictEqual(decryptedBuffer.toString(), input);
++ assert.strictEqual(otherDecrypted.toString(), input);
++
++ decryptedBuffer = crypto.privateDecrypt(rsaPkcs8KeyPem, encryptedBuffer);
++ assert.strictEqual(decryptedBuffer.toString(), input);
++
++ let decryptedBufferWithPassword = crypto.privateDecrypt({
++ key: rsaKeyPemEncrypted,
++ passphrase: 'password'
++ }, encryptedBuffer);
++
++ const otherDecryptedBufferWithPassword = crypto.privateDecrypt({
++ key: rsaKeyPemEncrypted,
++ passphrase: ec.encode('password')
++ }, encryptedBuffer);
++
++ assert.strictEqual(
++ otherDecryptedBufferWithPassword.toString(),
++ decryptedBufferWithPassword.toString());
++
++ decryptedBufferWithPassword = crypto.privateDecrypt({
++ key: rsaKeyPemEncrypted,
++ passphrase: 'password'
++ }, encryptedBuffer);
++
++ assert.strictEqual(decryptedBufferWithPassword.toString(), input);
++
++ encryptedBuffer = crypto.publicEncrypt({
++ key: rsaKeyPemEncrypted,
++ passphrase: 'password'
++ }, bufferToEncrypt);
++
++ decryptedBufferWithPassword = crypto.privateDecrypt({
++ key: rsaKeyPemEncrypted,
++ passphrase: 'password'
++ }, encryptedBuffer);
++ assert.strictEqual(decryptedBufferWithPassword.toString(), input);
++
++ encryptedBuffer = crypto.privateEncrypt({
++ key: rsaKeyPemEncrypted,
++ passphrase: bufferPassword
++ }, bufferToEncrypt);
++
++ decryptedBufferWithPassword = crypto.publicDecrypt({
++ key: rsaKeyPemEncrypted,
++ passphrase: bufferPassword
++ }, encryptedBuffer);
++ assert.strictEqual(decryptedBufferWithPassword.toString(), input);
++
++ // Now with explicit RSA_PKCS1_PADDING.
++ encryptedBuffer = crypto.privateEncrypt({
++ padding: crypto.constants.RSA_PKCS1_PADDING,
++ key: rsaKeyPemEncrypted,
++ passphrase: bufferPassword
++ }, bufferToEncrypt);
++
++ decryptedBufferWithPassword = crypto.publicDecrypt({
++ padding: crypto.constants.RSA_PKCS1_PADDING,
++ key: rsaKeyPemEncrypted,
++ passphrase: bufferPassword
++ }, encryptedBuffer);
++ assert.strictEqual(decryptedBufferWithPassword.toString(), input);
++
++ // Omitting padding should be okay because RSA_PKCS1_PADDING is the default.
++ decryptedBufferWithPassword = crypto.publicDecrypt({
++ key: rsaKeyPemEncrypted,
++ passphrase: bufferPassword
++ }, encryptedBuffer);
++ assert.strictEqual(decryptedBufferWithPassword.toString(), input);
++
++ // Now with RSA_NO_PADDING. Plaintext needs to match key size.
++ // OpenSSL 3.x has a rsa_check_padding that will cause an error if
++ // RSA_NO_PADDING is used.
++ if (!common.hasOpenSSL3) {
++ {
++ const plaintext = 'x'.repeat(rsaKeySize / 8);
++ encryptedBuffer = crypto.privateEncrypt({
++ padding: crypto.constants.RSA_NO_PADDING,
++ key: rsaKeyPemEncrypted,
++ passphrase: bufferPassword
++ }, Buffer.from(plaintext));
++
++ decryptedBufferWithPassword = crypto.publicDecrypt({
++ padding: crypto.constants.RSA_NO_PADDING,
++ key: rsaKeyPemEncrypted,
++ passphrase: bufferPassword
++ }, encryptedBuffer);
++ assert.strictEqual(decryptedBufferWithPassword.toString(), plaintext);
++ }
++ }
++
++ encryptedBuffer = crypto.publicEncrypt(certPem, bufferToEncrypt);
++
++ decryptedBuffer = crypto.privateDecrypt(keyPem, encryptedBuffer);
++ assert.strictEqual(decryptedBuffer.toString(), input);
++
++ encryptedBuffer = crypto.publicEncrypt(keyPem, bufferToEncrypt);
++
++ decryptedBuffer = crypto.privateDecrypt(keyPem, encryptedBuffer);
++ assert.strictEqual(decryptedBuffer.toString(), input);
++
++ encryptedBuffer = crypto.privateEncrypt(keyPem, bufferToEncrypt);
++
++ decryptedBuffer = crypto.publicDecrypt(keyPem, encryptedBuffer);
++ assert.strictEqual(decryptedBuffer.toString(), input);
++
++ assert.throws(() => {
++ crypto.privateDecrypt({
++ key: rsaKeyPemEncrypted,
++ passphrase: 'wrong'
++ }, bufferToEncrypt);
++ }, decryptError);
++
++ assert.throws(() => {
++ crypto.publicEncrypt({
++ key: rsaKeyPemEncrypted,
++ passphrase: 'wrong'
++ }, encryptedBuffer);
++ }, decryptError);
++
++ encryptedBuffer = crypto.privateEncrypt({
++ key: rsaKeyPemEncrypted,
++ passphrase: Buffer.from('password')
++ }, bufferToEncrypt);
++
++ assert.throws(() => {
++ crypto.publicDecrypt({
++ key: rsaKeyPemEncrypted,
++ passphrase: Buffer.from('wrong')
++ }, encryptedBuffer);
++ }, decryptError);
++}
++
++function test_rsa(padding, encryptOaepHash, decryptOaepHash) {
++ const size = (padding === 'RSA_NO_PADDING') ? rsaKeySize / 8 : 32;
++ const input = Buffer.allocUnsafe(size);
++ for (let i = 0; i < input.length; i++)
++ input[i] = (i * 7 + 11) & 0xff;
++ const bufferToEncrypt = Buffer.from(input);
++
++ padding = constants[padding];
++
++ const encryptedBuffer = crypto.publicEncrypt({
++ key: rsaPubPem,
++ padding: padding,
++ oaepHash: encryptOaepHash
++ }, bufferToEncrypt);
++
++ let decryptedBuffer = crypto.privateDecrypt({
++ key: rsaKeyPem,
++ padding: padding,
++ oaepHash: decryptOaepHash
++ }, encryptedBuffer);
++ assert.deepStrictEqual(decryptedBuffer, input);
++
++ decryptedBuffer = crypto.privateDecrypt({
++ key: rsaPkcs8KeyPem,
++ padding: padding,
++ oaepHash: decryptOaepHash
++ }, encryptedBuffer);
++ assert.deepStrictEqual(decryptedBuffer, input);
++}
++
++test_rsa('RSA_NO_PADDING');
++test_rsa('RSA_PKCS1_PADDING');
++test_rsa('RSA_PKCS1_OAEP_PADDING');
++
++// Test OAEP with different hash functions.
++test_rsa('RSA_PKCS1_OAEP_PADDING', undefined, 'sha1');
++test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha1', undefined);
++test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha256', 'sha256');
++test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha512', 'sha512');
++assert.throws(() => {
++ test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha256', 'sha512');
++}, {
++ code: 'ERR_OSSL_RSA_OAEP_DECODING_ERROR'
++});
++
++// The following RSA-OAEP test cases were created using the WebCrypto API to
++// ensure compatibility when using non-SHA1 hash functions.
++{
++ const { decryptionTests } =
++ JSON.parse(fixtures.readSync('rsa-oaep-test-vectors.js', 'utf8'));
++
++ for (const { ct, oaepHash, oaepLabel } of decryptionTests) {
++ const label = oaepLabel ? Buffer.from(oaepLabel, 'hex') : undefined;
++ const copiedLabel = oaepLabel ? getBufferCopy(label) : undefined;
++
++ const decrypted = crypto.privateDecrypt({
++ key: rsaPkcs8KeyPem,
++ oaepHash,
++ oaepLabel: oaepLabel ? label : undefined
++ }, Buffer.from(ct, 'hex'));
++
++ assert.strictEqual(decrypted.toString('utf8'), 'Hello Node.js');
++
++ const otherDecrypted = crypto.privateDecrypt({
++ key: rsaPkcs8KeyPem,
++ oaepHash,
++ oaepLabel: copiedLabel
++ }, Buffer.from(ct, 'hex'));
++
++ assert.strictEqual(otherDecrypted.toString('utf8'), 'Hello Node.js');
++ }
++}
++
++// Test invalid oaepHash and oaepLabel options.
++for (const fn of [crypto.publicEncrypt, crypto.privateDecrypt]) {
++ assert.throws(() => {
++ fn({
++ key: rsaPubPem,
++ oaepHash: 'Hello world'
++ }, Buffer.alloc(10));
++ }, {
++ code: 'ERR_OSSL_EVP_INVALID_DIGEST'
++ });
++
++ for (const oaepHash of [0, false, null, Symbol(), () => {}]) {
++ assert.throws(() => {
++ fn({
++ key: rsaPubPem,
++ oaepHash
++ }, Buffer.alloc(10));
++ }, {
++ code: 'ERR_INVALID_ARG_TYPE'
++ });
++ }
++
++ for (const oaepLabel of [0, false, null, Symbol(), () => {}, {}]) {
++ assert.throws(() => {
++ fn({
++ key: rsaPubPem,
++ oaepLabel
++ }, Buffer.alloc(10));
++ }, {
++ code: 'ERR_INVALID_ARG_TYPE'
++ });
++ }
++}
++
++// Test RSA key signing/verification
++let rsaSign = crypto.createSign('SHA1');
++let rsaVerify = crypto.createVerify('SHA1');
++assert.ok(rsaSign);
++assert.ok(rsaVerify);
++
++const expectedSignature = fixtures.readKey(
++ 'rsa_public_sha1_signature_signedby_rsa_private_pkcs8.sha1',
++ 'hex'
++);
++
++rsaSign.update(rsaPubPem);
++let rsaSignature = rsaSign.sign(rsaKeyPem, 'hex');
++assert.strictEqual(rsaSignature, expectedSignature);
++
++rsaVerify.update(rsaPubPem);
++assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true);
++
++// Test RSA PKCS#8 key signing/verification
++rsaSign = crypto.createSign('SHA1');
++rsaSign.update(rsaPubPem);
++rsaSignature = rsaSign.sign(rsaPkcs8KeyPem, 'hex');
++assert.strictEqual(rsaSignature, expectedSignature);
++
++rsaVerify = crypto.createVerify('SHA1');
++rsaVerify.update(rsaPubPem);
++assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true);
++
++// Test RSA key signing/verification with encrypted key
++rsaSign = crypto.createSign('SHA1');
++rsaSign.update(rsaPubPem);
++const signOptions = { key: rsaKeyPemEncrypted, passphrase: 'password' };
++rsaSignature = rsaSign.sign(signOptions, 'hex');
++assert.strictEqual(rsaSignature, expectedSignature);
++
++rsaVerify = crypto.createVerify('SHA1');
++rsaVerify.update(rsaPubPem);
++assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true);
++
++rsaSign = crypto.createSign('SHA1');
++rsaSign.update(rsaPubPem);
++assert.throws(() => {
++ const signOptions = { key: rsaKeyPemEncrypted, passphrase: 'wrong' };
++ rsaSign.sign(signOptions, 'hex');
++}, decryptPrivateKeyError);
++
++//
++// Test RSA signing and verification
++//
++{
++ const privateKey = fixtures.readKey('rsa_private_b.pem');
++ const publicKey = fixtures.readKey('rsa_public_b.pem');
++
++ const input = 'I AM THE WALRUS';
++
++ const signature = fixtures.readKey(
++ 'I_AM_THE_WALRUS_sha256_signature_signedby_rsa_private_b.sha256',
++ 'hex'
++ );
++
++ const sign = crypto.createSign('SHA256');
++ sign.update(input);
++
++ const output = sign.sign(privateKey, 'hex');
++ assert.strictEqual(output, signature);
++
++ const verify = crypto.createVerify('SHA256');
++ verify.update(input);
++
++ assert.strictEqual(verify.verify(publicKey, signature, 'hex'), true);
++
++ // Test the legacy signature algorithm name.
++ const sign2 = crypto.createSign('RSA-SHA256');
++ sign2.update(input);
++
++ const output2 = sign2.sign(privateKey, 'hex');
++ assert.strictEqual(output2, signature);
++
++ const verify2 = crypto.createVerify('SHA256');
++ verify2.update(input);
++
++ assert.strictEqual(verify2.verify(publicKey, signature, 'hex'), true);
++}
++
++
++//
++// Test DSA signing and verification
++//
++{
++ const input = 'I AM THE WALRUS';
++
++ // DSA signatures vary across runs so there is no static string to verify
++ // against.
++ const sign = crypto.createSign('SHA1');
++ sign.update(input);
++ const signature = sign.sign(dsaKeyPem, 'hex');
++
++ const verify = crypto.createVerify('SHA1');
++ verify.update(input);
++
++ assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true);
++
++ // Test the legacy 'DSS1' name.
++ const sign2 = crypto.createSign('DSS1');
++ sign2.update(input);
++ const signature2 = sign2.sign(dsaKeyPem, 'hex');
++
++ const verify2 = crypto.createVerify('DSS1');
++ verify2.update(input);
++
++ assert.strictEqual(verify2.verify(dsaPubPem, signature2, 'hex'), true);
++}
++
++
++//
++// Test DSA signing and verification with PKCS#8 private key
++//
++{
++ const input = 'I AM THE WALRUS';
++
++ // DSA signatures vary across runs so there is no static string to verify
++ // against.
++ const sign = crypto.createSign('SHA1');
++ sign.update(input);
++ const signature = sign.sign(dsaPkcs8KeyPem, 'hex');
++
++ const verify = crypto.createVerify('SHA1');
++ verify.update(input);
++
++ assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true);
++}
++
++
++//
++// Test DSA signing and verification with encrypted key
++//
++const input = 'I AM THE WALRUS';
++
++{
++ const sign = crypto.createSign('SHA1');
++ sign.update(input);
++ assert.throws(() => {
++ sign.sign({ key: dsaKeyPemEncrypted, passphrase: 'wrong' }, 'hex');
++ }, decryptPrivateKeyError);
++}
++
++{
++ // DSA signatures vary across runs so there is no static string to verify
++ // against.
++ const sign = crypto.createSign('SHA1');
++ sign.update(input);
++ const signOptions = { key: dsaKeyPemEncrypted, passphrase: 'password' };
++ const signature = sign.sign(signOptions, 'hex');
++
++ const verify = crypto.createVerify('SHA1');
++ verify.update(input);
++
++ assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true);
++}
+diff --git a/test/parallel/test-crypto-rsa-dsa.js b/test/parallel/test-crypto-rsa-dsa.js
+index 9afcb38..fd27827 100644
+--- a/test/parallel/test-crypto-rsa-dsa.js
++++ b/test/parallel/test-crypto-rsa-dsa.js
+@@ -220,20 +220,36 @@ function test_rsa(padding, encryptOaepHash, decryptOaepHash) {
+ padding: padding,
+ oaepHash: encryptOaepHash
+ }, bufferToEncrypt);
++ if (padding === constants.RSA_PKCS1_PADDING) {
++ assert.throws(() => {
++ crypto.privateDecrypt({
++ key: rsaKeyPem,
++ padding: padding,
++ oaepHash: decryptOaepHash
++ }, encryptedBuffer);
++ }, { code: 'ERR_INVALID_ARG_VALUE' });
++ assert.throws(() => {
++ crypto.privateDecrypt({
++ key: rsaPkcs8KeyPem,
++ padding: padding,
++ oaepHash: decryptOaepHash
++ }, encryptedBuffer);
++ }, { code: 'ERR_INVALID_ARG_VALUE' });
++ } else {
++ let decryptedBuffer = crypto.privateDecrypt({
++ key: rsaKeyPem,
++ padding: padding,
++ oaepHash: decryptOaepHash
++ }, encryptedBuffer);
++ assert.deepStrictEqual(decryptedBuffer, input);
+
+- let decryptedBuffer = crypto.privateDecrypt({
+- key: rsaKeyPem,
+- padding: padding,
+- oaepHash: decryptOaepHash
+- }, encryptedBuffer);
+- assert.deepStrictEqual(decryptedBuffer, input);
+-
+- decryptedBuffer = crypto.privateDecrypt({
+- key: rsaPkcs8KeyPem,
+- padding: padding,
+- oaepHash: decryptOaepHash
+- }, encryptedBuffer);
+- assert.deepStrictEqual(decryptedBuffer, input);
++ decryptedBuffer = crypto.privateDecrypt({
++ key: rsaPkcs8KeyPem,
++ padding: padding,
++ oaepHash: decryptOaepHash
++ }, encryptedBuffer);
++ assert.deepStrictEqual(decryptedBuffer, input);
++ }
+ }
+
+ test_rsa('RSA_NO_PADDING');
+--
+2.40.0
diff --git a/meta-oe/recipes-devtools/nodejs/nodejs/CVE-2024-22019.patch b/meta-oe/recipes-devtools/nodejs/nodejs/CVE-2024-22019.patch
new file mode 100644
index 0000000000..ca1c7981cc
--- /dev/null
+++ b/meta-oe/recipes-devtools/nodejs/nodejs/CVE-2024-22019.patch
@@ -0,0 +1,556 @@
+From 911cb33cdadab57a75f97186290ea8f3903a6171 Mon Sep 17 00:00:00 2001
+From: Paolo Insogna <paolo@cowtech.it>
+Date: Tue, 9 Jan 2024 18:10:04 +0100
+Subject: [PATCH] http: add maximum chunk extension size
+
+PR-URL: https://github.com/nodejs-private/node-private/pull/520
+Refs: https://github.com/nodejs-private/node-private/pull/518
+
+CVE-ID: CVE-2024-22019
+
+Upstream-Status: Backport [https://github.com/nodejs/node/commit/911cb33cdadab57a]
+
+Signed-off-by: Archana Polampalli <archana.polampalli@windriver.com>
+---
+ deps/llhttp/CMakeLists.txt | 2 +-
+ deps/llhttp/include/llhttp.h | 7 +-
+ deps/llhttp/src/api.c | 7 +
+ deps/llhttp/src/llhttp.c | 122 ++++++++++++++--
+ doc/api/errors.md | 12 ++
+ lib/_http_server.js | 8 ++
+ src/node_http_parser.cc | 20 ++-
+ .../test-http-chunk-extensions-limit.js | 131 ++++++++++++++++++
+ tools/update-llhttp.sh | 2 +-
+ 9 files changed, 292 insertions(+), 19 deletions(-)
+ create mode 100644 test/parallel/test-http-chunk-extensions-limit.js
+
+diff --git a/deps/llhttp/CMakeLists.txt b/deps/llhttp/CMakeLists.txt
+index d038203..747564a 100644
+--- a/deps/llhttp/CMakeLists.txt
++++ b/deps/llhttp/CMakeLists.txt
+@@ -1,7 +1,7 @@
+ cmake_minimum_required(VERSION 3.5.1)
+ cmake_policy(SET CMP0069 NEW)
+
+-project(llhttp VERSION 6.0.11)
++project(llhttp VERSION 6.1.0)
+ include(GNUInstallDirs)
+
+ set(CMAKE_C_STANDARD 99)
+diff --git a/deps/llhttp/include/llhttp.h b/deps/llhttp/include/llhttp.h
+index 2da66f1..78f27ab 100644
+--- a/deps/llhttp/include/llhttp.h
++++ b/deps/llhttp/include/llhttp.h
+@@ -2,8 +2,8 @@
+ #define INCLUDE_LLHTTP_H_
+
+ #define LLHTTP_VERSION_MAJOR 6
+-#define LLHTTP_VERSION_MINOR 0
+-#define LLHTTP_VERSION_PATCH 11
++#define LLHTTP_VERSION_MINOR 1
++#define LLHTTP_VERSION_PATCH 0
+
+ #ifndef LLHTTP_STRICT_MODE
+ # define LLHTTP_STRICT_MODE 0
+@@ -348,6 +348,9 @@ struct llhttp_settings_s {
+ */
+ llhttp_cb on_headers_complete;
+
++ /* Possible return values 0, -1, HPE_USER */
++ llhttp_data_cb on_chunk_parameters;
++
+ /* Possible return values 0, -1, HPE_USER */
+ llhttp_data_cb on_body;
+
+diff --git a/deps/llhttp/src/api.c b/deps/llhttp/src/api.c
+index c4ce197..d3065b3 100644
+--- a/deps/llhttp/src/api.c
++++ b/deps/llhttp/src/api.c
+@@ -355,6 +355,13 @@ int llhttp__on_chunk_header(llhttp_t* s, const char* p, const char* endp) {
+ }
+
+
++int llhttp__on_chunk_parameters(llhttp_t* s, const char* p, const char* endp) {
++ int err;
++ SPAN_CALLBACK_MAYBE(s, on_chunk_parameters, p, endp - p);
++ return err;
++}
++
++
+ int llhttp__on_chunk_complete(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_chunk_complete);
+diff --git a/deps/llhttp/src/llhttp.c b/deps/llhttp/src/llhttp.c
+index 5e7c5d1..5eb19f6 100644
+--- a/deps/llhttp/src/llhttp.c
++++ b/deps/llhttp/src/llhttp.c
+@@ -340,6 +340,8 @@ enum llparse_state_e {
+ s_n_llhttp__internal__n_invoke_is_equal_content_length,
+ s_n_llhttp__internal__n_chunk_size_almost_done,
+ s_n_llhttp__internal__n_chunk_parameters,
++ s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters,
++ s_n_llhttp__internal__n_chunk_parameters_ows,
+ s_n_llhttp__internal__n_chunk_size_otherwise,
+ s_n_llhttp__internal__n_chunk_size,
+ s_n_llhttp__internal__n_chunk_size_digit,
+@@ -539,6 +541,10 @@ int llhttp__on_body(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
++int llhttp__on_chunk_parameters(
++ llhttp__internal_t* s, const unsigned char* p,
++ const unsigned char* endp);
++
+ int llhttp__on_status(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+@@ -1226,8 +1232,7 @@ static llparse_state_t llhttp__internal__run(
+ goto s_n_llhttp__internal__n_chunk_parameters;
+ }
+ case 2: {
+- p++;
+- goto s_n_llhttp__internal__n_chunk_size_almost_done;
++ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_parameters;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_10;
+@@ -1236,6 +1241,34 @@ static llparse_state_t llhttp__internal__run(
+ /* UNREACHABLE */;
+ abort();
+ }
++ case s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters:
++ s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters: {
++ if (p == endp) {
++ return s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters;
++ }
++ state->_span_pos0 = (void*) p;
++ state->_span_cb0 = llhttp__on_chunk_parameters;
++ goto s_n_llhttp__internal__n_chunk_parameters;
++ /* UNREACHABLE */;
++ abort();
++ }
++ case s_n_llhttp__internal__n_chunk_parameters_ows:
++ s_n_llhttp__internal__n_chunk_parameters_ows: {
++ if (p == endp) {
++ return s_n_llhttp__internal__n_chunk_parameters_ows;
++ }
++ switch (*p) {
++ case ' ': {
++ p++;
++ goto s_n_llhttp__internal__n_chunk_parameters_ows;
++ }
++ default: {
++ goto s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters;
++ }
++ }
++ /* UNREACHABLE */;
++ abort();
++ }
+ case s_n_llhttp__internal__n_chunk_size_otherwise:
+ s_n_llhttp__internal__n_chunk_size_otherwise: {
+ if (p == endp) {
+@@ -1246,13 +1279,9 @@ static llparse_state_t llhttp__internal__run(
+ p++;
+ goto s_n_llhttp__internal__n_chunk_size_almost_done;
+ }
+- case ' ': {
+- p++;
+- goto s_n_llhttp__internal__n_chunk_parameters;
+- }
+ case ';': {
+ p++;
+- goto s_n_llhttp__internal__n_chunk_parameters;
++ goto s_n_llhttp__internal__n_chunk_parameters_ows;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_11;
+@@ -6074,6 +6103,24 @@ static llparse_state_t llhttp__internal__run(
+ /* UNREACHABLE */;
+ abort();
+ }
++ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_parameters: {
++ const unsigned char* start;
++ int err;
++
++ start = state->_span_pos0;
++ state->_span_pos0 = NULL;
++ err = llhttp__on_chunk_parameters(state, start, p);
++ if (err != 0) {
++ state->error = err;
++ state->error_pos = (const char*) (p + 1);
++ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_size_almost_done;
++ return s_error;
++ }
++ p++;
++ goto s_n_llhttp__internal__n_chunk_size_almost_done;
++ /* UNREACHABLE */;
++ abort();
++ }
+ s_n_llhttp__internal__n_error_10: {
+ state->error = 0x2;
+ state->reason = "Invalid character in chunk parameters";
+@@ -8441,6 +8488,8 @@ enum llparse_state_e {
+ s_n_llhttp__internal__n_invoke_is_equal_content_length,
+ s_n_llhttp__internal__n_chunk_size_almost_done,
+ s_n_llhttp__internal__n_chunk_parameters,
++ s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters,
++ s_n_llhttp__internal__n_chunk_parameters_ows,
+ s_n_llhttp__internal__n_chunk_size_otherwise,
+ s_n_llhttp__internal__n_chunk_size,
+ s_n_llhttp__internal__n_chunk_size_digit,
+@@ -8635,6 +8684,10 @@ int llhttp__on_body(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
++int llhttp__on_chunk_parameters(
++ llhttp__internal_t* s, const unsigned char* p,
++ const unsigned char* endp);
++
+ int llhttp__on_status(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+@@ -9299,8 +9352,7 @@ static llparse_state_t llhttp__internal__run(
+ goto s_n_llhttp__internal__n_chunk_parameters;
+ }
+ case 2: {
+- p++;
+- goto s_n_llhttp__internal__n_chunk_size_almost_done;
++ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_parameters;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_6;
+@@ -9309,6 +9361,34 @@ static llparse_state_t llhttp__internal__run(
+ /* UNREACHABLE */;
+ abort();
+ }
++ case s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters:
++ s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters: {
++ if (p == endp) {
++ return s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters;
++ }
++ state->_span_pos0 = (void*) p;
++ state->_span_cb0 = llhttp__on_chunk_parameters;
++ goto s_n_llhttp__internal__n_chunk_parameters;
++ /* UNREACHABLE */;
++ abort();
++ }
++ case s_n_llhttp__internal__n_chunk_parameters_ows:
++ s_n_llhttp__internal__n_chunk_parameters_ows: {
++ if (p == endp) {
++ return s_n_llhttp__internal__n_chunk_parameters_ows;
++ }
++ switch (*p) {
++ case ' ': {
++ p++;
++ goto s_n_llhttp__internal__n_chunk_parameters_ows;
++ }
++ default: {
++ goto s_n_llhttp__internal__n_span_start_llhttp__on_chunk_parameters;
++ }
++ }
++ /* UNREACHABLE */;
++ abort();
++ }
+ case s_n_llhttp__internal__n_chunk_size_otherwise:
+ s_n_llhttp__internal__n_chunk_size_otherwise: {
+ if (p == endp) {
+@@ -9319,13 +9399,9 @@ static llparse_state_t llhttp__internal__run(
+ p++;
+ goto s_n_llhttp__internal__n_chunk_size_almost_done;
+ }
+- case ' ': {
+- p++;
+- goto s_n_llhttp__internal__n_chunk_parameters;
+- }
+ case ';': {
+ p++;
+- goto s_n_llhttp__internal__n_chunk_parameters;
++ goto s_n_llhttp__internal__n_chunk_parameters_ows;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_7;
+@@ -13951,6 +14027,24 @@ static llparse_state_t llhttp__internal__run(
+ /* UNREACHABLE */;
+ abort();
+ }
++ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_parameters: {
++ const unsigned char* start;
++ int err;
++
++ start = state->_span_pos0;
++ state->_span_pos0 = NULL;
++ err = llhttp__on_chunk_parameters(state, start, p);
++ if (err != 0) {
++ state->error = err;
++ state->error_pos = (const char*) (p + 1);
++ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_size_almost_done;
++ return s_error;
++ }
++ p++;
++ goto s_n_llhttp__internal__n_chunk_size_almost_done;
++ /* UNREACHABLE */;
++ abort();
++ }
+ s_n_llhttp__internal__n_error_6: {
+ state->error = 0x2;
+ state->reason = "Invalid character in chunk parameters";
+diff --git a/doc/api/errors.md b/doc/api/errors.md
+index dcf8744..a76bfe5 100644
+--- a/doc/api/errors.md
++++ b/doc/api/errors.md
+@@ -3043,6 +3043,18 @@ malconfigured clients, if more than 8 KiB of HTTP header data is received then
+ HTTP parsing will abort without a request or response object being created, and
+ an `Error` with this code will be emitted.
+
++<a id="HPE_CHUNK_EXTENSIONS_OVERFLOW"></a>
++
++### `HPE_CHUNK_EXTENSIONS_OVERFLOW`
++
++<!-- YAML
++added: REPLACEME
++-->
++
++Too much data was received for a chunk extensions. In order to protect against
++malicious or malconfigured clients, if more than 16 KiB of data is received
++then an `Error` with this code will be emitted.
++
+ <a id="HPE_UNEXPECTED_CONTENT_LENGTH"></a>
+
+ ### `HPE_UNEXPECTED_CONTENT_LENGTH`
+diff --git a/lib/_http_server.js b/lib/_http_server.js
+index 4e23266..263bb52 100644
+--- a/lib/_http_server.js
++++ b/lib/_http_server.js
+@@ -706,6 +706,11 @@ const requestHeaderFieldsTooLargeResponse = Buffer.from(
+ `HTTP/1.1 431 ${STATUS_CODES[431]}\r\n` +
+ 'Connection: close\r\n\r\n', 'ascii'
+ );
++const requestChunkExtensionsTooLargeResponse = Buffer.from(
++ `HTTP/1.1 413 ${STATUS_CODES[413]}\r\n` +
++ 'Connection: close\r\n\r\n', 'ascii',
++);
++
+ function socketOnError(e) {
+ // Ignore further errors
+ this.removeListener('error', socketOnError);
+@@ -719,6 +724,9 @@ function socketOnError(e) {
+ case 'HPE_HEADER_OVERFLOW':
+ response = requestHeaderFieldsTooLargeResponse;
+ break;
++ case 'HPE_CHUNK_EXTENSIONS_OVERFLOW':
++ response = requestChunkExtensionsTooLargeResponse;
++ break;
+ case 'ERR_HTTP_REQUEST_TIMEOUT':
+ response = requestTimeoutResponse;
+ break;
+diff --git a/src/node_http_parser.cc b/src/node_http_parser.cc
+index 74f3248..b92e848 100644
+--- a/src/node_http_parser.cc
++++ b/src/node_http_parser.cc
+@@ -79,6 +79,8 @@ const uint32_t kOnExecute = 5;
+ const uint32_t kOnTimeout = 6;
+ // Any more fields than this will be flushed into JS
+ const size_t kMaxHeaderFieldsCount = 32;
++// Maximum size of chunk extensions
++const size_t kMaxChunkExtensionsSize = 16384;
+
+ const uint32_t kLenientNone = 0;
+ const uint32_t kLenientHeaders = 1 << 0;
+@@ -206,6 +208,7 @@ class Parser : public AsyncWrap, public StreamListener {
+
+ int on_message_begin() {
+ num_fields_ = num_values_ = 0;
++ chunk_extensions_nread_ = 0;
+ url_.Reset();
+ status_message_.Reset();
+ header_parsing_start_time_ = uv_hrtime();
+@@ -443,9 +446,22 @@ class Parser : public AsyncWrap, public StreamListener {
+ return 0;
+ }
+
+- // Reset nread for the next chunk
++ int on_chunk_extension(const char* at, size_t length) {
++ chunk_extensions_nread_ += length;
++
++ if (chunk_extensions_nread_ > kMaxChunkExtensionsSize) {
++ llhttp_set_error_reason(&parser_,
++ "HPE_CHUNK_EXTENSIONS_OVERFLOW:Chunk extensions overflow");
++ return HPE_USER;
++ }
++
++ return 0;
++ }
++
++ // Reset nread for the next chunk and also reset the extensions counter
+ int on_chunk_header() {
+ header_nread_ = 0;
++ chunk_extensions_nread_ = 0;
+ return 0;
+ }
+
+@@ -887,6 +903,7 @@ class Parser : public AsyncWrap, public StreamListener {
+ const char* current_buffer_data_;
+ bool pending_pause_ = false;
+ uint64_t header_nread_ = 0;
++ uint64_t chunk_extensions_nread_ = 0;
+ uint64_t max_http_header_size_;
+ uint64_t headers_timeout_;
+ uint64_t header_parsing_start_time_ = 0;
+@@ -921,6 +938,7 @@ const llhttp_settings_t Parser::settings = {
+ Proxy<DataCall, &Parser::on_header_field>::Raw,
+ Proxy<DataCall, &Parser::on_header_value>::Raw,
+ Proxy<Call, &Parser::on_headers_complete>::Raw,
++ Proxy<DataCall, &Parser::on_chunk_extension>::Raw,
+ Proxy<DataCall, &Parser::on_body>::Raw,
+ Proxy<Call, &Parser::on_message_complete>::Raw,
+ Proxy<Call, &Parser::on_chunk_header>::Raw,
+diff --git a/test/parallel/test-http-chunk-extensions-limit.js b/test/parallel/test-http-chunk-extensions-limit.js
+new file mode 100644
+index 0000000..6868b3d
+--- /dev/null
++++ b/test/parallel/test-http-chunk-extensions-limit.js
+@@ -0,0 +1,131 @@
++'use strict';
++
++const common = require('../common');
++const http = require('http');
++const net = require('net');
++const assert = require('assert');
++
++// Verify that chunk extensions are limited in size when sent all together.
++{
++ const server = http.createServer((req, res) => {
++ req.on('end', () => {
++ res.writeHead(200, { 'Content-Type': 'text/plain' });
++ res.end('bye');
++ });
++
++ req.resume();
++ });
++
++ server.listen(0, () => {
++ const sock = net.connect(server.address().port);
++ let data = '';
++
++ sock.on('data', (chunk) => data += chunk.toString('utf-8'));
++
++ sock.on('end', common.mustCall(function() {
++ assert.strictEqual(data, 'HTTP/1.1 413 Payload Too Large\r\nConnection: close\r\n\r\n');
++ server.close();
++ }));
++
++ sock.end('' +
++ 'GET / HTTP/1.1\r\n' +
++ 'Host: localhost:8080\r\n' +
++ 'Transfer-Encoding: chunked\r\n\r\n' +
++ '2;' + 'A'.repeat(20000) + '=bar\r\nAA\r\n' +
++ '0\r\n\r\n'
++ );
++ });
++}
++
++// Verify that chunk extensions are limited in size when sent in intervals.
++{
++ const server = http.createServer((req, res) => {
++ req.on('end', () => {
++ res.writeHead(200, { 'Content-Type': 'text/plain' });
++ res.end('bye');
++ });
++
++ req.resume();
++ });
++
++ server.listen(0, () => {
++ const sock = net.connect(server.address().port);
++ let remaining = 20000;
++ let data = '';
++
++ const interval = setInterval(
++ () => {
++ if (remaining > 0) {
++ sock.write('A'.repeat(1000));
++ } else {
++ sock.write('=bar\r\nAA\r\n0\r\n\r\n');
++ clearInterval(interval);
++ }
++
++ remaining -= 1000;
++ },
++ common.platformTimeout(20),
++ ).unref();
++
++ sock.on('data', (chunk) => data += chunk.toString('utf-8'));
++
++ sock.on('end', common.mustCall(function() {
++ assert.strictEqual(data, 'HTTP/1.1 413 Payload Too Large\r\nConnection: close\r\n\r\n');
++ server.close();
++ }));
++
++ sock.write('' +
++ 'GET / HTTP/1.1\r\n' +
++ 'Host: localhost:8080\r\n' +
++ 'Transfer-Encoding: chunked\r\n\r\n' +
++ '2;'
++ );
++ });
++}
++
++// Verify the chunk extensions is correctly reset after a chunk
++{
++ const server = http.createServer((req, res) => {
++ req.on('end', () => {
++ res.writeHead(200, { 'content-type': 'text/plain', 'connection': 'close', 'date': 'now' });
++ res.end('bye');
++ });
++
++ req.resume();
++ });
++
++ server.listen(0, () => {
++ const sock = net.connect(server.address().port);
++ let data = '';
++
++ sock.on('data', (chunk) => data += chunk.toString('utf-8'));
++
++ sock.on('end', common.mustCall(function() {
++ assert.strictEqual(
++ data,
++ 'HTTP/1.1 200 OK\r\n' +
++ 'content-type: text/plain\r\n' +
++ 'connection: close\r\n' +
++ 'date: now\r\n' +
++ 'Transfer-Encoding: chunked\r\n' +
++ '\r\n' +
++ '3\r\n' +
++ 'bye\r\n' +
++ '0\r\n' +
++ '\r\n',
++ );
++
++ server.close();
++ }));
++
++ sock.end('' +
++ 'GET / HTTP/1.1\r\n' +
++ 'Host: localhost:8080\r\n' +
++ 'Transfer-Encoding: chunked\r\n\r\n' +
++ '2;' + 'A'.repeat(10000) + '=bar\r\nAA\r\n' +
++ '2;' + 'A'.repeat(10000) + '=bar\r\nAA\r\n' +
++ '2;' + 'A'.repeat(10000) + '=bar\r\nAA\r\n' +
++ '0\r\n\r\n'
++ );
++ });
++}
+diff --git a/tools/update-llhttp.sh b/tools/update-llhttp.sh
+index 12e2f46..a95eef1 100755
+--- a/tools/update-llhttp.sh
++++ b/tools/update-llhttp.sh
+@@ -59,5 +59,5 @@ echo ""
+ echo "Please git add llhttp, commit the new version:"
+ echo ""
+ echo "$ git add -A deps/llhttp"
+-echo "$ git commit -m \"deps: update nghttp2 to $LLHTTP_VERSION\""
++echo "$ git commit -m \"deps: update llhttp to $LLHTTP_VERSION\""
+ echo ""
+--
+2.40.0
diff --git a/meta-oe/recipes-devtools/nodejs/nodejs/CVE-2024-22025.patch b/meta-oe/recipes-devtools/nodejs/nodejs/CVE-2024-22025.patch
new file mode 100644
index 0000000000..ac3a54aba6
--- /dev/null
+++ b/meta-oe/recipes-devtools/nodejs/nodejs/CVE-2024-22025.patch
@@ -0,0 +1,148 @@
+From 9052ef43dc2d1b0db340591a9bc9e45a25c01d90 Mon Sep 17 00:00:00 2001
+From: Matteo Collina <hello@matteocollina.com>
+Date: Tue, 6 Feb 2024 16:47:20 +0100
+Subject: [PATCH 4/5] zlib: pause stream if outgoing buffer is full
+
+Signed-off-by: Matteo Collina <hello@matteocollina.com>
+PR-URL: https://github.com/nodejs-private/node-private/pull/540
+Reviewed-By: Robert Nagy <ronagy@icloud.com>
+Ref: https://hackerone.com/reports/2284065
+
+CVE-ID: CVE-2024-22025
+
+Upstream-Status: Backport [https://github.com/nodejs/node/commit/9052ef43dc2d1b0d]
+
+Signed-off-by: Archana Polampalli <archana.polampalli@windriver.com>
+---
+ lib/zlib.js | 32 +++++++++++++++++++-------
+ test/parallel/test-zlib-brotli-16GB.js | 22 ++++++++++++++++++
+ test/parallel/test-zlib-params.js | 24 +++++++++++--------
+ 3 files changed, 61 insertions(+), 17 deletions(-)
+ create mode 100644 test/parallel/test-zlib-brotli-16GB.js
+
+diff --git a/lib/zlib.js b/lib/zlib.js
+index 9bde199..8e033e5 100644
+--- a/lib/zlib.js
++++ b/lib/zlib.js
+@@ -560,10 +560,11 @@ function processCallback() {
+ self.bytesWritten += inDelta;
+
+ const have = handle.availOutBefore - availOutAfter;
++ let streamBufferIsFull = false;
+ if (have > 0) {
+ const out = self._outBuffer.slice(self._outOffset, self._outOffset + have);
+ self._outOffset += have;
+- self.push(out);
++ streamBufferIsFull = !self.push(out);
+ } else {
+ assert(have === 0, 'have should not go down');
+ }
+@@ -588,13 +589,28 @@ function processCallback() {
+ handle.inOff += inDelta;
+ handle.availInBefore = availInAfter;
+
+- this.write(handle.flushFlag,
+- this.buffer, // in
+- handle.inOff, // in_off
+- handle.availInBefore, // in_len
+- self._outBuffer, // out
+- self._outOffset, // out_off
+- self._chunkSize); // out_len
++ if (!streamBufferIsFull) {
++ this.write(handle.flushFlag,
++ this.buffer, // in
++ handle.inOff, // in_off
++ handle.availInBefore, // in_len
++ self._outBuffer, // out
++ self._outOffset, // out_off
++ self._chunkSize); // out_len
++ } else {
++ const oldRead = self._read;
++ self._read = (n) => {
++ self._read = oldRead;
++ this.write(handle.flushFlag,
++ this.buffer, // in
++ handle.inOff, // in_off
++ handle.availInBefore, // in_len
++ self._outBuffer, // out
++ self._outOffset, // out_off
++ self._chunkSize); // out_len
++ self._read(n);
++ };
++ }
+ return;
+ }
+
+diff --git a/test/parallel/test-zlib-brotli-16GB.js b/test/parallel/test-zlib-brotli-16GB.js
+new file mode 100644
+index 0000000..1ca10f7
+--- /dev/null
++++ b/test/parallel/test-zlib-brotli-16GB.js
+@@ -0,0 +1,22 @@
++use strict';
++
++const common = require('../common');
++const { createBrotliDecompress } = require('node:zlib');
++const strictEqual = require('node:assert').strictEqual;
++
++// This tiny HEX string is a 16GB file.
++// This test verifies that the stream actually stops.
++/* eslint-disable max-len */
++const content = 'cfffff7ff82700e2b14020f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c32200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bff3f';
++
++const buf = Buffer.from(content, 'hex');
++
++const decoder = createBrotliDecompress();
++decoder.end(buf);
++
++// We need to wait to verify that the libuv thread pool had time
++// to process the data and the buffer is not empty.
++setTimeout(common.mustCall(() => {
++ // There is only one chunk in the buffer
++ strictEqual(decoder._readableState.buffer.length, 1);
++}), common.platformTimeout(100));
+diff --git a/test/parallel/test-zlib-params.js b/test/parallel/test-zlib-params.js
+index 30d4f13..18271fe 100644
+--- a/test/parallel/test-zlib-params.js
++++ b/test/parallel/test-zlib-params.js
+@@ -12,23 +12,29 @@ const deflater = zlib.createDeflate(opts);
+ const chunk1 = file.slice(0, chunkSize);
+ const chunk2 = file.slice(chunkSize);
+ const blkhdr = Buffer.from([0x00, 0x5a, 0x82, 0xa5, 0x7d]);
+-const expected = Buffer.concat([blkhdr, chunk2]);
+-let actual;
++const blkftr = Buffer.from('010000ffff7dac3072', 'hex');
++const expected = Buffer.concat([blkhdr, chunk2, blkftr]);
++const bufs = [];
++
++function read() {
++ let buf;
++ while ((buf = deflater.read()) !== null) {
++ bufs.push(buf);
++ }
++}
+
+ deflater.write(chunk1, function() {
+ deflater.params(0, zlib.constants.Z_DEFAULT_STRATEGY, function() {
+ while (deflater.read());
+- deflater.end(chunk2, function() {
+- const bufs = [];
+- let buf;
+- while ((buf = deflater.read()) !== null)
+- bufs.push(buf);
+- actual = Buffer.concat(bufs);
+- });
++
++ deflater.on('readable', read);
++
++ deflater.end(chunk2);
+ });
+ while (deflater.read());
+ });
+
+ process.once('exit', function() {
++ const actual = Buffer.concat(bufs);
+ assert.deepStrictEqual(actual, expected);
+ });
+--
+2.40.0