From 1272fd04b8dd367f23f438d366f905c90b3488c38d0c61cd43cac7ece7691ce0 Mon Sep 17 00:00:00 2001 From: Adam Majer Date: Tue, 20 Feb 2024 16:34:06 +0000 Subject: [PATCH] * CVE-2023-46809.patch: Node.js is vulnerable to the Marvin Attack (timing variant of the Bleichenbacher attack against PKCS#1 v1.5 padding) - (Medium) (CVE-2023-46809, bsc#1219997) * CVE-2024-22019.patch: http: Reading unprocessed HTTP request with unbounded chunk extension allows DoS attacks- (High) (CVE-2024-22019, bsc#1219993) * CVE-2024-22025.patch: fix Denial of Service by resource exhaustion in fetch() brotli decoding (CVE-2024-22025, bsc#1220014) * CVE-2024-24758.patch: ignore proxy-authorization headers (CVE-2024-24758, bsc#1220017) * CVE-2024-24806.patch: fix improper domain lookup that potentially leads to SSRF attacks (CVE-2024-24806, bsc#1220053) - CVE-2023-38552.patch: Integrity checks according to policies can be circumvented (CVE-2023-38552, bsc#1216272) - CVE-2023-39333.patch, wasm-fixture.tar.gz: Code injection via WebAssembly export names (CVE-2023-39333, bsc#1216273) - CVE-2023-45143.patch: undici Security Release (CVE-2023-45143, bsc#1216205) - nodejs.keyring: include new releaser keys OBS-URL: https://build.opensuse.org/package/show/devel:languages:nodejs/nodejs16?expand=0&rev=100 --- CVE-2023-46809.patch | 594 +++++++++++++++++++++++++++++++++++++++++++ CVE-2024-22019.patch | 527 ++++++++++++++++++++++++++++++++++++++ CVE-2024-22025.patch | 124 +++++++++ CVE-2024-24758.patch | 32 +++ CVE-2024-24806.patch | 26 ++ nodejs16.changes | 25 +- nodejs16.spec | 32 ++- 7 files changed, 1352 insertions(+), 8 deletions(-) create mode 100644 CVE-2023-46809.patch create mode 100644 CVE-2024-22019.patch create mode 100644 CVE-2024-22025.patch create mode 100644 CVE-2024-24758.patch create mode 100644 CVE-2024-24806.patch diff --git a/CVE-2023-46809.patch b/CVE-2023-46809.patch new file mode 100644 index 0000000..0671058 --- /dev/null +++ b/CVE-2023-46809.patch @@ -0,0 +1,594 @@ +Index: node-v16.20.2/test/parallel/test-crypto-rsa-dsa-revert.js +=================================================================== +--- /dev/null ++++ node-v16.20.2/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); ++} +Index: node-v16.20.2/test/parallel/test-crypto-rsa-dsa.js +=================================================================== +--- node-v16.20.2.orig/test/parallel/test-crypto-rsa-dsa.js ++++ node-v16.20.2/test/parallel/test-crypto-rsa-dsa.js +@@ -221,19 +221,37 @@ function test_rsa(padding, encryptOaepHa + 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); ++ 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); ++ ++ decryptedBuffer = crypto.privateDecrypt({ ++ key: rsaPkcs8KeyPem, ++ padding: padding, ++ oaepHash: decryptOaepHash ++ }, encryptedBuffer); ++ assert.deepStrictEqual(decryptedBuffer, input); ++ } + } + + test_rsa('RSA_NO_PADDING'); +Index: node-v16.20.2/src/crypto/crypto_cipher.cc +=================================================================== +--- node-v16.20.2.orig/src/crypto/crypto_cipher.cc ++++ node-v16.20.2/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,34 @@ void PublicKeyCipher::Cipher(const Funct + 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]); +Index: node-v16.20.2/src/node_revert.h +=================================================================== +--- node-v16.20.2.orig/src/node_revert.h ++++ node-v16.20.2/src/node_revert.h +@@ -18,7 +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_2016_PEND, "CVE-2016-PEND", "Vulnerability Title") ++ XX(CVE_2023_46809, "CVE-2023-46809", "Marvin attack on PKCS#1 padding") + + enum reversion { + #define V(code, ...) SECURITY_REVERT_##code, diff --git a/CVE-2024-22019.patch b/CVE-2024-22019.patch new file mode 100644 index 0000000..c71480c --- /dev/null +++ b/CVE-2024-22019.patch @@ -0,0 +1,527 @@ +Index: node-v16.20.2/deps/llhttp/CMakeLists.txt +=================================================================== +--- node-v16.20.2.orig/deps/llhttp/CMakeLists.txt ++++ node-v16.20.2/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) +Index: node-v16.20.2/deps/llhttp/include/llhttp.h +=================================================================== +--- node-v16.20.2.orig/deps/llhttp/include/llhttp.h ++++ node-v16.20.2/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 +@@ -349,6 +349,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; + + /* Possible return values 0, -1, `HPE_PAUSED` */ +Index: node-v16.20.2/deps/llhttp/src/api.c +=================================================================== +--- node-v16.20.2.orig/deps/llhttp/src/api.c ++++ node-v16.20.2/deps/llhttp/src/api.c +@@ -355,6 +355,13 @@ int llhttp__on_chunk_header(llhttp_t* s, + } + + ++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); +Index: node-v16.20.2/deps/llhttp/src/llhttp.c +=================================================================== +--- node-v16.20.2.orig/deps/llhttp/src/llhttp.c ++++ node-v16.20.2/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_ + 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_ + /* 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_ + 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_ + /* 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_ + 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_ + /* 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_ + 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_ + /* 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"; +Index: node-v16.20.2/doc/api/errors.md +=================================================================== +--- node-v16.20.2.orig/doc/api/errors.md ++++ node-v16.20.2/doc/api/errors.md +@@ -3043,6 +3043,18 @@ malconfigured clients, if more than 8 Ki + HTTP parsing will abort without a request or response object being created, and + an `Error` with this code will be emitted. + ++ ++ ++### `HPE_CHUNK_EXTENSIONS_OVERFLOW` ++ ++ ++ ++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. ++ + + + ### `HPE_UNEXPECTED_CONTENT_LENGTH` +Index: node-v16.20.2/test/parallel/test-http-chunk-extensions-limit.js +=================================================================== +--- /dev/null ++++ node-v16.20.2/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' ++ ); ++ }); ++} +Index: node-v16.20.2/tools/update-llhttp.sh +=================================================================== +--- node-v16.20.2.orig/tools/update-llhttp.sh ++++ node-v16.20.2/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 "" +Index: node-v16.20.2/lib/_http_server.js +=================================================================== +--- node-v16.20.2.orig/lib/_http_server.js ++++ node-v16.20.2/lib/_http_server.js +@@ -706,6 +706,11 @@ const requestHeaderFieldsTooLargeRespons + `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; +Index: node-v16.20.2/src/node_http_parser.cc +=================================================================== +--- node-v16.20.2.orig/src/node_http_parser.cc ++++ node-v16.20.2/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 + + 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 + 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 + 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::Raw, + Proxy::Raw, + Proxy::Raw, ++ Proxy::Raw, + Proxy::Raw, + Proxy::Raw, + Proxy::Raw, diff --git a/CVE-2024-22025.patch b/CVE-2024-22025.patch new file mode 100644 index 0000000..836c781 --- /dev/null +++ b/CVE-2024-22025.patch @@ -0,0 +1,124 @@ +Index: node-v16.20.2/lib/zlib.js +=================================================================== +--- node-v16.20.2.orig/lib/zlib.js ++++ node-v16.20.2/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,29 @@ 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; + } + +Index: node-v16.20.2/test/parallel/test-zlib-brotli-16GB.js +=================================================================== +--- /dev/null ++++ node-v16.20.2/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)); +Index: node-v16.20.2/test/parallel/test-zlib-params.js +=================================================================== +--- node-v16.20.2.orig/test/parallel/test-zlib-params.js ++++ node-v16.20.2/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); + }); diff --git a/CVE-2024-24758.patch b/CVE-2024-24758.patch new file mode 100644 index 0000000..f118147 --- /dev/null +++ b/CVE-2024-24758.patch @@ -0,0 +1,32 @@ +Index: node-v16.20.2/deps/undici/src/lib/fetch/index.js +=================================================================== +--- node-v16.20.2.orig/deps/undici/src/lib/fetch/index.js ++++ node-v16.20.2/deps/undici/src/lib/fetch/index.js +@@ -1200,6 +1200,13 @@ async function httpRedirectFetch (fetchP + if (!sameOrigin(requestCurrentURL(request), locationURL)) { + // https://fetch.spec.whatwg.org/#cors-non-wildcard-request-header-name + request.headersList.delete('authorization') ++ ++ // https://fetch.spec.whatwg.org/#authentication-entries ++ request.headersList.delete('proxy-authorization', true) ++ ++ // "Cookie" and "Host" are forbidden request-headers, which undici doesn't implement. ++ request.headersList.delete('cookie') ++ request.headersList.delete('host') + } + + // 14. If request’s body is non-null, then set request’s body to the first return +Index: node-v16.20.2/deps/undici/undici.js +=================================================================== +--- node-v16.20.2.orig/deps/undici/undici.js ++++ node-v16.20.2/deps/undici/undici.js +@@ -11167,6 +11167,9 @@ var require_fetch = __commonJS({ + } + if (!sameOrigin(requestCurrentURL(request), locationURL)) { + request.headersList.delete("authorization"); ++ request.headersList.delete("proxy-authorization", true); ++ request.headersList.delete("cookie"); ++ request.headersList.delete("host"); + } + if (request.body != null) { + assert(request.body.source != null); diff --git a/CVE-2024-24806.patch b/CVE-2024-24806.patch new file mode 100644 index 0000000..8837482 --- /dev/null +++ b/CVE-2024-24806.patch @@ -0,0 +1,26 @@ +Index: node-v16.20.2/deps/uv/src/idna.c +=================================================================== +--- node-v16.20.2.orig/deps/uv/src/idna.c ++++ node-v16.20.2/deps/uv/src/idna.c +@@ -273,6 +273,9 @@ long uv__idna_toascii(const char* s, con + char* ds; + int rc; + ++ if (s == se) ++ return UV_EINVAL; ++ + ds = d; + + si = s; +@@ -307,8 +310,9 @@ long uv__idna_toascii(const char* s, con + return rc; + } + +- if (d < de) +- *d++ = '\0'; ++ if (d >= de) ++ return UV_EINVAL; + ++ *d++ = '\0'; + return d - ds; /* Number of bytes written. */ + } diff --git a/nodejs16.changes b/nodejs16.changes index 1d410ea..38acd75 100644 --- a/nodejs16.changes +++ b/nodejs16.changes @@ -1,10 +1,29 @@ +------------------------------------------------------------------- +Tue Feb 20 09:52:34 UTC 2024 - Adam Majer + + * CVE-2023-46809.patch: Node.js is vulnerable to the Marvin Attack + (timing variant of the Bleichenbacher attack against + PKCS#1 v1.5 padding) - (Medium) (CVE-2023-46809, bsc#1219997) + * CVE-2024-22019.patch: http: Reading unprocessed HTTP request with + unbounded chunk extension allows DoS attacks- (High) + (CVE-2024-22019, bsc#1219993) + * CVE-2024-22025.patch: fix Denial of Service by resource exhaustion + in fetch() brotli decoding (CVE-2024-22025, bsc#1220014) + * CVE-2024-24758.patch: ignore proxy-authorization headers + (CVE-2024-24758, bsc#1220017) + * CVE-2024-24806.patch: fix improper domain lookup that + potentially leads to SSRF attacks (CVE-2024-24806, bsc#1220053) + ------------------------------------------------------------------- Tue Oct 17 11:52:34 UTC 2023 - Adam Majer -- CVE-2023-38552.patch: Integrity checks according to policies can be circumvented (CVE-2023-38552, bsc#1216272) -- CVE-2023-39333.patch: Code injection via WebAssembly export names (CVE-2023-39333, bsc#1216273) +- CVE-2023-38552.patch: Integrity checks according to policies + can be circumvented (CVE-2023-38552, bsc#1216272) +- CVE-2023-39333.patch, wasm-fixture.tar.gz: Code injection via + WebAssembly export names (CVE-2023-39333, bsc#1216273) - CVE-2023-44487.patch: nghttp2 Security Release (CVE-2023-44487, bsc#1216190) -- CVE-2023-45143.patch: undici Security Release (CVE-2023-39333, bsc#1216273) +- CVE-2023-45143.patch: undici Security Release (CVE-2023-45143, bsc#1216205) +- nodejs.keyring: include new releaser keys ------------------------------------------------------------------- Thu Aug 10 14:33:17 UTC 2023 - Adam Majer diff --git a/nodejs16.spec b/nodejs16.spec index b7572eb..68fae47 100644 --- a/nodejs16.spec +++ b/nodejs16.spec @@ -1,7 +1,7 @@ # # spec file for package nodejs16 # -# Copyright (c) 2022 SUSE LLC +# Copyright (c) 2024 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -49,6 +49,11 @@ Release: 0 %bcond_with libalternatives %endif +# nodejs20+ is not for SLE12 +%if %{node_version_number} >= 20 && 0%{?suse_version} > 0 && 0%{?suse_version} < 1500 +ExclusiveArch do_not_build +%endif + %if %node_version_number >= 12 %define openssl_req_ver 1.1.1 %else @@ -135,9 +140,9 @@ Source3: nodejs.keyring Source5: node-gyp_7.1.2.tar.xz # Only required to run unit tests in NodeJS 10+ Source10: update_npm_tarball.sh -Source11: node_modules.tar.xz +Source11: node_modules.tar.xz +Source12: wasm-fixtures.tar.gz Source20: bash_output_helper.bash -Source21: wasm-fixtures.tar.gz ## Patches not distribution specific Patch1: cares_public_headers.patch @@ -151,7 +156,11 @@ Patch68: CVE-2023-44487.patch Patch69: CVE-2023-38552.patch Patch70: CVE-2023-39333.patch Patch71: CVE-2023-45143.patch - +Patch72: CVE-2024-24806.patch +Patch73: CVE-2024-24758.patch +Patch74: CVE-2024-22025.patch +Patch75: CVE-2024-22019.patch +Patch76: CVE-2023-46809.patch ## Patches specific to SUSE and openSUSE Patch100: linker_lto_jobs.patch @@ -382,6 +391,7 @@ Provides: bundled(llhttp) = 6.0.11 Provides: bundled(ngtcp2) = 0.8.1 Provides: bundled(base64) = 0.4.0 + # bundled url-ada parser, not ada @@ -639,11 +649,15 @@ The API documentation for the Node.js JavaScript runtime. %prep %if ! %{git_node} echo "`grep node-v%{version}.tar.xz %{S:1} | head -n1 | cut -c1-64` %{S:0}" | sha256sum -c -%setup -q -n node-v%{version} -a21 +%setup -q -n node-v%{version} %else %setup -q -n node-%{version} %endif +%if %{node_version_number} == 16 +tar zxf %{S:12} +%endif + %if %{node_version_number} <= 10 rm -r deps/npm/* pushd deps/npm @@ -665,6 +679,7 @@ tar Jxf %{SOURCE5} popd %if %{node_version_number} >= 19 +%else %endif %endif @@ -681,9 +696,16 @@ popd %patch69 -p1 %patch70 -p1 %patch71 -p1 +%patch72 -p1 +%patch73 -p1 +%patch74 -p1 +%patch75 -p1 +%patch76 -p1 %patch100 -p1 %patch101 -p1 +%if 0%{?suse_version} >= 1500 || 0%{?suse_version} == 0 %patch102 -p1 +%endif # Add check_output to configure script (not part of Python 2.6 in SLE11). %if 0%{?suse_version} == 1110 %endif