Version in base suite: 20.19.2+dfsg-1+deb13u2 Base version: nodejs_20.19.2+dfsg-1+deb13u2 Target version: nodejs_20.19.2+dfsg-1+deb13u3 Base file: /srv/ftp-master.debian.org/ftp/pool/main/n/nodejs/nodejs_20.19.2+dfsg-1+deb13u2.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/n/nodejs/nodejs_20.19.2+dfsg-1+deb13u3.dsc changelog | 82 ++ patches/build/openssl_tests.patch | 64 + patches/sec/CVE-2026-48617.patch | 112 ++ patches/sec/CVE-2026-48618.patch | 83 ++ patches/sec/CVE-2026-48619.patch | 246 ++++++ patches/sec/CVE-2026-48928.patch | 37 patches/sec/CVE-2026-48930.patch | 162 ++++ patches/sec/CVE-2026-48931.patch | 169 ++++ patches/sec/CVE-2026-48933.patch | 122 +++ patches/sec/CVE-2026-48934.patch | 173 ++++ patches/sec/CVE-2026-48935.patch | 64 + patches/sec/CVE-2026-48937.patch | 222 +++++ patches/sec/CVE-2026-48937_pre1.patch | 363 +++++++++ patches/sec/CVE-2026-56846.patch | 192 +++++ patches/sec/CVE-2026-56847.patch | 1296 ++++++++++++++++++++++++++++++++++ patches/sec/CVE-2026-56848.patch | 116 +++ patches/sec/CVE-2026-56850.patch | 183 ++++ patches/sec/CVE-2026-58039.patch | 195 +++++ patches/sec/CVE-2026-58040.patch | 237 ++++++ patches/sec/CVE-2026-58042.patch | 216 +++++ patches/sec/CVE-2026-58043.patch | 105 ++ patches/series | 20 22 files changed, 4459 insertions(+) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmps26tlv2y/nodejs_20.19.2+dfsg-1+deb13u2.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmps26tlv2y/nodejs_20.19.2+dfsg-1+deb13u3.dsc: no acceptable signature found diff -Nru nodejs-20.19.2+dfsg/debian/changelog nodejs-20.19.2+dfsg/debian/changelog --- nodejs-20.19.2+dfsg/debian/changelog 2026-03-24 21:11:25.000000000 +0000 +++ nodejs-20.19.2+dfsg/debian/changelog 2026-09-22 17:12:18.000000000 +0000 @@ -1,3 +1,85 @@ +nodejs (20.19.2+dfsg-1+deb13u3) trixie-security; urgency=medium + + * Team upload + * Fix CVE-2026-48617: + A flaw in Node.js Permission Model enforcement allows Bypass + via `process.report.writeReport()` Path Misvalidation. + This can lead to confidentiality impact or bypass of the + intended security boundary under affected configurations. + * Fix CVE-2026-48618: + A flaw in Node.js TLS hostname handling can cause Node.js unicode + dot separator handling can lead to tls wildcard-depth + authentication bypass due to resolver and verifier hostname + normalization mismat. This can lead to confidentiality impact + or bypass of the intended security boundary under + affected configurations. + * Fix CVE-2026-48619: + A malicious HTTP/2 server can send repeated ORIGIN frames with unique + origins, causing unbounded growth of the client-side originSet for the + lifetime of the session. Cap the set at 128 entries; once full, new + origins from ORIGIN frames are silently dropped. + * Fix CVE-2026-48928: case-sensitive SNI context matching + The regex constructed by server.addContext() lacked the case-insensitive + flag, causing uppercase or mixed-case SNI hostnames from ClientHello to + miss their intended context and fall back to the default context. This + violates RFC 6066 Section 3, which states that DNS hostnames are + case-insensitive. In mTLS configurations with per-tenant contexts, this + allowed bypassing client certificate authorization by simply + uppercasing the SNI hostname. + * Fix CVE-2026-48930: + A flaw in Node.js TLS hostname handling can cause Embedded-nul hostnames + can lead to silent authority rebinding due to c-string truncation + in resolver bindings. + * Fix CVE-2026-48931: + HTTP Agent can cause a client to accept as valid a response + that is send before the client has sent the request. + * Fix CVE-2026-48933: + A flaw in Node.js WebCrypto implementation can crash the process + if the input of `subtle.encrypt()` is a multiple of 2GiB. + * Fix CVE-2026-48934: + A flaw in Node.js TLS host verification can cause an attacker + to bypass certification validation. + * Fix CVE-2026-48935: + A flaw in Node.js Permission API can cause a file metadata + to be modified even on a path that was set as read-only + with e.g. --allow-fs-read. + * Fix CVE-2026-48937: + A flaw in Node.js HTTP/2 server API can cause servers + to keep accepting data even after sending a `GOAWAY` frame. + * Fix CVE-2026-56846 + A flaw in Node.js HTTP/2 handling can cause HTTP/2 retained + header blocks evade maxSessionMemory + and enable remote memory exhaustion. + * Fix CVE-2026-56847: + A flaw in Node.js Permission Model enforcement allows + trace_events.createTracing().enable() Writes Trace Logs + Outside --allow-fs-write. + * Fix CVE-2026-56848: + A flaw in Node.js HTTP/2 handling allows `nghttp2_session_mem_send()` + to be called re-entrantly while `nghttp2_session_mem_recv()` is executing, + resulting in a heap-use-after-free. + * Fix CVE-2026-56850: + A flaw in Node.js HTTPS Agent connection reuse can cause PFX object-array + key collisions, allowing mutual TLS (mTLS) client identities to be + reused across requests configured with different client certificates. + * Fix CVE-2026-58039: + A flaw in Node.js Permission Model enforcement allows process.report writes + (and overwrites) files outside --allow-fs-write paths. + This can lead to confidentiality impact or bypass of the intended + security boundary under affected configurations + * Fix CVE-2026-58043! + A flaw in Node.js Permission Model enforcement can over-grant + filesystem access across radix-tree prefix boundaries. + Under `--permission`, an attacker who is granted access to one + path can abuse boundary handling to read from or write to paths + outside the intended filesystem allowlist. + * Fix CVE-2026-58040: + An incomplete fix has been identified in Node.js: HTTPS Agent + TLS session reuse skips hostname verification across identity policies + (incomplete fix of CVE-2026-48934). + + -- Bastien Roucariès Tue, 22 Sep 2026 19:12:18 +0200 + nodejs (20.19.2+dfsg-1+deb13u2) trixie-security; urgency=medium * Upstream security patches: diff -Nru nodejs-20.19.2+dfsg/debian/patches/build/openssl_tests.patch nodejs-20.19.2+dfsg/debian/patches/build/openssl_tests.patch --- nodejs-20.19.2+dfsg/debian/patches/build/openssl_tests.patch 1970-01-01 00:00:00.000000000 +0000 +++ nodejs-20.19.2+dfsg/debian/patches/build/openssl_tests.patch 2026-09-22 17:02:25.000000000 +0000 @@ -0,0 +1,64 @@ +Description: Broaden OpenSSL version support in those tests +Origin: https://github.com/nodejs/node/tree/v24.x/test/parallel/test-tls-junk-server.js + https://github.com/nodejs/node/tree/v24.x/test/parallel/test-tls-alert-handling.js +Last-Update: 2026-09-11 +--- a/test/parallel/test-tls-alert-handling.js ++++ b/test/parallel/test-tls-alert-handling.js +@@ -31,17 +31,12 @@ + let iter = 0; + + const errorHandler = common.mustCall((err) => { +- let expectedErrorCode = 'ERR_SSL_WRONG_VERSION_NUMBER'; +- let expectedErrorReason = 'wrong version number'; +- if (common.hasOpenSSL(3, 2)) { +- expectedErrorCode = 'ERR_SSL_PACKET_LENGTH_TOO_LONG'; +- expectedErrorReason = 'packet length too long'; +- } +- +- assert.strictEqual(err.code, expectedErrorCode); ++ assert.match(err.code, ++ /ERR_SSL_(WRONG_VERSION_NUMBER|PACKET_LENGTH_TOO_LONG|BAD_RECORD_TYPE)/); + assert.strictEqual(err.library, 'SSL routines'); + if (!common.hasOpenSSL3) assert.strictEqual(err.function, 'ssl3_get_record'); +- assert.strictEqual(err.reason, expectedErrorReason); ++ assert.match(err.reason, ++ /wrong[\s_]version[\s_]number|packet[\s_]length[\s_]too[\s_]long|bad[\s_]record[\s_]type/i); + errorReceived = true; + if (canCloseServer()) + server.close(); +@@ -94,16 +89,12 @@ + }); + })); + client.on('error', common.mustCall((err) => { +- let expectedErrorCode = 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION'; +- let expectedErrorReason = 'tlsv1 alert protocol version'; +- if (common.hasOpenSSL(3, 2)) { +- expectedErrorCode = 'ERR_SSL_TLSV1_ALERT_RECORD_OVERFLOW'; +- expectedErrorReason = 'tlsv1 alert record overflow'; +- } +- assert.strictEqual(err.code, expectedErrorCode); ++ assert.match(err.code, ++ /ERR_SSL_(TLSV1_ALERT_PROTOCOL_VERSION|TLSV1_ALERT_RECORD_OVERFLOW|(SSL\/)?TLS_ALERT_UNEXPECTED_MESSAGE)/); + assert.strictEqual(err.library, 'SSL routines'); + if (!common.hasOpenSSL3) + assert.strictEqual(err.function, 'ssl3_read_bytes'); +- assert.strictEqual(err.reason, expectedErrorReason); ++ assert.match(err.reason, ++ /tlsv1[\s_]alert[\s_]protocol[\s_]version|tlsv1[\s_]alert[\s_]record[\s_]overflow|(ssl\/)?tls[\s_]alert[\s_]unexpected[\s_]message/i); + })); + } +--- a/test/parallel/test-tls-junk-server.js ++++ b/test/parallel/test-tls-junk-server.js +@@ -20,10 +20,8 @@ + const req = https.request({ port: this.address().port }); + req.end(); + +- let expectedErrorMessage = new RegExp('wrong version number'); +- if (common.hasOpenSSL(3, 2)) { +- expectedErrorMessage = new RegExp('packet length too long'); +- } ++ const expectedErrorMessage = ++ /wrong[ _]version[ _]number|packet length too long|bad record type/i; + req.once('error', common.mustCall(function(err) { + assert(expectedErrorMessage.test(err.message)); + server.close(); diff -Nru nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48617.patch nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48617.patch --- nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48617.patch 1970-01-01 00:00:00.000000000 +0000 +++ nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48617.patch 2026-08-31 15:10:46.000000000 +0000 @@ -0,0 +1,112 @@ +From: RafaelGSS +Date: Mon, 11 May 2026 18:25:16 -0300 +Subject: permission: handle process.chdir on writereport + +Signed-off-by: RafaelGSS +PR-URL: https://github.com/nodejs-private/node-private/pull/870 +Reviewed-By: Antoine du Hamel +CVE-ID: CVE-2026-48617 +Refs: https://hackerone.com/reports/3625987 +bug: https://nodejs.org/en/blog/vulnerability/june-2026-security-releases#permission-model-bypass-via-processreportwritereport-path-misvalidation-cve-2026-48617---low +origin: backport, https://github.com/nodejs/node/commit/2f62693801a12bc8a485b3b7da3239ac522f607d +--- + lib/internal/process/report.js | 13 ++++++++ + test/parallel/test-permission-fs-write-report.js | 42 ++++++++++++++++++++++++ + 2 files changed, 55 insertions(+) + +diff --git a/lib/internal/process/report.js b/lib/internal/process/report.js +index 1639142..7adaeac 100644 +--- a/lib/internal/process/report.js ++++ b/lib/internal/process/report.js +@@ -1,8 +1,10 @@ + 'use strict'; + const { ++ ERR_ACCESS_DENIED, + ERR_SYNTHETIC, + } = require('internal/errors').codes; + const { getValidatedPath } = require('internal/fs/utils'); ++const permission = require('internal/process/permission'); + const { + validateBoolean, + validateObject, +@@ -23,6 +25,17 @@ const report = { + file = getValidatedPath(file); + } + ++ if (permission.isEnabled()) { ++ const resource = file ?? process.cwd(); ++ if (!permission.has('fs.write', resource)) { ++ throw new ERR_ACCESS_DENIED( ++ 'Access to this API has been restricted', ++ 'FileSystemWrite', ++ resource, ++ ); ++ } ++ } ++ + if (err === undefined) { + err = new ERR_SYNTHETIC(); + } else { +diff --git a/test/parallel/test-permission-fs-write-report.js b/test/parallel/test-permission-fs-write-report.js +index c8f6673..190f646 100644 +--- a/test/parallel/test-permission-fs-write-report.js ++++ b/test/parallel/test-permission-fs-write-report.js +@@ -1,12 +1,28 @@ + // Flags: --experimental-permission --allow-fs-read=* + 'use strict'; + ++const { spawnSyncAndExitWithoutError } = require('../common/child_process'); ++ + const common = require('../common'); + common.skipIfWorker(); + if (!common.hasCrypto) + common.skip('no crypto'); + ++// We need to define the flags dynamically to account for the `NODE_TEST_DIR` env var. ++if (!process.permission) { ++ spawnSyncAndExitWithoutError(process.execPath, [ ++ '--permission', ++ '--allow-fs-read=*', `--allow-fs-write=${process.env.NODE_TEST_DIR || './test'}/.tmp.*`, '--allow-child-process', ++ __filename, ++ ]); ++ return; ++} ++ + const assert = require('assert'); ++const path = require('path'); ++const tmpdir = require('../common/tmpdir'); ++ ++tmpdir.refresh(); + + { + assert.throws(() => { +@@ -27,3 +43,29 @@ const assert = require('assert'); + resource: process.cwd(), + })); + } ++ ++{ ++ const reportPath = path.join(tmpdir.path, 'report.json'); ++ spawnSyncAndExitWithoutError( ++ process.execPath, ++ [ ++ '--permission', ++ '--allow-fs-read=*', ++ `--allow-fs-write=${tmpdir.path}/*`, ++ '-e', ++ `process.report.writeReport(${JSON.stringify(reportPath)})`, ++ ] ++ ); ++} ++ ++spawnSyncAndExitWithoutError( ++ process.execPath, ++ [ ++ '--permission', ++ '--allow-fs-read=*', ++ `--allow-fs-write=${tmpdir.path}`, ++ '-e', ++ 'process.report.writeReport()', ++ ], ++ { cwd: tmpdir.path } ++); diff -Nru nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48618.patch nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48618.patch --- nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48618.patch 1970-01-01 00:00:00.000000000 +0000 +++ nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48618.patch 2026-08-31 15:10:46.000000000 +0000 @@ -0,0 +1,83 @@ +From: Matteo Collina +Date: Mon, 11 May 2026 13:02:28 +0200 +Subject: tls: normalize hostname for server identity checks + +Signed-off-by: Matteo Collina +PR-URL: https://github.com/nodejs-private/node-private/pull/869 +Backport-PR-URL: https://github.com/nodejs-private/node-private/pull/893 +Reviewed-By: Antoine du Hamel +CVE-ID: CVE-2026-48618 +Refs: https://hackerone.com/reports/3688064 +origin: backport, https://github.com/nodejs/node/commit/2197a47144f3356ab451c5dcd858a49eb5957a70 +--- + lib/tls.js | 21 ++++++++++++++------- + test/parallel/test-tls-check-server-identity.js | 9 +++++++++ + 2 files changed, 23 insertions(+), 7 deletions(-) + +diff --git a/lib/tls.js b/lib/tls.js +index 0c1b335..e470ad8 100644 +--- a/lib/tls.js ++++ b/lib/tls.js +@@ -50,6 +50,8 @@ const { canonicalizeIP } = internalBinding('cares_wrap'); + const _tls_common = require('_tls_common'); + const _tls_wrap = require('_tls_wrap'); + const { createSecurePair } = require('internal/tls/secure-pair'); ++const { domainToASCII } = require('internal/url'); ++const { validateString } = require('internal/validators'); + + // Allow {CLIENT_RENEG_LIMIT} client-initiated session renegotiations + // every {CLIENT_RENEG_WINDOW} seconds. An error event is emitted if more +@@ -264,6 +266,11 @@ exports.checkServerIdentity = function checkServerIdentity(hostname, cert) { + const ips = []; + + hostname = '' + hostname; ++ const hostnameASCII = domainToASCII(hostname); ++ ++ // Remove trailing dots for error messages and matching. ++ hostname = unfqdn(hostname); ++ const hostnameASCIIWithoutFQDN = unfqdn(hostnameASCII); + + if (altNames) { + const splitAltNames = altNames.includes('"') ? +@@ -281,14 +288,14 @@ exports.checkServerIdentity = function checkServerIdentity(hostname, cert) { + let valid = false; + let reason = 'Unknown reason'; + +- hostname = unfqdn(hostname); // Remove trailing dot for error messages. +- +- if (net.isIP(hostname)) { +- valid = ips.includes(canonicalizeIP(hostname)); +- if (!valid) +- reason = `IP: ${hostname} is not in the cert's list: ` + ips.join(', '); ++ if (net.isIP(hostnameASCIIWithoutFQDN)) { ++ valid = ips.includes(canonicalizeIP(hostnameASCIIWithoutFQDN)); ++ if (!valid) { ++ reason = ++ `IP: ${hostname} is not in the cert's list: ` + ips.join(', '); ++ } + } else if (dnsNames.length > 0 || subject?.CN) { +- const hostParts = splitHost(hostname); ++ const hostParts = splitHost(hostnameASCIIWithoutFQDN); + const wildcard = (pattern) => check(hostParts, pattern, true); + + if (dnsNames.length > 0) { +diff --git a/test/parallel/test-tls-check-server-identity.js b/test/parallel/test-tls-check-server-identity.js +index fe81fc5..3dc0275 100644 +--- a/test/parallel/test-tls-check-server-identity.js ++++ b/test/parallel/test-tls-check-server-identity.js +@@ -313,6 +313,15 @@ const tests = [ + error: 'Host: localhost. is not in the cert\'s altnames: ' + + 'DNS:a.com' + }, ++ { ++ host: 'foo。bar.example.com', ++ cert: { ++ subjectaltname: 'DNS:*.example.com', ++ subject: {} ++ }, ++ error: 'Host: foo。bar.example.com. is not in the cert\'s altnames: ' + ++ 'DNS:*.example.com' ++ }, + // IDNA + { + host: 'xn--bcher-kva.example.com', diff -Nru nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48619.patch nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48619.patch --- nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48619.patch 1970-01-01 00:00:00.000000000 +0000 +++ nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48619.patch 2026-08-31 15:10:46.000000000 +0000 @@ -0,0 +1,246 @@ +From: Matteo Collina +Date: Sun, 19 Apr 2026 16:48:43 +0200 +Subject: http2: cap originSet size to prevent unbounded memory growth + +A malicious HTTP/2 server can send repeated ORIGIN frames with unique +origins, causing unbounded growth of the client-side originSet for the +lifetime of the session. Cap the set at 128 entries; once full, new +origins from ORIGIN frames are silently dropped. + +Ref: https://hackerone.com/reports/3676863 +PR-URL: https://github.com/nodejs-private/node-private/pull/855 +Reviewed-By: Antoine du Hamel +CVE-ID: CVE-2026-48619 +Refs: https://hackerone.com/reports/3676863 +origin: https://github.com/nodejs/node/commit/c79968e108002c2394bdb9e9cefb2c8c8cc202f8 +origin: https://github.com/nodejs/node/commit/c79968e108002c2394bdb9e9cefb2c8c8cc202f8 +--- + doc/api/errors.md | 11 +++ + doc/api/http2.md | 2 + + lib/internal/errors.js | 2 + + lib/internal/http2/core.js | 17 +++- + test/parallel/test-http2-origin-set-max-size.mjs | 110 +++++++++++++++++++++++ + 5 files changed, 141 insertions(+), 1 deletion(-) + create mode 100644 test/parallel/test-http2-origin-set-max-size.mjs + +diff --git a/doc/api/errors.md b/doc/api/errors.md +index d6ead7e..95fa835 100644 +--- a/doc/api/errors.md ++++ b/doc/api/errors.md +@@ -1729,6 +1729,17 @@ added: v15.14.0 + The limit of acceptable invalid HTTP/2 protocol frames sent by the peer, + as specified through the `maxSessionInvalidFrames` option, has been exceeded. + ++ ++ ++### `ERR_HTTP2_TOO_MANY_ORIGINS` ++ ++ ++ ++The number of uniq origin sent by the server has exceeded the value defined in ++`options.maxOriginSetSize`. ++ + + + ### `ERR_HTTP2_TRAILERS_ALREADY_SENT` +diff --git a/doc/api/http2.md b/doc/api/http2.md +index 772596c..287e4df 100644 +--- a/doc/api/http2.md ++++ b/doc/api/http2.md +@@ -3168,6 +3168,8 @@ changes: + This is similar to [`server.maxHeadersCount`][] or + [`request.maxHeadersCount`][] in the `node:http` module. The minimum value + is `1`. **Default:** `128`. ++ * `maxOriginSetSize` {number} Sets the maximum number of uniq origin the sever ++ can send via ORIGIN frames. **Default:** `128`. + * `maxOutstandingPings` {number} Sets the maximum number of outstanding, + unacknowledged pings. **Default:** `10`. + * `maxReservedRemoteStreams` {number} Sets the maximum number of reserved push +diff --git a/lib/internal/errors.js b/lib/internal/errors.js +index 262f99e..82ae970 100644 +--- a/lib/internal/errors.js ++++ b/lib/internal/errors.js +@@ -1315,6 +1315,8 @@ E('ERR_HTTP2_STREAM_SELF_DEPENDENCY', + E('ERR_HTTP2_TOO_MANY_CUSTOM_SETTINGS', + 'Number of custom settings exceeds MAX_ADDITIONAL_SETTINGS', Error); + E('ERR_HTTP2_TOO_MANY_INVALID_FRAMES', 'Too many invalid HTTP/2 frames', Error); ++E('ERR_HTTP2_TOO_MANY_ORIGINS', ++ 'The server sent more ORIGIN frames than the allowed number of %s', Error); + E('ERR_HTTP2_TRAILERS_ALREADY_SENT', + 'Trailing headers have already been sent', Error); + E('ERR_HTTP2_TRAILERS_NOT_READY', +diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js +index cc08f81..93d36d0 100644 +--- a/lib/internal/http2/core.js ++++ b/lib/internal/http2/core.js +@@ -101,6 +101,7 @@ const { + ERR_HTTP2_STREAM_ERROR, + ERR_HTTP2_STREAM_SELF_DEPENDENCY, + ERR_HTTP2_TOO_MANY_CUSTOM_SETTINGS, ++ ERR_HTTP2_TOO_MANY_ORIGINS, + ERR_HTTP2_TRAILERS_ALREADY_SENT, + ERR_HTTP2_TRAILERS_NOT_READY, + ERR_HTTP2_UNSUPPORTED_PROTOCOL, +@@ -236,6 +237,7 @@ const kInit = Symbol('init'); + const kInfoHeaders = Symbol('sent-info-headers'); + const kLocalSettings = Symbol('local-settings'); + const kNativeFields = Symbol('kNativeFields'); ++const kMaxOriginSetSize = Symbol('max-ORIGIN-set-size'); + const kOptions = Symbol('options'); + const kOwner = owner_symbol; + const kOrigin = Symbol('origin'); +@@ -673,8 +675,13 @@ function onOrigin(origins) { + if (!session.encrypted || session.destroyed) + return undefined; + const originSet = initOriginSet(session); +- for (let n = 0; n < origins.length; n++) ++ for (let n = 0; n < origins.length; n++) { ++ if (originSet.size >= session[kMaxOriginSetSize]) { ++ session.destroy(new ERR_HTTP2_TOO_MANY_ORIGINS(session[kMaxOriginSetSize])); ++ return; ++ } + originSet.add(origins[n]); ++ } + session.emit('origin', origins); + } + +@@ -3323,6 +3330,13 @@ function connect(authority, options, listener) { + assertIsObject(options, 'options'); + options = { ...options }; + ++ let { maxOriginSetSize } = options; ++ if (maxOriginSetSize != null) { ++ validateNumber(maxOriginSetSize, 'options.maxOriginSetSize', 0); ++ } else { ++ maxOriginSetSize = 128; ++ } ++ + assertIsArray(options.remoteCustomSettings, 'options.remoteCustomSettings'); + if (options.remoteCustomSettings) { + options.remoteCustomSettings = [ ...options.remoteCustomSettings ]; +@@ -3369,6 +3383,7 @@ function connect(authority, options, listener) { + + session[kAuthority] = `${options.servername || host}:${port}`; + session[kProtocol] = protocol; ++ session[kMaxOriginSetSize] = maxOriginSetSize; + + if (typeof listener === 'function') + session.once('connect', listener); +diff --git a/test/parallel/test-http2-origin-set-max-size.mjs b/test/parallel/test-http2-origin-set-max-size.mjs +new file mode 100644 +index 0000000..08bdf1e +--- /dev/null ++++ b/test/parallel/test-http2-origin-set-max-size.mjs +@@ -0,0 +1,110 @@ ++import { ++ expectsError, ++ hasCrypto, ++ mustCall, ++ mustNotCall, ++ mustSucceed, ++ skip, ++} from '../common/index.mjs'; ++import * as fixtures from '../common/fixtures.mjs'; ++import assert from 'node:assert'; ++ ++if (!hasCrypto) ++ skip('missing crypto'); ++ ++const { ++ createSecureServer, ++ connect, ++} = await import('node:http2'); ++ ++const key = fixtures.readKey('agent8-key.pem', 'binary'); ++const cert = fixtures.readKey('agent8-cert.pem', 'binary'); ++const ca = fixtures.readKey('fake-startcom-root-cert.pem', 'binary'); ++ ++const server = createSecureServer({ key, cert }); ++server.on('stream', (stream) => { ++ stream.respond(); ++ stream.end('ok'); ++}); ++server.on('session', (session) => { ++ let i = 0; ++ const timer = setInterval(() => { ++ try { ++ session.origin(...Array.from({ length: 10 }, () => `https://o${i++}.example.com`)); ++ } catch { ++ clearInterval(timer); ++ } ++ }, 10); ++ ++ session.on('close', () => { ++ clearInterval(timer); ++ }); ++}); ++ ++await new Promise((resolve) => server.listen(0, resolve)); ++ ++// Test fantasist values ++[Symbol(), '0', 1n, {}, [], true, false, /s/, () => {}].forEach((maxOriginSetSize) => { ++ assert.throws( ++ () => connect(`https://localhost:${server.address().port}`, { ca, maxOriginSetSize }), ++ { code: 'ERR_INVALID_ARG_TYPE' }, ++ ); ++}); ++[NaN, -1].forEach((maxOriginSetSize) => { ++ assert.throws( ++ () => connect(`https://localhost:${server.address().port}`, { ca, maxOriginSetSize }), ++ { code: 'ERR_OUT_OF_RANGE' }, ++ ); ++}); ++ ++await new Promise((resolve) => server.getConnections(mustSucceed((count) => { ++ assert.strictEqual(count, 0); ++ resolve(); ++}))); ++ ++// Test default value ++await new Promise((resolve) => { ++ const client = connect(`https://localhost:${server.address().port}`, { ca }); ++ ++ client.on('origin', mustCall(12)); // Default value is 128, the first 12 frames should pass, the 13th one should error ++ client.on('error', expectsError({ ++ code: 'ERR_HTTP2_TOO_MANY_ORIGINS', ++ })); ++ client.on('goaway', mustNotCall()); ++ client.on('close', resolve); ++ ++ client.request().resume(); ++}); ++ ++// Test non-default values ++await Promise.all([-0, 9, 1.5].map((maxOriginSetSize) => new Promise((resolve) => { ++ const client = connect(`https://localhost:${server.address().port}`, { ca, maxOriginSetSize }); ++ ++ client.on('origin', mustNotCall()); // The server send 10 origins on the first frame, that's already too many. ++ client.on('error', expectsError({ ++ code: 'ERR_HTTP2_TOO_MANY_ORIGINS', ++ })); ++ client.on('goaway', mustNotCall()); ++ client.on('close', resolve); ++ ++ client.request().resume(); ++}))); ++ ++ ++// Test values higher than the default value ++await Promise.all([512, Infinity].map((maxOriginSetSize) => new Promise((resolve) => { ++ const client = connect(`https://localhost:${server.address().port}`, { ca, maxOriginSetSize }); ++ ++ client.on('origin', mustCall(() => { ++ if (client.originSet.length > 128) { ++ client.destroy(); ++ } ++ }, 13)); ++ client.on('error', mustNotCall()); ++ client.on('goaway', mustNotCall()); ++ client.on('close', resolve); ++ ++ client.request().resume(); ++}))); ++ ++server.close(); diff -Nru nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48928.patch nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48928.patch --- nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48928.patch 1970-01-01 00:00:00.000000000 +0000 +++ nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48928.patch 2026-08-31 15:10:46.000000000 +0000 @@ -0,0 +1,37 @@ +From: Matteo Collina +Date: Mon, 20 Apr 2026 18:06:29 +0200 +Subject: tls: fix case-sensitive SNI context matching + +The regex constructed by server.addContext() lacked the case-insensitive +flag, causing uppercase or mixed-case SNI hostnames from ClientHello to +miss their intended context and fall back to the default context. This +violates RFC 6066 Section 3, which states that DNS hostnames are +case-insensitive. In mTLS configurations with per-tenant contexts, this +allowed bypassing client certificate authorization by simply +uppercasing the SNI hostname. + +Add the 'i' flag to the RegExp in addContext() so that SNI matching +is case-insensitive. + +PR-URL: https://github.com/nodejs-private/node-private/pull/857 +Reviewed-By: Antoine du Hamel +CVE-ID: CVE-2026-48928 +Refs: https://hackerone.com/reports/3656869 +origin: backport, https://github.com/nodejs/node/commit/39d1d0968471a144d93dc293d640008f57d3c58e +--- + lib/_tls_wrap.js | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js +index 33d5ae8..6ffea12 100644 +--- a/lib/_tls_wrap.js ++++ b/lib/_tls_wrap.js +@@ -1614,7 +1614,7 @@ Server.prototype.addContext = function(servername, context) { + servername + .replace(/([.^$+?\-\\[\]{}])/g, '\\$1') + .replaceAll('*', '[^.]*') +- }$`); ++ }$`,'i'); + + const secureContext = + context instanceof common.SecureContext ? context : tls.createSecureContext(context); diff -Nru nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48930.patch nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48930.patch --- nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48930.patch 1970-01-01 00:00:00.000000000 +0000 +++ nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48930.patch 2026-08-31 15:10:46.000000000 +0000 @@ -0,0 +1,162 @@ +From: Matteo Collina +Date: Mon, 11 May 2026 09:29:15 +0200 +Subject: dns,net: reject hostnames with embedded NUL bytes + +Ref: https://hackerone.com/reports/3656716 +PR-URL: https://github.com/nodejs-private/node-private/pull/868 +Reviewed-By: Antoine du Hamel +CVE-ID: CVE-2026-48930 +Refs: https://hackerone.com/reports/3656716 +origin: backport, https://github.com/nodejs/node/commit/c551a51d0c58dfc91961fb3f24c2c86af6183eca +Bug: https://nodejs.org/en/blog/vulnerability/june-2026-security-releases#embedded-nul-hostnames-can-lead-to-silent-authority-rebinding-due-to-c-string-trunc +--- + lib/dns.js | 4 ++-- + lib/internal/dns/promises.js | 3 ++- + lib/internal/validators.js | 10 ++++++++++ + lib/net.js | 3 +++ + test/parallel/test-dns-lookup.js | 11 +++++++++++ + test/parallel/test-net-connect-options-invalid.js | 13 +++++++++++++ + 6 files changed, 41 insertions(+), 3 deletions(-) + +diff --git a/lib/dns.js b/lib/dns.js +index 448258d..a748a15 100644 +--- a/lib/dns.js ++++ b/lib/dns.js +@@ -82,7 +82,7 @@ const { + validateNumber, + validateOneOf, + validatePort, +- validateString, ++ validateStringWithoutNullBytes, + } = require('internal/validators'); + + const { +@@ -147,7 +147,7 @@ function lookup(hostname, options, callback) { + + // Parse arguments + if (hostname) { +- validateString(hostname, 'hostname'); ++ validateStringWithoutNullBytes(hostname, 'hostname'); + } + + if (typeof options === 'function') { +diff --git a/lib/internal/dns/promises.js b/lib/internal/dns/promises.js +index 602ff44..891380f 100644 +--- a/lib/internal/dns/promises.js ++++ b/lib/internal/dns/promises.js +@@ -67,6 +67,7 @@ const { + validateOneOf, + validatePort, + validateString, ++ validateStringWithoutNullBytes, + } = require('internal/validators'); + + const kPerfHooksDnsLookupContext = Symbol('kPerfHooksDnsLookupContext'); +@@ -198,7 +199,7 @@ function lookup(hostname, options) { + + // Parse arguments + if (hostname) { +- validateString(hostname, 'hostname'); ++ validateStringWithoutNullBytes(hostname, 'hostname'); + } + + if (typeof options === 'number') { +diff --git a/lib/internal/validators.js b/lib/internal/validators.js +index 56f5612..a7295f3 100644 +--- a/lib/internal/validators.js ++++ b/lib/internal/validators.js +@@ -16,6 +16,7 @@ const { + ObjectPrototypeHasOwnProperty, + RegExpPrototypeExec, + String, ++ StringPrototypeIncludes, + StringPrototypeToUpperCase, + StringPrototypeTrim, + } = primordials; +@@ -163,6 +164,14 @@ const validateString = hideStackFrames((value, name) => { + throw new ERR_INVALID_ARG_TYPE(name, 'string', value); + }); + ++/** @type {validateString} */ ++const validateStringWithoutNullBytes = hideStackFrames((value, name) => { ++ validateString(value, name); ++ if (StringPrototypeIncludes(value, '\u0000')) { ++ throw new ERR_INVALID_ARG_VALUE(name, value, 'must be a string without null bytes'); ++ } ++}); ++ + /** + * @callback validateNumber + * @param {*} value +@@ -636,6 +645,7 @@ module.exports = { + validatePort, + validateSignalName, + validateString, ++ validateStringWithoutNullBytes, + validateUint32, + validateUndefined, + validateUnion, +diff --git a/lib/net.js b/lib/net.js +index 15a4a6a..219ec11 100644 +--- a/lib/net.js ++++ b/lib/net.js +@@ -129,6 +129,7 @@ const { + validateNumber, + validatePort, + validateString, ++ validateStringWithoutNullBytes, + } = require('internal/validators'); + const kLastWriteQueueSize = Symbol('lastWriteQueueSize'); + const { getOptionValue } = require('internal/options'); +@@ -1287,6 +1288,8 @@ function lookupAndConnect(self, options) { + const host = options.host || 'localhost'; + let { port, autoSelectFamilyAttemptTimeout, autoSelectFamily } = options; + ++ validateStringWithoutNullBytes(host, 'options.host'); ++ + if (localAddress && !isIP(localAddress)) { + throw new ERR_INVALID_IP_ADDRESS(localAddress); + } +diff --git a/test/parallel/test-dns-lookup.js b/test/parallel/test-dns-lookup.js +index 404c555..b13b50d 100644 +--- a/test/parallel/test-dns-lookup.js ++++ b/test/parallel/test-dns-lookup.js +@@ -23,6 +23,17 @@ const dnsPromises = dns.promises; + assert.throws(() => dnsPromises.lookup(1, {}), err); + } + ++{ ++ const err = { ++ code: 'ERR_INVALID_ARG_VALUE', ++ name: 'TypeError', ++ message: /The argument 'hostname' must be a string without null bytes\./, ++ }; ++ ++ assert.throws(() => dns.lookup('127.0.0.1\u0000.allowed.example', {}), err); ++ assert.throws(() => dnsPromises.lookup('127.0.0.1\u0000.allowed.example', {}), err); ++} ++ + // This also verifies different expectWarning notations. + common.expectWarning({ + // For 'internal/test/binding' module. +diff --git a/test/parallel/test-net-connect-options-invalid.js b/test/parallel/test-net-connect-options-invalid.js +index 05a5654..a5b54b4 100644 +--- a/test/parallel/test-net-connect-options-invalid.js ++++ b/test/parallel/test-net-connect-options-invalid.js +@@ -25,3 +25,16 @@ const net = require('net'); + }); + }); + } ++ ++{ ++ assert.throws(() => { ++ net.createConnection({ ++ host: '127.0.0.1\u0000.allowed.example', ++ port: 8080, ++ }); ++ }, { ++ code: 'ERR_INVALID_ARG_VALUE', ++ name: 'TypeError', ++ message: /The property 'options\.host' must be a string without null bytes\./, ++ }); ++} diff -Nru nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48931.patch nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48931.patch --- nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48931.patch 1970-01-01 00:00:00.000000000 +0000 +++ nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48931.patch 2026-08-31 15:10:47.000000000 +0000 @@ -0,0 +1,169 @@ +From: Matteo Collina +Date: Sun, 29 Mar 2026 17:42:54 +0200 +Subject: http: fix response queue poisoning in http.Agent + +Attach a data guard listener on idle keepAlive sockets in the +freeSockets pool. If unsolicited data arrives while the socket +is idle, destroy it immediately to prevent response queue poisoning. + +Refs: https://hackerone.com/reports/3582376 +PR-URL: https://github.com/nodejs-private/node-private/pull/846 +Reviewed-By: Antoine du Hamel +CVE-ID: CVE-2026-48931 +origin: https://github.com/nodejs/node/commit/0a22d40180cb796e0d68e94c1a7a8a05a8f47c10 +bug: https://nodejs.org/en/blog/vulnerability/june-2026-security-releases#http-response-queue-poisoning-via-toctou-race-condition-in-httpagent-cve-2026-48931---low +--- + lib/_http_agent.js | 13 ++++ + .../test-http-agent-free-socket-data-guard.js | 90 ++++++++++++++++++++++ + test/parallel/test-http-agent-keepalive.js | 3 +- + 3 files changed, 105 insertions(+), 1 deletion(-) + create mode 100644 test/parallel/test-http-agent-free-socket-data-guard.js + +diff --git a/lib/_http_agent.js b/lib/_http_agent.js +index 17f5d10..4c1aa62 100644 +--- a/lib/_http_agent.js ++++ b/lib/_http_agent.js +@@ -78,6 +78,16 @@ function freeSocketErrorListener(err) { + socket.emit('agentRemove'); + } + ++// Guard against unsolicited data arriving while a socket is idle in the ++// freeSockets pool. When the HTTPParser is detached the data would sit ++// in the TCP buffer and be silently consumed as the response for the ++// *next* request that reuses the socket (response-queue poisoning). ++// See: https://hackerone.com/reports/3582376 ++function freeSocketDataGuard() { ++ debug('DATA on FREE socket - destroying poisoned socket'); ++ this.destroy(); ++} ++ + function Agent(options) { + if (!(this instanceof Agent)) + return new Agent(options); +@@ -175,6 +185,8 @@ function Agent(options) { + this.removeSocket(socket, options); + + socket.once('error', freeSocketErrorListener); ++ socket.on('data', freeSocketDataGuard); ++ socket.resume(); + freeSockets.push(socket); + }); + +@@ -510,6 +522,7 @@ Agent.prototype.keepSocketAlive = function keepSocketAlive(socket) { + Agent.prototype.reuseSocket = function reuseSocket(socket, req) { + debug('have free socket'); + socket.removeListener('error', freeSocketErrorListener); ++ socket.removeListener('data', freeSocketDataGuard); + req.reusedSocket = true; + socket.ref(); + }; +diff --git a/test/parallel/test-http-agent-free-socket-data-guard.js b/test/parallel/test-http-agent-free-socket-data-guard.js +new file mode 100644 +index 0000000..9c1a526 +--- /dev/null ++++ b/test/parallel/test-http-agent-free-socket-data-guard.js +@@ -0,0 +1,90 @@ ++'use strict'; ++ ++// Regression test for HackerOne report #3582376 ++// HTTP Response Queue Poisoning via TOCTOU Race Condition in http.Agent ++// ++// When keepAlive is true, there is a window between a socket entering the ++// freeSockets pool (parser detached) and being reassigned. If the server ++// writes a full HTTP response during this window, it is consumed as the ++// response for the *next* request — poisoning the response queue. ++// ++// The fix attaches a data guard listener + resume() on idle sockets so ++// that unsolicited data causes the socket to be destroyed. ++ ++const common = require('../common'); ++const assert = require('assert'); ++const http = require('http'); ++ ++let serverSocket; ++ ++const server = http.createServer(common.mustCall((req, res) => { ++ // Capture the raw socket on the first request ++ serverSocket ||= req.socket; ++ res.end(req.url); ++}, 2)); // Expect request1 and request2 ++ ++server.listen(0, common.mustCall(() => { ++ const agent = new http.Agent({ keepAlive: true }); ++ const options = { host: '127.0.0.1', port: server.address().port, agent }; ++ const name = agent.getName(options); ++ ++ // Step 1: Send request1 ++ const request1 = http.request({ ...options, path: '/request1' }); ++ request1.end(); ++ ++ request1.on('response', common.mustCall((response) => { ++ let body = ''; ++ response.setEncoding('utf8'); ++ response.on('data', (data) => { body += data; }); ++ response.on('end', common.mustCall(() => { ++ assert.strictEqual(body, '/request1'); ++ })); ++ })); ++ ++ request1.on('close', common.mustCall(() => { ++ // Use nextTick to ensure socket is in freeSockets ++ process.nextTick(common.mustCall(() => { ++ // Verify the socket is in the free pool with parser detached ++ assert.strictEqual(agent.freeSockets[name]?.length, 1); ++ const freeSocket = agent.freeSockets[name][0]; ++ assert.strictEqual(freeSocket.parser, null); ++ // With the fix, a data guard listener is attached ++ assert.strictEqual(freeSocket.listenerCount('data'), 1); ++ ++ // Step 2: Server injects a poisoned response while socket is idle ++ serverSocket.write( ++ 'HTTP/1.1 200 OK\r\n' + ++ 'X-Poisoned: true\r\n' + ++ 'Connection: keep-alive\r\n' + ++ 'Content-Length: 0\r\n' + ++ '\r\n' ++ ); ++ ++ // Step 3: Allow the event loop to poll I/O so the guard can fire. ++ // In a real attack, there is always time between the poison arriving ++ // and the next client request. setTimeout(0) runs after the I/O poll ++ // phase, giving the guard a chance to receive the poisoned data. ++ setTimeout(common.mustCall(() => { ++ // The guard should have destroyed the poisoned socket ++ assert.strictEqual(freeSocket.destroyed, true); ++ assert.strictEqual(agent.freeSockets[name], undefined); ++ ++ // Step 4: Send request2 — should get a fresh connection ++ const request2 = http.request({ ...options, path: '/request2' }); ++ request2.end(); ++ ++ request2.on('response', common.mustCall((response) => { ++ let body = ''; ++ response.setEncoding('utf8'); ++ response.on('data', (data) => { body += data; }); ++ response.on('end', common.mustCall(() => { ++ assert.strictEqual(response.headers['x-poisoned'], undefined); ++ assert.strictEqual(body, '/request2'); ++ agent.destroy(); ++ server.close(); ++ })); ++ })); ++ }), 50); ++ })); ++ })); ++})); +diff --git a/test/parallel/test-http-agent-keepalive.js b/test/parallel/test-http-agent-keepalive.js +index f742463..e4f5c09 100644 +--- a/test/parallel/test-http-agent-keepalive.js ++++ b/test/parallel/test-http-agent-keepalive.js +@@ -149,7 +149,8 @@ server.listen(0, common.mustCall(() => { + function checkListeners(socket) { + const callback = common.mustCall(() => { + if (!socket.destroyed) { +- assert.strictEqual(socket.listenerCount('data'), 0); ++ // Sockets have freeSocketDataGuard while in the free pool. ++ assert.strictEqual(socket.listenerCount('data'), 1); + assert.strictEqual(socket.listenerCount('drain'), 0); + // Sockets have freeSocketErrorListener. + assert.strictEqual(socket.listenerCount('error'), 1); diff -Nru nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48933.patch nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48933.patch --- nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48933.patch 1970-01-01 00:00:00.000000000 +0000 +++ nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48933.patch 2026-08-31 15:10:47.000000000 +0000 @@ -0,0 +1,122 @@ +From: Filip Skokan +Date: Mon, 25 May 2026 11:09:31 +0200 +Subject: crypto: guard WebCrypto cipher output length + +Reject WebCrypto cipher operations whose computed output length would +exceed INT_MAX before passing the length to OpenSSL. + +This avoids signed overflow in the AES and ChaCha20-Poly1305 one-shot +cipher paths and turns oversized inputs into a clean operation failure. + +Refs: https://hackerone.com/reports/3760016 +Signed-off-by: Filip Skokan +Backport-PR-URL: https://github.com/nodejs-private/node-private/pull/879 +Reviewed-By: Antoine du Hamel +PR-URL: https://github.com/nodejs-private/node-private/pull/878 +CVE-ID: CVE-2026-48933 +origin: backport, https://github.com/nodejs/node/commit/38b4c5ed51b2ec81c28fbd379fea72e22fa12a15 +bug: https://nodejs.org/en/blog/vulnerability/june-2026-security-releases#nodejs-webcrypto-aes-integer-overflow-leads-to-remote-process-abort-dos-cve-2026-48933---high +--- + src/crypto/crypto_aes.cc | 12 ++++++++++-- + src/crypto/crypto_cipher.h | 13 +++++++++++++ + test/cctest/test_node_crypto.cc | 19 ++++++++++++++++++- + 3 files changed, 41 insertions(+), 3 deletions(-) + +diff --git a/src/crypto/crypto_aes.cc b/src/crypto/crypto_aes.cc +index c56d435..f074f8b 100644 +--- a/src/crypto/crypto_aes.cc ++++ b/src/crypto/crypto_aes.cc +@@ -110,7 +110,15 @@ WebCryptoCipherStatus AES_Cipher( + } + + size_t total = 0; +- int buf_len = in.size() + EVP_CIPHER_CTX_block_size(ctx.get()) + tag_len; ++ const int block_size = EVP_CIPHER_CTX_block_size(ctx.get()); ++ if (block_size < 0) { ++ return WebCryptoCipherStatus::FAILED; ++ } ++ int buf_len; ++ if (!TryGetIntCipherOutputLength( ++ in.size(), static_cast(block_size) + tag_len, &buf_len)) { ++ return WebCryptoCipherStatus::FAILED; ++ } + int out_len; + + if (mode == EVP_CIPH_GCM_MODE && +@@ -146,7 +154,7 @@ WebCryptoCipherStatus AES_Cipher( + + total += out_len; + CHECK_LE(out_len, buf_len); +- out_len = EVP_CIPHER_CTX_block_size(ctx.get()); ++ out_len = block_size; + if (!EVP_CipherFinal_ex( + ctx.get(), buf.data() + total, &out_len)) { + return WebCryptoCipherStatus::FAILED; +diff --git a/src/crypto/crypto_cipher.h b/src/crypto/crypto_cipher.h +index e725a2f..9881109 100644 +--- a/src/crypto/crypto_cipher.h ++++ b/src/crypto/crypto_cipher.h +@@ -10,6 +10,7 @@ + #include "memory_tracker.h" + #include "v8.h" + ++#include + #include + + namespace node { +@@ -134,6 +135,18 @@ enum class WebCryptoCipherStatus { + FAILED + }; + ++inline bool TryGetIntCipherOutputLength(size_t input_len, ++ size_t output_overhead, ++ int* output_len) { ++ static constexpr size_t kMaxLength = INT_MAX; ++ if (output_overhead > kMaxLength || ++ input_len > kMaxLength - output_overhead) { ++ return false; ++ } ++ *output_len = static_cast(input_len + output_overhead); ++ return true; ++} ++ + // CipherJob is a base implementation class for implementations of + // one-shot sync and async ciphers. It has been added primarily to + // support the AES and RSA ciphers underlying the WebCrypt API. +diff --git a/test/cctest/test_node_crypto.cc b/test/cctest/test_node_crypto.cc +index 9d6405a..00dd630 100644 +--- a/test/cctest/test_node_crypto.cc ++++ b/test/cctest/test_node_crypto.cc +@@ -2,10 +2,13 @@ + // and setting it to a file that does not exist. + #define NODE_OPENSSL_SYSTEM_CERT_PATH "/missing/ca.pem" + ++#include "crypto/crypto_cipher.h" + #include "crypto/crypto_context.h" ++#include "gtest/gtest.h" + #include "node_options.h" + #include "openssl/err.h" +-#include "gtest/gtest.h" ++ ++#include + + /* + * This test verifies that a call to NewRootCertDir with the build time +@@ -21,3 +24,17 @@ TEST(NodeCrypto, NewRootCertStore) { + "any errors on the OpenSSL error stack\n"; + X509_STORE_free(store); + } ++ ++TEST(NodeCrypto, TryGetIntCipherOutputLength) { ++ int output_len = 0; ++ ++ EXPECT_TRUE( ++ node::crypto::TryGetIntCipherOutputLength(INT_MAX - 16, 16, &output_len)); ++ EXPECT_EQ(output_len, INT_MAX); ++ ++ EXPECT_FALSE( ++ node::crypto::TryGetIntCipherOutputLength(INT_MAX - 15, 16, &output_len)); ++ ++ EXPECT_FALSE(node::crypto::TryGetIntCipherOutputLength( ++ 0, static_cast(INT_MAX) + 1, &output_len)); ++} diff -Nru nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48934.patch nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48934.patch --- nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48934.patch 1970-01-01 00:00:00.000000000 +0000 +++ nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48934.patch 2026-08-31 15:10:47.000000000 +0000 @@ -0,0 +1,173 @@ +From: Matteo Collina +Date: Mon, 13 Apr 2026 09:53:48 +0200 +Subject: tls: bind reusable sessions to authenticated host + +Backport-PR-URL: https://github.com/nodejs-private/node-private/pull/895 +PR-URL: https://github.com/nodejs-private/node-private/pull/854 +Reviewed-By: Antoine du Hamel +CVE-ID: CVE-2026-48934 +Refs: https://hackerone.com/reports/3649802 +origin: backport, https://github.com/nodejs/node/commit/fd890ba01d508ac111bbba302981d7fdf734d2ce +bug: https://nodejs.org/en/blog/vulnerability/june-2026-security-releases#tls-host-identity-verification-bypass-via-session-reuse-with-different-servername-leads-to-unauthorized-connections-cve-2026-48934---medium +--- + lib/_tls_wrap.js | 109 +++++++++++++++++++++++++++++++++++++++++++++++++------ + 1 file changed, 98 insertions(+), 11 deletions(-) + +diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js +index 6ffea12..e273bfa 100644 +--- a/lib/_tls_wrap.js ++++ b/lib/_tls_wrap.js +@@ -111,6 +111,82 @@ const kIsVerified = Symbol('verified'); + + const noop = () => {}; + ++const kTLSSessionStatePrefix = Buffer.from('\0nodejs:tls:session:1\0'); ++ ++function getSessionServerIdentity(options) { ++ return options?.servername || ++ options?.host || ++ options?.socket?._host || ++ 'localhost'; ++} ++ ++function wrapSessionState(session, options) { ++ if (!Buffer.isBuffer(session) || options?.isServer) ++ return session; ++ ++ const servername = Buffer.from(getSessionServerIdentity(options), 'utf8'); ++ const servernameLength = Buffer.allocUnsafe(2); ++ servernameLength.writeUInt16BE(servername.length, 0); ++ ++ return Buffer.concat([ ++ kTLSSessionStatePrefix, ++ servernameLength, ++ servername, ++ session, ++ ]); ++} ++ ++function unwrapSessionState(session) { ++ if (!Buffer.isBuffer(session) || ++ session.length < kTLSSessionStatePrefix.length + 2 || ++ Buffer.compare( ++ session.subarray(0, kTLSSessionStatePrefix.length), ++ kTLSSessionStatePrefix, ++ ) !== 0) { ++ return; ++ } ++ ++ const start = kTLSSessionStatePrefix.length; ++ const servernameLength = session.readUInt16BE(start); ++ const servernameStart = start + 2; ++ const servernameEnd = servernameStart + servernameLength; ++ if (session.length < servernameEnd) ++ return; ++ ++ return { ++ servername: session.toString('utf8', servernameStart, servernameEnd), ++ session: session.subarray(servernameEnd), ++ }; ++} ++ ++function getSessionForReuse(session, options) { ++ if (typeof session === 'string') ++ session = Buffer.from(session, 'latin1'); ++ ++ if (options?.isServer) ++ return session; ++ ++ const wrappedSession = unwrapSessionState(session); ++ if (wrappedSession !== undefined) { ++ const servername = getSessionServerIdentity(options); ++ if (wrappedSession.servername !== servername) { ++ debug('ignore session for %s: authenticated for %s', ++ servername, wrappedSession.servername); ++ return; ++ } ++ ++ return wrappedSession.session; ++ } ++ ++ if (Buffer.isBuffer(session) && options?.rejectUnauthorized !== false) { ++ debug('ignore raw session for verified client connection to %s', ++ getSessionServerIdentity(options)); ++ return; ++ } ++ ++ return session; ++} ++ + let ipServernameWarned = false; + let tlsTracingWarned = false; + +@@ -339,10 +415,11 @@ function requestOCSPDone(socket) { + function onnewsessionclient(sessionId, session) { + debug('client emit session'); + const owner = this[owner_symbol]; ++ const wrappedSession = wrapSessionState(session, owner[kConnectOptions]); + if (owner[kIsVerified]) { +- owner.emit('session', session); ++ owner.emit('session', wrappedSession); + } else { +- owner[kPendingSession] = session; ++ owner[kPendingSession] = wrappedSession; + } + } + +@@ -1129,9 +1206,19 @@ TLSSocket.prototype.setServername = function(name) { + }; + + TLSSocket.prototype.setSession = function(session) { +- if (typeof session === 'string') +- session = Buffer.from(session, 'latin1'); +- this._handle.setSession(session); ++ session = getSessionForReuse(session, this[kConnectOptions] || this._tlsOptions); ++ if (session !== undefined) ++ this._handle.setSession(session); ++}; ++ ++TLSSocket.prototype.getSession = function() { ++ if (!this._handle) ++ return null; ++ ++ return wrapSessionState( ++ this._handle.getSession(), ++ this[kConnectOptions] || this._tlsOptions, ++ ); + }; + + TLSSocket.prototype.getPeerCertificate = function(detailed) { +@@ -1190,7 +1277,6 @@ function makeSocketMethodProxy(name) { + 'getFinished', + 'getPeerFinished', + 'getProtocol', +- 'getSession', + 'getTLSTicket', + 'isSessionReused', + 'enableTrace', +@@ -1701,12 +1787,11 @@ function onConnectSecure() { + // Verify that server's identity matches it's certificate's names + // Unless server has resumed our existing session + if (!verifyError && !this.isSessionReused()) { +- const hostname = options.servername || +- options.host || +- (options.socket && options.socket._host) || +- 'localhost'; + const cert = this.getPeerCertificate(true); +- verifyError = options.checkServerIdentity(hostname, cert); ++ verifyError = options.checkServerIdentity( ++ getSessionServerIdentity(options), ++ cert, ++ ); + } + + if (verifyError) { +@@ -1787,6 +1872,8 @@ exports.connect = function connect(...args) { + + const context = options.secureContext || tls.createSecureContext(options); + ++ options.session = getSessionForReuse(options.session, options); ++ + const tlssock = new TLSSocket(options.socket, { + allowHalfOpen: options.allowHalfOpen, + pipe: !!options.path, diff -Nru nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48935.patch nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48935.patch --- nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48935.patch 1970-01-01 00:00:00.000000000 +0000 +++ nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48935.patch 2026-09-22 17:02:25.000000000 +0000 @@ -0,0 +1,64 @@ +From: RafaelGSS +Date: Mon, 11 May 2026 18:44:39 -0300 +Subject: permission: disable FileHandle utimes with permission model + +PR-URL: https://github.com/nodejs-private/node-private/pull/873 +Reviewed-By: Antoine du Hamel +CVE-ID: CVE-2026-48935 +Refs: https://hackerone.com/reports/3625987 +origin: https://github.com/nodejs/node/commit/28dcd388644c676b5b8149abfe18ec32cd010781 +bug: https://nodejs.org/en/blog/vulnerability/june-2026-security-releases#permission-model-bypass-via-filehandleutimes-in-the-promises-api-cve-2026-48935---low +--- + lib/internal/fs/promises.js | 3 ++ + .../test-permission-fs-filehandle-utimes.js | 33 ++++++++++++++++++++++ + 2 files changed, 36 insertions(+) + create mode 100644 test/parallel/test-permission-fs-filehandle-utimes.js + +--- a/lib/internal/fs/promises.js ++++ b/lib/internal/fs/promises.js +@@ -1149,6 +1149,9 @@ + } + + async function futimes(handle, atime, mtime) { ++ if (permission.isEnabled()) { ++ throw new ERR_ACCESS_DENIED('futimes API is disabled when Permission Model is enabled.'); ++ } + atime = toUnixTimestamp(atime, 'atime'); + mtime = toUnixTimestamp(mtime, 'mtime'); + return await PromisePrototypeThen( +--- /dev/null ++++ b/test/parallel/test-permission-fs-filehandle-utimes.js +@@ -0,0 +1,33 @@ ++// Flags: --experimental-permission --allow-fs-read=* ++'use strict'; ++ ++const common = require('../common'); ++const { isMainThread } = require('worker_threads'); ++ ++if (!isMainThread) { ++ common.skip('This test only works on a main thread'); ++} ++ ++if (!common.hasCrypto) { ++ common.skip('no crypto'); ++} ++ ++const assert = require('assert'); ++const { open } = require('fs/promises'); ++const fixtures = require('../common/fixtures'); ++ ++const regularFile = fixtures.path('permission', 'deny', 'regular-file.md'); ++ ++// FileHandle.utimes() must be blocked when the permission model is enabled, ++// consistent with fs.futimes() / fs.futimesSync(). ++(async () => { ++ const fh = await open(regularFile, 'r'); ++ try { ++ await assert.rejects( ++ fh.utimes(Date.now(), Date.now()), ++ common.expectsError({ code: 'ERR_ACCESS_DENIED' }), ++ ); ++ } finally { ++ await fh.close(); ++ } ++})().then(common.mustCall()); diff -Nru nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48937.patch nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48937.patch --- nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48937.patch 1970-01-01 00:00:00.000000000 +0000 +++ nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48937.patch 2026-09-22 17:02:25.000000000 +0000 @@ -0,0 +1,222 @@ +From: Tim Perry +Date: Wed, 22 Apr 2026 13:36:27 +0200 +Subject: deps: fix integration issues with the latest nghttp2 + +This is a set of src & tests fixes for nghttp2 due to changes in +v1.67.0+ which require a selection of changes to how we handle +low-level protocol errors when using the latest versions of nghttp2, +changing both some src error handling and updating some tests to match. + +Signed-off-by: Tim Perry +PR-URL: https://github.com/nodejs/node/pull/62891 +Backport-PR-URL: https://github.com/nodejs/node/pull/63195 +Reviewed-By: Marco Ippolito +Reviewed-By: Tim Perry +Reviewed-By: Rafael Gonzaga +Reviewed-By: Antoine du Hamel +Refs: https://hackerone.com/reports/3658225 +CVE-ID: CVE-2026-48937 +origin: backport, https://github.com/nodejs/node/commit/a1a5bb968303229d4a3a7c9e389dc3ece6237b4c +Bug: https://nodejs.org/en/blog/vulnerability/june-2026-security-releases#http2-sessions-never-clean-up-after-goaway-on-invalid-protocol-errors-cve-2026-48937---medium +--- + src/node_http2.cc | 31 ++++++++ + src/node_http2.h | 2 + + .../test-http2-misbehaving-flow-control-paused.js | 33 +++++--- + .../test-http2-misbehaving-flow-control.js | 33 +++++--- + ...st-http2-options-max-headers-exceeds-nghttp2.js | 12 ++- + ...test-http2-session-cleanup-on-nghttp2-goaway.js | 91 ++++++++++++++++++++++ + 6 files changed, 176 insertions(+), 26 deletions(-) + create mode 100644 test/parallel/test-http2-session-cleanup-on-nghttp2-goaway.js + +diff --git a/src/node_http2.cc b/src/node_http2.cc +index 4f44789..5047e5c 100644 +--- a/src/node_http2.cc ++++ b/src/node_http2.cc +@@ -1246,6 +1246,24 @@ int Http2Session::OnFrameSent(nghttp2_session* handle, + void* user_data) { + Http2Session* session = static_cast(user_data); + session->statistics_.frame_sent += 1; ++ ++ // If nghttp2 has internally terminated the session (e.g. due to a protocol ++ // error like oversized frames, padding errors, or HPACK compression ++ // failures), it calls nghttp2_session_terminate_session() directly which ++ // queues a GOAWAY but does not invoke any application-level callback. ++ // Detect that case here: a GOAWAY was sent but we never initiated it ++ // (no Close(), no session.close(), no session.goaway()). ++ // ++ // We set a flag here, and then throw the error at the end of ++ // SendPendingData, to wait until the GOAWAY is written before the session ++ // is torn down. ++ if (frame->hd.type == NGHTTP2_GOAWAY && !session->is_closing() && ++ !session->is_destroyed() && !session->IsGracefulCloseInitiated() && ++ !session->goaway_initiated_) { ++ Debug(session, "nghttp2 session terminated internally"); ++ session->internal_goaway_sent_ = true; ++ } ++ + return 0; + } + +@@ -1976,6 +1994,18 @@ uint8_t Http2Session::SendPendingData() { + + MaybeStopReading(); + ++ // If nghttp2 has internally torn down the session (detected in OnFrameSent) ++ // during the nghttp2_session_mem_send loop above, at this point we error: ++ if (internal_goaway_sent_) { ++ internal_goaway_sent_ = false; ++ if (!is_closing() && !is_destroyed()) { ++ Isolate* isolate = env()->isolate(); ++ HandleScope scope(isolate); ++ Local arg = Integer::New(isolate, NGHTTP2_ERR_PROTO); ++ MakeCallback(env()->http2session_on_error_function(), 1, &arg); ++ } ++ } ++ + return 0; + } + +@@ -2901,6 +2931,7 @@ void Http2Session::Goaway(uint32_t code, + if (is_destroyed()) + return; + ++ goaway_initiated_ = true; + Http2Scope h2scope(this); + // the last proc stream id is the most recently created Http2Stream. + if (lastStreamID <= 0) +diff --git a/src/node_http2.h b/src/node_http2.h +index a60a7ba..9104605 100644 +--- a/src/node_http2.h ++++ b/src/node_http2.h +@@ -968,6 +968,8 @@ class Http2Session : public AsyncWrap, + + // Flag to indicate that JavaScript has initiated a graceful closure + bool graceful_close_initiated_ = false; ++ bool goaway_initiated_ = false; ++ bool internal_goaway_sent_ = false; + }; + + struct Http2SessionPerformanceEntryTraits { +diff --git a/test/parallel/test-http2-options-max-headers-exceeds-nghttp2.js b/test/parallel/test-http2-options-max-headers-exceeds-nghttp2.js +index 7767dbb..46f560f 100644 +--- a/test/parallel/test-http2-options-max-headers-exceeds-nghttp2.js ++++ b/test/parallel/test-http2-options-max-headers-exceeds-nghttp2.js +@@ -90,11 +90,19 @@ server.listen(0, common.mustCall(() => { + 0, + common.mustCall(() => { + const client = h2.connect(`http://localhost:${server.address().port}`); +- client.on('error', common.mustNotCall()); ++ // The server sends oversized headers that cause a compression error on ++ // the client side, so nghttp2 internally terminates the client session. ++ client.on('error', common.expectsError({ ++ code: 'ERR_HTTP2_ERROR', ++ name: 'Error', ++ })); + + const req = client.request(); + req.on('response', common.mustNotCall()); +- req.on('error', common.mustNotCall()); ++ req.on('error', common.expectsError({ ++ code: 'ERR_HTTP2_ERROR', ++ name: 'Error', ++ })); + req.end(); + }), + ); +diff --git a/test/parallel/test-http2-session-cleanup-on-nghttp2-goaway.js b/test/parallel/test-http2-session-cleanup-on-nghttp2-goaway.js +new file mode 100644 +index 0000000..f3576f9 +--- /dev/null ++++ b/test/parallel/test-http2-session-cleanup-on-nghttp2-goaway.js +@@ -0,0 +1,91 @@ ++'use strict'; ++ ++const common = require('../common'); ++ ++if (!common.hasCrypto) ++ common.skip('missing crypto'); ++ ++const http2 = require('http2'); ++const net = require('net'); ++ ++// When nghttp2 internally sends a GOAWAY frame due to a protocol error, it ++// may call nghttp2_session_terminate_session() directly, bypassing the ++// on_invalid_frame_recv_callback entirely. This test ensures that even ++// in that scenario, we still correctly clean up the session & connection. ++// ++// This test reproduces this with a client who sends a frame header with ++// a length exceeding the default max_frame_size (16384). nghttp2 responds ++// with GOAWAY(FRAME_SIZE_ERROR) without notifying Node through any callback. ++ ++const server = http2.createServer(); ++ ++server.on('session', common.mustCall((session) => { ++ session.on('error', common.expectsError({ ++ code: 'ERR_HTTP2_ERROR', ++ name: 'Error', ++ message: 'Protocol error' ++ })); ++ ++ session.on('close', common.mustCall(() => server.close())); ++})); ++ ++server.listen(0, common.mustCall(() => { ++ const conn = net.connect({ ++ port: server.address().port, ++ allowHalfOpen: true, ++ }); ++ ++ // HTTP/2 client connection preface. ++ conn.write('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'); ++ ++ // Empty SETTINGS frame. ++ const settingsFrame = Buffer.alloc(9); ++ settingsFrame[3] = 0x04; // type: SETTINGS ++ conn.write(settingsFrame); ++ ++ let inbuf = Buffer.alloc(0); ++ let state = 'settingsHeader'; ++ let settingsFrameLength; ++ ++ conn.on('data', (chunk) => { ++ inbuf = Buffer.concat([inbuf, chunk]); ++ ++ switch (state) { ++ case 'settingsHeader': ++ if (inbuf.length < 9) return; ++ settingsFrameLength = inbuf.readUIntBE(0, 3); ++ inbuf = inbuf.slice(9); ++ state = 'readingSettings'; ++ // Fallthrough ++ case 'readingSettings': { ++ if (inbuf.length < settingsFrameLength) return; ++ inbuf = inbuf.slice(settingsFrameLength); ++ state = 'done'; ++ ++ // ACK the server SETTINGS. ++ const ack = Buffer.alloc(9); ++ ack[3] = 0x04; // type: SETTINGS ++ ack[4] = 0x01; // flag: ACK ++ conn.write(ack); ++ ++ // Send a HEADERS frame header claiming length 16385, which exceeds ++ // the default max_frame_size of 16384. nghttp2 checks the length ++ // before reading any payload, so no body is needed. This triggers ++ // nghttp2_session_terminate_session(FRAME_SIZE_ERROR) directly in ++ // nghttp2_session_mem_recv2 — bypassing on_invalid_frame_recv_callback. ++ const oversized = Buffer.alloc(9); ++ oversized.writeUIntBE(16385, 0, 3); // length: 16385 (one over max) ++ oversized[3] = 0x01; // type: HEADERS ++ oversized[4] = 0x04; // flags: END_HEADERS ++ oversized.writeUInt32BE(1, 5); // stream id: 1 ++ conn.write(oversized); ++ ++ // No need to write the data - the header alone triggers the check. ++ } ++ } ++ }); ++ ++ // The server must close the connection after sending GOAWAY: ++ conn.on('end', common.mustCall(() => conn.end())); ++ conn.on('close', common.mustCall()); ++})); diff -Nru nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48937_pre1.patch nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48937_pre1.patch --- nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48937_pre1.patch 1970-01-01 00:00:00.000000000 +0000 +++ nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-48937_pre1.patch 2026-08-31 15:10:47.000000000 +0000 @@ -0,0 +1,363 @@ +From: Kushagra Pandey +Date: Sat, 19 Apr 2025 22:06:03 +0530 +Subject: http2: fix graceful session close + +Fix issue where session.close() prematurely destroys the session +when response.end() was called with an empty payload while active +http2 streams still existed. This change ensures that sessions are +closed gracefully only after all http2 streams complete and clients +properly receive the GOAWAY frame as per the HTTP/2 spec. + +Refs: https://nodejs.org/api/http2.html\#http2sessionclosecallback +PR-URL: https://github.com/nodejs/node/pull/57808 +Fixes: https://github.com/nodejs/node/issues/57809 +Refs: https://nodejs.org/api/http2.html%5C#http2sessionclosecallback +Reviewed-By: Matteo Collina +Reviewed-By: Tim Perry +Reviewed-By: James M Snell +origin: backport, https://github.com/nodejs/node/commit/137717354fb5fef6174eb66421207cd9c46c5096 +--- + lib/internal/http2/core.js | 11 +++- + src/env_properties.h | 1 + + src/node_http2.cc | 59 +++++++++++++++++++++- + src/node_http2.h | 15 ++++++ + .../test-http2-client-rststream-before-connect.js | 17 ++++--- + ...nse.js => test-http2-session-graceful-close.js} | 37 ++++++++------ + 6 files changed, 115 insertions(+), 25 deletions(-) + copy test/parallel/{test-http2-compat-serverresponse.js => test-http2-session-graceful-close.js} (57%) + +diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js +index 93d36d0..c489268 100644 +--- a/lib/internal/http2/core.js ++++ b/lib/internal/http2/core.js +@@ -1075,6 +1075,7 @@ function setupHandle(socket, type, options) { + if (typeof options.selectPadding === 'function') + this[kSelectPadding] = options.selectPadding; + handle.consume(socket._handle); ++ handle.ongracefulclosecomplete = this[kMaybeDestroy].bind(this, null); + + this[kHandle] = handle; + if (this[kNativeFields]) { +@@ -1599,6 +1600,10 @@ class Http2Session extends EventEmitter { + if (typeof callback === 'function') + this.once('close', callback); + this.goaway(); ++ const handle = this[kHandle]; ++ if (handle) { ++ handle.setGracefulClose(); ++ } + this[kMaybeDestroy](); + } + +@@ -1619,11 +1624,13 @@ class Http2Session extends EventEmitter { + // * session is closed and there are no more pending or open streams + [kMaybeDestroy](error) { + if (error == null) { ++ const handle = this[kHandle]; ++ const hasPendingData = !!handle && handle.hasPendingData(); + const state = this[kState]; + // Do not destroy if we're not closed and there are pending/open streams + if (!this.closed || + state.streams.size > 0 || +- state.pendingStreams.size > 0) { ++ state.pendingStreams.size > 0 || hasPendingData) { + return; + } + } +@@ -3317,7 +3324,7 @@ function socketOnClose() { + state.streams.forEach((stream) => stream.close(NGHTTP2_CANCEL)); + state.pendingStreams.forEach((stream) => stream.close(NGHTTP2_CANCEL)); + session.close(); +- session[kMaybeDestroy](err); ++ closeSession(session, NGHTTP2_NO_ERROR, err); + } + } + +diff --git a/src/env_properties.h b/src/env_properties.h +index 555c8fd..94cc1f2 100644 +--- a/src/env_properties.h ++++ b/src/env_properties.h +@@ -249,6 +249,7 @@ + V(onsignal_string, "onsignal") \ + V(onunpipe_string, "onunpipe") \ + V(onwrite_string, "onwrite") \ ++ V(ongracefulclosecomplete_string, "ongracefulclosecomplete") \ + V(openssl_error_stack, "opensslErrorStack") \ + V(options_string, "options") \ + V(order_string, "order") \ +diff --git a/src/node_http2.cc b/src/node_http2.cc +index 9a0c264..4f44789 100644 +--- a/src/node_http2.cc ++++ b/src/node_http2.cc +@@ -545,7 +545,8 @@ Http2Session::Http2Session(Http2State* http2_state, + : AsyncWrap(http2_state->env(), wrap, AsyncWrap::PROVIDER_HTTP2SESSION), + js_fields_(http2_state->env()->isolate()), + session_type_(type), +- http2_state_(http2_state) { ++ http2_state_(http2_state), ++ graceful_close_initiated_(false) { + MakeWeak(); + statistics_.session_type = type; + statistics_.start_time = uv_hrtime(); +@@ -749,6 +750,24 @@ void Http2Stream::EmitStatistics() { + }); + } + ++void Http2Session::HasPendingData(const FunctionCallbackInfo& args) { ++ Http2Session* session; ++ ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); ++ args.GetReturnValue().Set(session->HasPendingData()); ++} ++ ++bool Http2Session::HasPendingData() const { ++ nghttp2_session* session = session_.get(); ++ int want_write = nghttp2_session_want_write(session); ++ // It is expected that want_read will alway be 0 if graceful ++ // session close is initiated and goaway frame is sent. ++ int want_read = nghttp2_session_want_read(session); ++ if (want_write == 0 && want_read == 0) { ++ return false; ++ } ++ return true; ++} ++ + void Http2Session::EmitStatistics() { + if (LIKELY(!HasHttp2Observer(env()))) + return; +@@ -1731,6 +1750,7 @@ void Http2Session::HandleSettingsFrame(const nghttp2_frame* frame) { + void Http2Session::OnStreamAfterWrite(WriteWrap* w, int status) { + Debug(this, "write finished with status %d", status); + ++ MaybeNotifyGracefulCloseComplete(); + CHECK(is_write_in_progress()); + set_write_in_progress(false); + +@@ -1951,6 +1971,7 @@ uint8_t Http2Session::SendPendingData() { + if (!res.async) { + set_write_in_progress(false); + ClearOutgoing(res.err); ++ MaybeNotifyGracefulCloseComplete(); + } + + MaybeStopReading(); +@@ -3424,6 +3445,8 @@ void Initialize(Local target, + SetProtoMethod(isolate, session, "receive", Http2Session::Receive); + SetProtoMethod(isolate, session, "destroy", Http2Session::Destroy); + SetProtoMethod(isolate, session, "goaway", Http2Session::Goaway); ++ SetProtoMethod( ++ isolate, session, "hasPendingData", Http2Session::HasPendingData); + SetProtoMethod(isolate, session, "settings", Http2Session::Settings); + SetProtoMethod(isolate, session, "request", Http2Session::Request); + SetProtoMethod( +@@ -3444,6 +3467,8 @@ void Initialize(Local target, + "remoteSettings", + Http2Session::RefreshSettings); ++ SetProtoMethod( ++ isolate, session, "setGracefulClose", Http2Session::SetGracefulClose); + SetConstructorFunction(context, target, "Http2Session", session); + + Local constants = Object::New(isolate); +@@ -3498,6 +3523,38 @@ void Initialize(Local target, + nghttp2_set_debug_vprintf_callback(NgHttp2Debug); + #endif + } ++ ++void Http2Session::SetGracefulClose(const FunctionCallbackInfo& args) { ++ Http2Session* session; ++ ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); ++ CHECK_NOT_NULL(session); ++ // Set the graceful close flag ++ session->SetGracefulCloseInitiated(true); ++ ++ Debug(session, "Setting graceful close initiated flag"); ++} ++ ++void Http2Session::MaybeNotifyGracefulCloseComplete() { ++ nghttp2_session* session = session_.get(); ++ ++ if (!IsGracefulCloseInitiated()) { ++ return; ++ } ++ ++ int want_write = nghttp2_session_want_write(session); ++ int want_read = nghttp2_session_want_read(session); ++ bool should_notify = (want_write == 0 && want_read == 0); ++ ++ if (should_notify) { ++ Debug(this, "Notifying JS after write in graceful close mode"); ++ ++ // Make the callback to JavaScript ++ HandleScope scope(env()->isolate()); ++ MakeCallback(env()->ongracefulclosecomplete_string(), 0, nullptr); ++ } ++ ++ return; ++} + } // namespace http2 + } // namespace node + +diff --git a/src/node_http2.h b/src/node_http2.h +index 3ba05cb..a60a7ba 100644 +--- a/src/node_http2.h ++++ b/src/node_http2.h +@@ -712,6 +712,7 @@ class Http2Session : public AsyncWrap, + static void Consume(const v8::FunctionCallbackInfo& args); + static void Receive(const v8::FunctionCallbackInfo& args); + static void Destroy(const v8::FunctionCallbackInfo& args); ++ static void HasPendingData(const v8::FunctionCallbackInfo& args); + static void Settings(const v8::FunctionCallbackInfo& args); + static void Request(const v8::FunctionCallbackInfo& args); + static void SetNextStreamID(const v8::FunctionCallbackInfo& args); +@@ -723,6 +724,7 @@ class Http2Session : public AsyncWrap, + static void Ping(const v8::FunctionCallbackInfo& args); + static void AltSvc(const v8::FunctionCallbackInfo& args); + static void Origin(const v8::FunctionCallbackInfo& args); ++ static void SetGracefulClose(const v8::FunctionCallbackInfo& args); + + template + static void RefreshSettings(const v8::FunctionCallbackInfo& args); +@@ -735,6 +737,7 @@ class Http2Session : public AsyncWrap, + + BaseObjectPtr PopPing(); + bool AddPing(const uint8_t* data, v8::Local callback); ++ bool HasPendingData() const; + + BaseObjectPtr PopSettings(); + bool AddSettings(v8::Local callback); +@@ -785,6 +788,13 @@ class Http2Session : public AsyncWrap, + + Statistics statistics_ = {}; + ++ bool IsGracefulCloseInitiated() const { ++ return graceful_close_initiated_; ++ } ++ void SetGracefulCloseInitiated(bool value) { ++ graceful_close_initiated_ = value; ++ } ++ + private: + void EmitStatistics(); + +@@ -951,8 +961,13 @@ class Http2Session : public AsyncWrap, + void CopyDataIntoOutgoing(const uint8_t* src, size_t src_length); + void ClearOutgoing(int status); + ++ void MaybeNotifyGracefulCloseComplete(); ++ + friend class Http2Scope; + friend class Http2StreamListener; ++ ++ // Flag to indicate that JavaScript has initiated a graceful closure ++ bool graceful_close_initiated_ = false; + }; + + struct Http2SessionPerformanceEntryTraits { +diff --git a/test/parallel/test-http2-client-rststream-before-connect.js b/test/parallel/test-http2-client-rststream-before-connect.js +index bc0cb5f..788253d 100644 +--- a/test/parallel/test-http2-client-rststream-before-connect.js ++++ b/test/parallel/test-http2-client-rststream-before-connect.js +@@ -5,16 +5,23 @@ if (!common.hasCrypto) + common.skip('missing crypto'); + const assert = require('assert'); + const h2 = require('http2'); ++let client; + + const server = h2.createServer(); + server.on('stream', (stream) => { +- stream.on('close', common.mustCall()); +- stream.respond(); +- stream.end('ok'); ++ stream.on('close', common.mustCall(() => { ++ client.close(); ++ server.close(); ++ })); ++ stream.on('error', common.expectsError({ ++ code: 'ERR_HTTP2_STREAM_ERROR', ++ name: 'Error', ++ message: 'Stream closed with error code NGHTTP2_PROTOCOL_ERROR' ++ })); + }); + + server.listen(0, common.mustCall(() => { +- const client = h2.connect(`http://localhost:${server.address().port}`); ++ client = h2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + const closeCode = 1; + +@@ -52,8 +59,6 @@ server.listen(0, common.mustCall(() => { + req.on('close', common.mustCall(() => { + assert.strictEqual(req.destroyed, true); + assert.strictEqual(req.rstCode, closeCode); +- server.close(); +- client.close(); + })); + + req.on('error', common.expectsError({ +diff --git a/test/parallel/test-http2-compat-serverresponse.js b/test/parallel/test-http2-session-graceful-close.js +similarity index 57% +copy from test/parallel/test-http2-compat-serverresponse.js +copy to test/parallel/test-http2-session-graceful-close.js +index fbde586..174eb03 100644 +--- a/test/parallel/test-http2-compat-serverresponse.js ++++ b/test/parallel/test-http2-session-graceful-close.js +@@ -6,38 +6,43 @@ if (!common.hasCrypto) + const assert = require('assert'); + const h2 = require('http2'); + +-// Http2ServerResponse should expose convenience properties +- + const server = h2.createServer(); +-server.listen(0, common.mustCall(function() { +- const port = server.address().port; +- server.once('request', common.mustCall(function(request, response) { +- assert.strictEqual(response.req, request); ++let session; + +- // Verify that writing to response.req is allowed. +- response.req = null; +- +- response.on('finish', common.mustCall(function() { +- process.nextTick(() => { +- server.close(); +- }); +- })); +- response.end(); ++server.on('session', common.mustCall(function(s) { ++ session = s; ++ session.on('close', common.mustCall(function() { ++ server.close(); + })); ++})); ++ ++server.listen(0, common.mustCall(function() { ++ const port = server.address().port; + + const url = `http://localhost:${port}`; + const client = h2.connect(url, common.mustCall(function() { + const headers = { +- ':path': '/foobar', ++ ':path': '/', + ':method': 'GET', + ':scheme': 'http', + ':authority': `localhost:${port}` + }; + const request = client.request(headers); ++ request.on('response', common.mustCall(function(headers) { ++ assert.strictEqual(headers[':status'], 200); ++ }, 1)); + request.on('end', common.mustCall(function() { + client.close(); + })); + request.end(); + request.resume(); + })); ++ client.on('goaway', common.mustCallAtLeast(1)); ++})); ++ ++server.once('request', common.mustCall(function(request, response) { ++ response.on('finish', common.mustCall(function() { ++ session.close(); ++ })); ++ response.end(); + })); diff -Nru nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-56846.patch nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-56846.patch --- nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-56846.patch 1970-01-01 00:00:00.000000000 +0000 +++ nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-56846.patch 2026-09-22 17:02:25.000000000 +0000 @@ -0,0 +1,192 @@ +From: Matteo Collina +Date: Mon, 8 Jun 2026 13:53:02 +0200 +Subject: http2: retain header memory in session accounting +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +Signed-off-by: Matteo Collina +PR-URL: https://github.com/nodejs/node/pull/63752 +Reviewed-By: Tim Perry +Reviewed-By: Rafael Gonzaga +Reviewed-By: Gürgün Dayıoğlu +CVE-ID: CVE-2026-56846 +origin: https://github.com/nodejs/node/commit/f14d78b9e0201bfe0291f2b0dc4b5d88c70cc28e +bug: https://nodejs.org/en/blog/vulnerability/july-2026-security-releases#http2-retained-headers-can-bypass-maxsessionmemory-limits-cve-2026-56846---high +--- + doc/api/http2.md | 21 ++++--- + src/node_http2.cc | 13 +++- + src/node_http2.h | 4 +- + ...est-http2-max-session-memory-stalled-headers.js | 69 ++++++++++++++++++++++ + 4 files changed, 96 insertions(+), 11 deletions(-) + create mode 100644 test/parallel/test-http2-max-session-memory-stalled-headers.js + +diff --git a/doc/api/http2.md b/doc/api/http2.md +index 287e4df..9b04be0 100644 +--- a/doc/api/http2.md ++++ b/doc/api/http2.md +@@ -2824,9 +2824,10 @@ changes: + This is a credit based limit, existing `Http2Stream`s may cause this + limit to be exceeded, but new `Http2Stream` instances will be rejected + while this limit is exceeded. The current number of `Http2Stream` sessions, +- the current memory use of the header compression tables, current data +- queued to be sent, and unacknowledged `PING` and `SETTINGS` frames are all +- counted towards the current limit. **Default:** `10`. ++ the current memory use of the header compression tables, header blocks ++ retained by open streams, current data queued to be sent, and ++ unacknowledged `PING` and `SETTINGS` frames are all counted towards the ++ current limit. **Default:** `10`. + * `maxHeaderListPairs` {number} Sets the maximum number of header entries. + This is similar to [`server.maxHeadersCount`][] or + [`request.maxHeadersCount`][] in the `node:http` module. The minimum value +@@ -3006,9 +3007,10 @@ changes: + credit based limit, existing `Http2Stream`s may cause this + limit to be exceeded, but new `Http2Stream` instances will be rejected + while this limit is exceeded. The current number of `Http2Stream` sessions, +- the current memory use of the header compression tables, current data +- queued to be sent, and unacknowledged `PING` and `SETTINGS` frames are all +- counted towards the current limit. **Default:** `10`. ++ the current memory use of the header compression tables, header blocks ++ retained by open streams, current data queued to be sent, and ++ unacknowledged `PING` and `SETTINGS` frames are all counted towards the ++ current limit. **Default:** `10`. + * `maxHeaderListPairs` {number} Sets the maximum number of header entries. + This is similar to [`server.maxHeadersCount`][] or + [`request.maxHeadersCount`][] in the `node:http` module. The minimum value +@@ -3161,9 +3163,10 @@ changes: + This is a credit based limit, existing `Http2Stream`s may cause this + limit to be exceeded, but new `Http2Stream` instances will be rejected + while this limit is exceeded. The current number of `Http2Stream` sessions, +- the current memory use of the header compression tables, current data +- queued to be sent, and unacknowledged `PING` and `SETTINGS` frames are all +- counted towards the current limit. **Default:** `10`. ++ the current memory use of the header compression tables, header blocks ++ retained by open streams, current data queued to be sent, and ++ unacknowledged `PING` and `SETTINGS` frames are all counted towards the ++ current limit. **Default:** `10`. + * `maxHeaderListPairs` {number} Sets the maximum number of header entries. + This is similar to [`server.maxHeadersCount`][] or + [`request.maxHeadersCount`][] in the `node:http` module. The minimum value +diff --git a/src/node_http2.cc b/src/node_http2.cc +index 5047e5c..2367d0a 100644 +--- a/src/node_http2.cc ++++ b/src/node_http2.cc +@@ -883,6 +883,14 @@ BaseObjectPtr Http2Session::RemoveStream(int32_t id) { + stream = FindStream(id); + if (stream) { + streams_.erase(id); ++ if (stream->current_headers_length_ > 0) { ++ DecrementCurrentSessionMemory(stream->current_headers_length_); ++ stream->current_headers_length_ = 0; ++ } ++ if (stream->retained_headers_length_ > 0) { ++ DecrementCurrentSessionMemory(stream->retained_headers_length_); ++ stream->retained_headers_length_ = 0; ++ } + DecrementCurrentSessionMemory(sizeof(*stream)); + } + return stream; +@@ -1527,7 +1535,10 @@ void Http2Session::HandleHeadersFrame(const nghttp2_frame* frame) { + }); + CHECK_EQ(stream->headers_count(), 0); + +- DecrementCurrentSessionMemory(stream->current_headers_length_); ++ // Keep the header block charged against maxSessionMemory while the ++ // corresponding JS objects can still keep it alive for the lifetime of ++ // the stream. ++ stream->retained_headers_length_ += stream->current_headers_length_; + stream->current_headers_length_ = 0; + + Local args[] = { +diff --git a/src/node_http2.h b/src/node_http2.h +index 9104605..cb0e067 100644 +--- a/src/node_http2.h ++++ b/src/node_http2.h +@@ -485,9 +485,11 @@ class Http2Stream : public AsyncWrap, + + // The Current Headers block... As headers are received for this stream, + // they are temporarily stored here until the OnFrameReceived is called +- // signalling the end of the HEADERS frame ++ // signalling the end of the HEADERS frame. + nghttp2_headers_category current_headers_category_ = NGHTTP2_HCAT_HEADERS; + uint32_t current_headers_length_ = 0; // total number of octets ++ // Charged against maxSessionMemory while headers stay alive in JS. ++ uint64_t retained_headers_length_ = 0; + std::vector current_headers_; + + // This keeps track of the amount of data read from the socket while the +diff --git a/test/parallel/test-http2-max-session-memory-stalled-headers.js b/test/parallel/test-http2-max-session-memory-stalled-headers.js +new file mode 100644 +index 0000000..c04bfd0 +--- /dev/null ++++ b/test/parallel/test-http2-max-session-memory-stalled-headers.js +@@ -0,0 +1,69 @@ ++'use strict'; ++ ++const common = require('../common'); ++if (!common.hasCrypto) ++ common.skip('missing crypto'); ++ ++const Countdown = require('../common/countdown'); ++const assert = require('assert'); ++const http2 = require('http2'); ++ ++const { ++ NGHTTP2_ENHANCE_YOUR_CALM, ++} = http2.constants; ++ ++// Regression test: header blocks retained by stalled streams should continue ++// to count against maxSessionMemory after they have been handed to JS. ++const maxSessionMemory = 1; ++const totalRequests = 400; ++const cookieCrumbs = 120; ++ ++let accepted = 0; ++let rejected = 0; ++ ++const server = http2.createServer({ maxSessionMemory }); ++server.on('stream', (stream) => { ++ accepted++; ++ stream.on('error', () => {}); ++ stream.respond(); ++ stream.write('x'); ++}); ++ ++server.listen(0, common.mustCall(() => { ++ const client = http2.connect(`http://localhost:${server.address().port}`, { ++ settings: { ++ initialWindowSize: 0, ++ }, ++ }); ++ client.on('error', () => {}); ++ ++ client.on('remoteSettings', common.mustCall(() => { ++ let destroyed = false; ++ const countdown = new Countdown(totalRequests, common.mustCall(() => { ++ assert(rejected > 0); ++ assert(accepted < totalRequests); ++ server.close(); ++ })); ++ ++ for (let i = 0; i < totalRequests; i++) { ++ const headers = {':path': '/', 'cookie': []}; ++ for (let j = 0; j < cookieCrumbs; j++) { ++ headers.cookie.push('a=1'); ++ } ++ ++ const req = client.request(headers); ++ req.on('error', () => {}); ++ req.on('close', () => { ++ if (req.rstCode === NGHTTP2_ENHANCE_YOUR_CALM) { ++ rejected++; ++ if (!destroyed) { ++ destroyed = true; ++ client.destroy(); ++ } ++ } ++ countdown.dec(); ++ }); ++ req.end(); ++ } ++ })); ++})); diff -Nru nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-56847.patch nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-56847.patch --- nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-56847.patch 1970-01-01 00:00:00.000000000 +0000 +++ nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-56847.patch 2026-09-22 17:02:25.000000000 +0000 @@ -0,0 +1,1296 @@ +From: RafaelGSS +Date: Fri, 3 Jul 2026 17:55:14 -0300 +Subject: permission: enforce fs write permission for trace events + +Signed-off-by: RafaelGSS +PR-URL: https://github.com/nodejs-private/node-private/pull/927 +CVE-ID: CVE-2026-56847 +origin: https://github.com/nodejs/node/commit/0566c3cccdc99b935646e813f71e2380aedee50d +bug: https://nodejs.org/en/blog/vulnerability/july-2026-security-releases#permission-model-allows-trace-events-to-write-outside-the-allowlist-cve-2026-56847---low +--- + src/node_trace_events.cc | 8 + + src/tracing/node_trace_writer.cc | 32 +- + src/tracing/node_trace_writer.h | 3 + + test/ffi/fixture_library/build/Makefile | 354 +++++++++++++ + test/ffi/fixture_library/build/binding.Makefile | 6 + + test/ffi/fixture_library/build/config.gypi | 570 +++++++++++++++++++++ + .../build/ffi_test_library.target.mk | 155 ++++++ + test/node_trace.1.log | 1 + + .../test-permission-fs-write-trace-events.js | 70 +++ + 9 files changed, 1186 insertions(+), 13 deletions(-) + create mode 100644 test/ffi/fixture_library/build/Makefile + create mode 100644 test/ffi/fixture_library/build/binding.Makefile + create mode 100644 test/ffi/fixture_library/build/config.gypi + create mode 100644 test/ffi/fixture_library/build/ffi_test_library.target.mk + create mode 100644 test/node_trace.1.log + create mode 100644 test/parallel/test-permission-fs-write-trace-events.js + +--- a/src/node_trace_events.cc ++++ b/src/node_trace_events.cc +@@ -5,7 +5,9 @@ + #include "node_external_reference.h" + #include "node_internals.h" + #include "node_v8_platform-inl.h" ++#include "permission/permission.h" + #include "tracing/agent.h" ++#include "tracing/node_trace_writer.h" + #include "util-inl.h" + + #include +@@ -84,6 +86,12 @@ + CHECK_NOT_NULL(category_set); + const auto& categories = category_set->GetCategories(); + if (!category_set->enabled_ && !categories.empty()) { ++ const std::string filepath = tracing::NodeTraceWriter::GetFilePath( ++ per_process::cli_options->trace_event_file_pattern, 1); ++ THROW_IF_INSUFFICIENT_PERMISSIONS( ++ category_set->env(), ++ permission::PermissionScope::kFileSystemWrite, ++ filepath); + // Starts the Tracing Agent if it wasn't started already (e.g. through + // a command line flag.) + StartTracingAgent(); +--- a/src/tracing/node_trace_writer.cc ++++ b/src/tracing/node_trace_writer.cc +@@ -8,9 +8,27 @@ + namespace node { + namespace tracing { + ++void replace_substring(std::string* target, ++ const std::string& search, ++ const std::string& insert) { ++ size_t pos = target->find(search); ++ for (; pos != std::string::npos; pos = target->find(search, pos)) { ++ target->replace(pos, search.size(), insert); ++ pos += insert.size(); ++ } ++} ++ + NodeTraceWriter::NodeTraceWriter(const std::string& log_file_pattern) + : log_file_pattern_(log_file_pattern) {} + ++std::string NodeTraceWriter::GetFilePath(const std::string& log_file_pattern, ++ int file_num) { ++ std::string filepath(log_file_pattern); ++ replace_substring(&filepath, "${pid}", std::to_string(uv_os_getpid())); ++ replace_substring(&filepath, "${rotation}", std::to_string(file_num)); ++ return filepath; ++} ++ + void NodeTraceWriter::InitializeOnThread(uv_loop_t* loop) { + CHECK_NULL(tracing_loop_); + tracing_loop_ = loop; +@@ -60,25 +78,13 @@ + } + } + +-void replace_substring(std::string* target, +- const std::string& search, +- const std::string& insert) { +- size_t pos = target->find(search); +- for (; pos != std::string::npos; pos = target->find(search, pos)) { +- target->replace(pos, search.size(), insert); +- pos += insert.size(); +- } +-} +- + void NodeTraceWriter::OpenNewFileForStreaming() { + ++file_num_; + uv_fs_t req; + + // Evaluate a JS-style template string, it accepts the values ${pid} and + // ${rotation} +- std::string filepath(log_file_pattern_); +- replace_substring(&filepath, "${pid}", std::to_string(uv_os_getpid())); +- replace_substring(&filepath, "${rotation}", std::to_string(file_num_)); ++ std::string filepath(GetFilePath(log_file_pattern_, file_num_)); + + if (fd_ != -1) { + CHECK_EQ(uv_fs_close(nullptr, &req, fd_, nullptr), 0); +--- a/src/tracing/node_trace_writer.h ++++ b/src/tracing/node_trace_writer.h +@@ -19,6 +19,9 @@ + explicit NodeTraceWriter(const std::string& log_file_pattern); + ~NodeTraceWriter() override; + ++ static std::string GetFilePath(const std::string& log_file_pattern, ++ int file_num); ++ + void InitializeOnThread(uv_loop_t* loop) override; + void AppendTraceEvent(TraceObject* trace_event) override; + void Flush(bool blocking) override; +--- /dev/null ++++ b/test/ffi/fixture_library/build/Makefile +@@ -0,0 +1,354 @@ ++# We borrow heavily from the kernel build setup, though we are simpler since ++# we don't have Kconfig tweaking settings on us. ++ ++# The implicit make rules have it looking for RCS files, among other things. ++# We instead explicitly write all the rules we care about. ++# It's even quicker (saves ~200ms) to pass -r on the command line. ++MAKEFLAGS=-r ++ ++# The source directory tree. ++srcdir := .. ++abs_srcdir := $(abspath $(srcdir)) ++ ++# The name of the builddir. ++builddir_name ?= . ++ ++# The V=1 flag on command line makes us verbosely print command lines. ++ifdef V ++ quiet= ++else ++ quiet=quiet_ ++endif ++ ++# Specify BUILDTYPE=Release on the command line for a release build. ++BUILDTYPE ?= Release ++ ++# Directory all our build output goes into. ++# Note that this must be two directories beneath src/ for unit tests to pass, ++# as they reach into the src/ directory for data with relative paths. ++builddir ?= $(builddir_name)/$(BUILDTYPE) ++abs_builddir := $(abspath $(builddir)) ++depsdir := $(builddir)/.deps ++ ++# Object output directory. ++obj := $(builddir)/obj ++abs_obj := $(abspath $(obj)) ++ ++# We build up a list of every single one of the targets so we can slurp in the ++# generated dependency rule Makefiles in one pass. ++all_deps := ++ ++ ++ ++CC.target ?= $(CC) ++CFLAGS.target ?= $(CPPFLAGS) $(CFLAGS) ++CXX.target ?= $(CXX) ++CXXFLAGS.target ?= $(CPPFLAGS) $(CXXFLAGS) ++LINK.target ?= $(LINK) ++LDFLAGS.target ?= $(LDFLAGS) ++AR.target ?= $(AR) ++PLI.target ?= pli ++ ++# C++ apps need to be linked with g++. ++LINK ?= $(CXX.target) ++ ++# TODO(evan): move all cross-compilation logic to gyp-time so we don't need ++# to replicate this environment fallback in make as well. ++CC.host ?= gcc ++CFLAGS.host ?= $(CPPFLAGS_host) $(CFLAGS_host) ++CXX.host ?= g++ ++CXXFLAGS.host ?= $(CPPFLAGS_host) $(CXXFLAGS_host) ++LINK.host ?= $(CXX.host) ++LDFLAGS.host ?= $(LDFLAGS_host) ++AR.host ?= ar ++PLI.host ?= pli ++ ++# Define a dir function that can handle spaces. ++# http://www.gnu.org/software/make/manual/make.html#Syntax-of-Functions ++# "leading spaces cannot appear in the text of the first argument as written. ++# These characters can be put into the argument value by variable substitution." ++empty := ++space := $(empty) $(empty) ++ ++# http://stackoverflow.com/questions/1189781/using-make-dir-or-notdir-on-a-path-with-spaces ++replace_spaces = $(subst $(space),?,$1) ++unreplace_spaces = $(subst ?,$(space),$1) ++dirx = $(call unreplace_spaces,$(dir $(call replace_spaces,$1))) ++ ++# Flags to make gcc output dependency info. Note that you need to be ++# careful here to use the flags that ccache and distcc can understand. ++# We write to a dep file on the side first and then rename at the end ++# so we can't end up with a broken dep file. ++depfile = $(depsdir)/$(call replace_spaces,$@).d ++DEPFLAGS = -MMD -MF $(depfile).raw ++ ++# We have to fixup the deps output in a few ways. ++# (1) the file output should mention the proper .o file. ++# ccache or distcc lose the path to the target, so we convert a rule of ++# the form: ++# foobar.o: DEP1 DEP2 ++# into ++# path/to/foobar.o: DEP1 DEP2 ++# (2) we want missing files not to cause us to fail to build. ++# We want to rewrite ++# foobar.o: DEP1 DEP2 \ ++# DEP3 ++# to ++# DEP1: ++# DEP2: ++# DEP3: ++# so if the files are missing, they're just considered phony rules. ++# We have to do some pretty insane escaping to get those backslashes ++# and dollar signs past make, the shell, and sed at the same time. ++# Doesn't work with spaces, but that's fine: .d files have spaces in ++# their names replaced with other characters. ++define fixup_dep ++# The depfile may not exist if the input file didn't have any #includes. ++touch $(depfile).raw ++# Fixup path as in (1). ++sed -e "s|^$(notdir $@)|$@|" $(depfile).raw >> $(depfile) ++# Add extra rules as in (2). ++# We remove slashes and replace spaces with new lines; ++# remove blank lines; ++# delete the first line and append a colon to the remaining lines. ++sed -e 's|\\||' -e 'y| |\n|' $(depfile).raw |\ ++ grep -v '^$$' |\ ++ sed -e 1d -e 's|$$|:|' \ ++ >> $(depfile) ++rm $(depfile).raw ++endef ++ ++# Command definitions: ++# - cmd_foo is the actual command to run; ++# - quiet_cmd_foo is the brief-output summary of the command. ++ ++quiet_cmd_cc = CC($(TOOLSET)) $@ ++cmd_cc = $(CC.$(TOOLSET)) -o $@ $< $(GYP_CFLAGS) $(DEPFLAGS) $(CFLAGS.$(TOOLSET)) -c ++ ++quiet_cmd_cxx = CXX($(TOOLSET)) $@ ++cmd_cxx = $(CXX.$(TOOLSET)) -o $@ $< $(GYP_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c ++ ++quiet_cmd_touch = TOUCH $@ ++cmd_touch = touch $@ ++ ++quiet_cmd_copy = COPY $@ ++# send stderr to /dev/null to ignore messages when linking directories. ++cmd_copy = ln -f "$<" "$@" 2>/dev/null || (rm -rf "$@" && cp -af "$<" "$@") ++ ++quiet_cmd_symlink = SYMLINK $@ ++cmd_symlink = ln -sf "$<" "$@" ++ ++quiet_cmd_alink = AR($(TOOLSET)) $@ ++cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) crs $@ $(filter %.o,$^) ++ ++quiet_cmd_alink_thin = AR($(TOOLSET)) $@ ++cmd_alink_thin = rm -f $@ && $(AR.$(TOOLSET)) crsT $@ $(filter %.o,$^) ++ ++# Due to circular dependencies between libraries :(, we wrap the ++# special "figure out circular dependencies" flags around the entire ++# input list during linking. ++quiet_cmd_link = LINK($(TOOLSET)) $@ ++cmd_link = $(LINK.$(TOOLSET)) -o $@ $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,--start-group $(LD_INPUTS) $(LIBS) -Wl,--end-group ++ ++# Note: this does not handle spaces in paths ++define xargs ++ $(1) $(word 1,$(2)) ++$(if $(word 2,$(2)),$(call xargs,$(1),$(wordlist 2,$(words $(2)),$(2)))) ++endef ++ ++define write-to-file ++ @: >$(1) ++$(call xargs,@printf "%s\n" >>$(1),$(2)) ++endef ++ ++OBJ_FILE_LIST := ar-file-list ++ ++define create_archive ++ rm -f $(1) $(1).$(OBJ_FILE_LIST); mkdir -p `dirname $(1)` ++ $(call write-to-file,$(1).$(OBJ_FILE_LIST),$(filter %.o,$(2))) ++ $(AR.$(TOOLSET)) crs $(1) @$(1).$(OBJ_FILE_LIST) ++endef ++ ++define create_thin_archive ++ rm -f $(1) $(OBJ_FILE_LIST); mkdir -p `dirname $(1)` ++ $(call write-to-file,$(1).$(OBJ_FILE_LIST),$(filter %.o,$(2))) ++ $(AR.$(TOOLSET)) crsT $(1) @$(1).$(OBJ_FILE_LIST) ++endef ++ ++# We support two kinds of shared objects (.so): ++# 1) shared_library, which is just bundling together many dependent libraries ++# into a link line. ++# 2) loadable_module, which is generating a module intended for dlopen(). ++# ++# They differ only slightly: ++# In the former case, we want to package all dependent code into the .so. ++# In the latter case, we want to package just the API exposed by the ++# outermost module. ++# This means shared_library uses --whole-archive, while loadable_module doesn't. ++# (Note that --whole-archive is incompatible with the --start-group used in ++# normal linking.) ++ ++# Other shared-object link notes: ++# - Set SONAME to the library filename so our binaries don't reference ++# the local, absolute paths used on the link command-line. ++quiet_cmd_solink = SOLINK($(TOOLSET)) $@ ++cmd_solink = $(LINK.$(TOOLSET)) -o $@ -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -Wl,--whole-archive $(LD_INPUTS) -Wl,--no-whole-archive $(LIBS) ++ ++quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@ ++cmd_solink_module = $(LINK.$(TOOLSET)) -o $@ -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -Wl,--start-group $(filter-out FORCE_DO_CMD, $^) -Wl,--end-group $(LIBS) ++ ++ ++# Define an escape_quotes function to escape single quotes. ++# This allows us to handle quotes properly as long as we always use ++# use single quotes and escape_quotes. ++escape_quotes = $(subst ','\'',$(1)) ++# This comment is here just to include a ' to unconfuse syntax highlighting. ++# Define an escape_vars function to escape '$' variable syntax. ++# This allows us to read/write command lines with shell variables (e.g. ++# $LD_LIBRARY_PATH), without triggering make substitution. ++escape_vars = $(subst $$,$$$$,$(1)) ++# Helper that expands to a shell command to echo a string exactly as it is in ++# make. This uses printf instead of echo because printf's behaviour with respect ++# to escape sequences is more portable than echo's across different shells ++# (e.g., dash, bash). ++exact_echo = printf '%s\n' '$(call escape_quotes,$(1))' ++ ++# Helper to compare the command we're about to run against the command ++# we logged the last time we ran the command. Produces an empty ++# string (false) when the commands match. ++# Tricky point: Make has no string-equality test function. ++# The kernel uses the following, but it seems like it would have false ++# positives, where one string reordered its arguments. ++# arg_check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \ ++# $(filter-out $(cmd_$@), $(cmd_$(1)))) ++# We instead substitute each for the empty string into the other, and ++# say they're equal if both substitutions produce the empty string. ++# .d files contain ? instead of spaces, take that into account. ++command_changed = $(or $(subst $(cmd_$(1)),,$(cmd_$(call replace_spaces,$@))),\ ++ $(subst $(cmd_$(call replace_spaces,$@)),,$(cmd_$(1)))) ++ ++# Helper that is non-empty when a prerequisite changes. ++# Normally make does this implicitly, but we force rules to always run ++# so we can check their command lines. ++# $? -- new prerequisites ++# $| -- order-only dependencies ++prereq_changed = $(filter-out FORCE_DO_CMD,$(filter-out $|,$?)) ++ ++# Helper that executes all postbuilds until one fails. ++define do_postbuilds ++ @E=0;\ ++ for p in $(POSTBUILDS); do\ ++ eval $$p;\ ++ E=$$?;\ ++ if [ $$E -ne 0 ]; then\ ++ break;\ ++ fi;\ ++ done;\ ++ if [ $$E -ne 0 ]; then\ ++ rm -rf "$@";\ ++ exit $$E;\ ++ fi ++endef ++ ++# do_cmd: run a command via the above cmd_foo names, if necessary. ++# Should always run for a given target to handle command-line changes. ++# Second argument, if non-zero, makes it do asm/C/C++ dependency munging. ++# Third argument, if non-zero, makes it do POSTBUILDS processing. ++# Note: We intentionally do NOT call dirx for depfile, since it contains ? for ++# spaces already and dirx strips the ? characters. ++define do_cmd ++$(if $(or $(command_changed),$(prereq_changed)), ++ @$(call exact_echo, $($(quiet)cmd_$(1))) ++ @mkdir -p "$(call dirx,$@)" "$(dir $(depfile))" ++ $(if $(findstring flock,$(word 1,$(cmd_$1))), ++ @$(cmd_$(1)) ++ @echo " $(quiet_cmd_$(1)): Finished", ++ @$(cmd_$(1)) ++ ) ++ @$(call exact_echo,$(call escape_vars,cmd_$(call replace_spaces,$@) := $(cmd_$(1)))) > $(depfile) ++ @$(if $(2),$(fixup_dep)) ++ $(if $(and $(3), $(POSTBUILDS)), ++ $(call do_postbuilds) ++ ) ++) ++endef ++ ++# Declare the "all" target first so it is the default, ++# even though we don't have the deps yet. ++.PHONY: all ++all: ++ ++# make looks for ways to re-generate included makefiles, but in our case, we ++# don't have a direct way. Explicitly telling make that it has nothing to do ++# for them makes it go faster. ++%.d: ; ++ ++# Use FORCE_DO_CMD to force a target to run. Should be coupled with ++# do_cmd. ++.PHONY: FORCE_DO_CMD ++FORCE_DO_CMD: ++ ++TOOLSET := target ++# Suffix rules, putting all outputs into $(obj). ++$(obj).$(TOOLSET)/%.o: $(srcdir)/%.c FORCE_DO_CMD ++ @$(call do_cmd,cc,1) ++$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD ++ @$(call do_cmd,cxx,1) ++$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cpp FORCE_DO_CMD ++ @$(call do_cmd,cxx,1) ++$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cxx FORCE_DO_CMD ++ @$(call do_cmd,cxx,1) ++$(obj).$(TOOLSET)/%.o: $(srcdir)/%.s FORCE_DO_CMD ++ @$(call do_cmd,cc,1) ++$(obj).$(TOOLSET)/%.o: $(srcdir)/%.S FORCE_DO_CMD ++ @$(call do_cmd,cc,1) ++ ++# Try building from generated source, too. ++$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD ++ @$(call do_cmd,cc,1) ++$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD ++ @$(call do_cmd,cxx,1) ++$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cpp FORCE_DO_CMD ++ @$(call do_cmd,cxx,1) ++$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cxx FORCE_DO_CMD ++ @$(call do_cmd,cxx,1) ++$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.s FORCE_DO_CMD ++ @$(call do_cmd,cc,1) ++$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.S FORCE_DO_CMD ++ @$(call do_cmd,cc,1) ++ ++$(obj).$(TOOLSET)/%.o: $(obj)/%.c FORCE_DO_CMD ++ @$(call do_cmd,cc,1) ++$(obj).$(TOOLSET)/%.o: $(obj)/%.cc FORCE_DO_CMD ++ @$(call do_cmd,cxx,1) ++$(obj).$(TOOLSET)/%.o: $(obj)/%.cpp FORCE_DO_CMD ++ @$(call do_cmd,cxx,1) ++$(obj).$(TOOLSET)/%.o: $(obj)/%.cxx FORCE_DO_CMD ++ @$(call do_cmd,cxx,1) ++$(obj).$(TOOLSET)/%.o: $(obj)/%.s FORCE_DO_CMD ++ @$(call do_cmd,cc,1) ++$(obj).$(TOOLSET)/%.o: $(obj)/%.S FORCE_DO_CMD ++ @$(call do_cmd,cc,1) ++ ++ ++ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ ++ $(findstring $(join ^,$(prefix)),\ ++ $(join ^,ffi_test_library.target.mk)))),) ++ include ffi_test_library.target.mk ++endif ++ ++quiet_cmd_regen_makefile = ACTION Regenerating $@ ++cmd_regen_makefile = cd $(srcdir); /home/rafaelgss/repos/os/node-private/deps/npm/node_modules/node-gyp/gyp/gyp_main.py -fmake --ignore-environment "-Dlibrary=shared_library" "-Dvisibility=default" "-Dnode_root_dir=/home/rafaelgss/repos/os/node-private/out/Release/addons_headers" "-Dnode_gyp_dir=/home/rafaelgss/repos/os/node-private/deps/npm/node_modules/node-gyp" "-Dnode_lib_file=/home/rafaelgss/repos/os/node-private/out/Release/addons_headers/$(Configuration)/node.lib" "-Dmodule_root_dir=/home/rafaelgss/repos/os/node-private/test/ffi/fixture_library" "-Dnode_engine=v8" "--depth=." "-Goutput_dir=." "--generator-output=build" -I/home/rafaelgss/repos/os/node-private/test/ffi/fixture_library/build/config.gypi -I/home/rafaelgss/repos/os/node-private/deps/npm/node_modules/node-gyp/addon.gypi -I/home/rafaelgss/repos/os/node-private/out/Release/addons_headers/include/node/common.gypi "--toplevel-dir=." binding.gyp ++Makefile: $(srcdir)/../../../deps/npm/node_modules/node-gyp/addon.gypi $(srcdir)/../../../out/Release/addons_headers/include/node/common.gypi $(srcdir)/binding.gyp $(srcdir)/build/config.gypi ++ $(call do_cmd,regen_makefile) ++ ++# "all" is a concatenation of the "all" targets from all the included ++# sub-makefiles. This is just here to clarify. ++all: ++ ++# Add in dependency-tracking rules. $(all_deps) is the list of every single ++# target in our tree. Only consider the ones with .d (dependency) info: ++d_files := $(wildcard $(foreach f,$(all_deps),$(depsdir)/$(f).d)) ++ifneq ($(d_files),) ++ include $(d_files) ++endif +--- /dev/null ++++ b/test/ffi/fixture_library/build/binding.Makefile +@@ -0,0 +1,6 @@ ++# This file is generated by gyp; do not edit. ++ ++export builddir_name ?= ./build/. ++.PHONY: all ++all: ++ $(MAKE) ffi_test_library +--- /dev/null ++++ b/test/ffi/fixture_library/build/config.gypi +@@ -0,0 +1,570 @@ ++# Do not edit. File was generated by node-gyp's "configure" step ++{ ++ "variables": { ++ "use_ccache_win": 0, ++ "clang": 0, ++ "llvm_version": "0.0", ++ "gas_version": "2.42", ++ "node_enable_experimentals": "false", ++ "node_prefix": "/usr/local", ++ "node_install_npm": "true", ++ "node_install_corepack": "false", ++ "control_flow_guard": "false", ++ "node_use_amaro": "true", ++ "debug_node": "false", ++ "debug_symbols": "false", ++ "build_type%": "Release", ++ "error_on_warn": "false", ++ "suppress_all_error_on_warn": "false", ++ "use_prefix_to_find_headers": "false", ++ "host_arch": "x64", ++ "target_arch": "x64", ++ "node_byteorder": "little", ++ "cargo_rust_target": "", ++ "want_separate_host_toolset": 0, ++ "node_use_node_snapshot": "true", ++ "node_use_node_code_cache": "true", ++ "node_write_snapshot_as_array_literals": "false", ++ "node_enable_v8_vtunejit": "false", ++ "enable_pgo_generate": "false", ++ "enable_pgo_use": "false", ++ "enable_lto": "false", ++ "enable_thin_lto": "false", ++ "lto_jobs": "", ++ "single_executable_application": "true", ++ "node_use_lief": "true", ++ "node_with_ltcg": "false", ++ "node_tag": "", ++ "node_release_urlbase": "", ++ "node_debug_lib": "false", ++ "debug_nghttp2": "false", ++ "node_no_browser_globals": "false", ++ "node_shared": "false", ++ "libdir": "lib", ++ "node_module_version": 147, ++ "shlib_suffix": "so.147", ++ "asan": 0, ++ "ubsan": 0, ++ "coverage": "false", ++ "node_target_type": "executable", ++ "node_library_files": [ ++ "lib/_http_agent.js", ++ "lib/_http_client.js", ++ "lib/_http_common.js", ++ "lib/_http_incoming.js", ++ "lib/_http_outgoing.js", ++ "lib/_http_server.js", ++ "lib/_tls_common.js", ++ "lib/_tls_wrap.js", ++ "lib/assert.js", ++ "lib/assert/strict.js", ++ "lib/async_hooks.js", ++ "lib/buffer.js", ++ "lib/child_process.js", ++ "lib/cluster.js", ++ "lib/console.js", ++ "lib/constants.js", ++ "lib/crypto.js", ++ "lib/dgram.js", ++ "lib/diagnostics_channel.js", ++ "lib/dns.js", ++ "lib/dns/promises.js", ++ "lib/domain.js", ++ "lib/dtls.js", ++ "lib/events.js", ++ "lib/ffi.js", ++ "lib/fs.js", ++ "lib/fs/promises.js", ++ "lib/http.js", ++ "lib/http2.js", ++ "lib/https.js", ++ "lib/inspector.js", ++ "lib/inspector/promises.js", ++ "lib/internal/abort_controller.js", ++ "lib/internal/assert.js", ++ "lib/internal/assert/assertion_error.js", ++ "lib/internal/assert/myers_diff.js", ++ "lib/internal/assert/utils.js", ++ "lib/internal/async_context_frame.js", ++ "lib/internal/async_hooks.js", ++ "lib/internal/async_local_storage/async_context_frame.js", ++ "lib/internal/async_local_storage/async_hooks.js", ++ "lib/internal/async_local_storage/run_scope.js", ++ "lib/internal/blob.js", ++ "lib/internal/blocklist.js", ++ "lib/internal/bootstrap/node.js", ++ "lib/internal/bootstrap/realm.js", ++ "lib/internal/bootstrap/shadow_realm.js", ++ "lib/internal/bootstrap/switches/does_not_own_process_state.js", ++ "lib/internal/bootstrap/switches/does_own_process_state.js", ++ "lib/internal/bootstrap/switches/is_main_thread.js", ++ "lib/internal/bootstrap/switches/is_not_main_thread.js", ++ "lib/internal/bootstrap/web/exposed-wildcard.js", ++ "lib/internal/bootstrap/web/exposed-window-or-worker.js", ++ "lib/internal/buffer.js", ++ "lib/internal/child_process.js", ++ "lib/internal/child_process/serialization.js", ++ "lib/internal/cli_table.js", ++ "lib/internal/cluster/child.js", ++ "lib/internal/cluster/primary.js", ++ "lib/internal/cluster/round_robin_handle.js", ++ "lib/internal/cluster/shared_handle.js", ++ "lib/internal/cluster/utils.js", ++ "lib/internal/cluster/worker.js", ++ "lib/internal/console/constructor.js", ++ "lib/internal/console/global.js", ++ "lib/internal/constants.js", ++ "lib/internal/crypto/aes.js", ++ "lib/internal/crypto/argon2.js", ++ "lib/internal/crypto/certificate.js", ++ "lib/internal/crypto/cfrg.js", ++ "lib/internal/crypto/chacha20_poly1305.js", ++ "lib/internal/crypto/cipher.js", ++ "lib/internal/crypto/diffiehellman.js", ++ "lib/internal/crypto/ec.js", ++ "lib/internal/crypto/hash.js", ++ "lib/internal/crypto/hashnames.js", ++ "lib/internal/crypto/hkdf.js", ++ "lib/internal/crypto/kem.js", ++ "lib/internal/crypto/keygen.js", ++ "lib/internal/crypto/keys.js", ++ "lib/internal/crypto/mac.js", ++ "lib/internal/crypto/ml_dsa.js", ++ "lib/internal/crypto/ml_kem.js", ++ "lib/internal/crypto/pbkdf2.js", ++ "lib/internal/crypto/random.js", ++ "lib/internal/crypto/rsa.js", ++ "lib/internal/crypto/scrypt.js", ++ "lib/internal/crypto/sig.js", ++ "lib/internal/crypto/util.js", ++ "lib/internal/crypto/webcrypto.js", ++ "lib/internal/crypto/webcrypto_util.js", ++ "lib/internal/crypto/webidl.js", ++ "lib/internal/crypto/x509.js", ++ "lib/internal/data_url.js", ++ "lib/internal/debugger/inspect.js", ++ "lib/internal/debugger/inspect_client.js", ++ "lib/internal/debugger/inspect_helpers.js", ++ "lib/internal/debugger/inspect_probe.js", ++ "lib/internal/debugger/inspect_repl.js", ++ "lib/internal/dgram.js", ++ "lib/internal/dns/callback_resolver.js", ++ "lib/internal/dns/promises.js", ++ "lib/internal/dns/utils.js", ++ "lib/internal/dtls/dtls.js", ++ "lib/internal/dtls/state.js", ++ "lib/internal/dtls/stats.js", ++ "lib/internal/dtls/symbols.js", ++ "lib/internal/encoding.js", ++ "lib/internal/encoding/single-byte.js", ++ "lib/internal/encoding/util.js", ++ "lib/internal/error_serdes.js", ++ "lib/internal/errors.js", ++ "lib/internal/errors/error_source.js", ++ "lib/internal/event_target.js", ++ "lib/internal/events/abort_listener.js", ++ "lib/internal/events/symbols.js", ++ "lib/internal/ffi-shared-buffer.js", ++ "lib/internal/ffi/fast-api.js", ++ "lib/internal/file.js", ++ "lib/internal/fixed_queue.js", ++ "lib/internal/freelist.js", ++ "lib/internal/freeze_intrinsics.js", ++ "lib/internal/fs/cp/cp-sync.js", ++ "lib/internal/fs/cp/cp.js", ++ "lib/internal/fs/dir.js", ++ "lib/internal/fs/glob.js", ++ "lib/internal/fs/promises.js", ++ "lib/internal/fs/read/context.js", ++ "lib/internal/fs/recursive_watch.js", ++ "lib/internal/fs/rimraf.js", ++ "lib/internal/fs/streams.js", ++ "lib/internal/fs/sync_write_stream.js", ++ "lib/internal/fs/utils.js", ++ "lib/internal/fs/watchers.js", ++ "lib/internal/heap_utils.js", ++ "lib/internal/histogram.js", ++ "lib/internal/http.js", ++ "lib/internal/http2/compat.js", ++ "lib/internal/http2/core.js", ++ "lib/internal/http2/util.js", ++ "lib/internal/inspector/network.js", ++ "lib/internal/inspector/network_http.js", ++ "lib/internal/inspector/network_http2.js", ++ "lib/internal/inspector/network_resources.js", ++ "lib/internal/inspector/network_undici.js", ++ "lib/internal/inspector/webstorage.js", ++ "lib/internal/inspector_async_hook.js", ++ "lib/internal/inspector_network_tracking.js", ++ "lib/internal/js_stream_socket.js", ++ "lib/internal/legacy/processbinding.js", ++ "lib/internal/linkedlist.js", ++ "lib/internal/locks.js", ++ "lib/internal/main/check_syntax.js", ++ "lib/internal/main/embedding.js", ++ "lib/internal/main/eval_stdin.js", ++ "lib/internal/main/eval_string.js", ++ "lib/internal/main/inspect.js", ++ "lib/internal/main/mksnapshot.js", ++ "lib/internal/main/print_help.js", ++ "lib/internal/main/prof_process.js", ++ "lib/internal/main/repl.js", ++ "lib/internal/main/run_main_module.js", ++ "lib/internal/main/test_runner.js", ++ "lib/internal/main/watch_mode.js", ++ "lib/internal/main/worker_thread.js", ++ "lib/internal/mime.js", ++ "lib/internal/modules/cjs/loader.js", ++ "lib/internal/modules/customization_hooks.js", ++ "lib/internal/modules/esm/assert.js", ++ "lib/internal/modules/esm/create_dynamic_module.js", ++ "lib/internal/modules/esm/get_format.js", ++ "lib/internal/modules/esm/hooks.js", ++ "lib/internal/modules/esm/load.js", ++ "lib/internal/modules/esm/loader.js", ++ "lib/internal/modules/esm/module_job.js", ++ "lib/internal/modules/esm/module_map.js", ++ "lib/internal/modules/esm/resolve.js", ++ "lib/internal/modules/esm/shared_constants.js", ++ "lib/internal/modules/esm/translators.js", ++ "lib/internal/modules/esm/utils.js", ++ "lib/internal/modules/esm/worker.js", ++ "lib/internal/modules/helpers.js", ++ "lib/internal/modules/package_json_reader.js", ++ "lib/internal/modules/package_map.js", ++ "lib/internal/modules/run_main.js", ++ "lib/internal/modules/typescript.js", ++ "lib/internal/navigator.js", ++ "lib/internal/net.js", ++ "lib/internal/options.js", ++ "lib/internal/per_context/domexception.js", ++ "lib/internal/per_context/messageport.js", ++ "lib/internal/per_context/primordials.js", ++ "lib/internal/perf/event_loop_delay.js", ++ "lib/internal/perf/event_loop_utilization.js", ++ "lib/internal/perf/nodetiming.js", ++ "lib/internal/perf/observe.js", ++ "lib/internal/perf/performance.js", ++ "lib/internal/perf/performance_entry.js", ++ "lib/internal/perf/resource_timing.js", ++ "lib/internal/perf/timerify.js", ++ "lib/internal/perf/usertiming.js", ++ "lib/internal/perf/utils.js", ++ "lib/internal/priority_queue.js", ++ "lib/internal/process/execution.js", ++ "lib/internal/process/finalization.js", ++ "lib/internal/process/per_thread.js", ++ "lib/internal/process/permission.js", ++ "lib/internal/process/pre_execution.js", ++ "lib/internal/process/promises.js", ++ "lib/internal/process/report.js", ++ "lib/internal/process/signal.js", ++ "lib/internal/process/task_queues.js", ++ "lib/internal/process/warning.js", ++ "lib/internal/process/worker_thread_only.js", ++ "lib/internal/promise_hooks.js", ++ "lib/internal/querystring.js", ++ "lib/internal/quic/diagnostics.js", ++ "lib/internal/quic/quic.js", ++ "lib/internal/quic/state.js", ++ "lib/internal/quic/stats.js", ++ "lib/internal/quic/symbols.js", ++ "lib/internal/readline/callbacks.js", ++ "lib/internal/readline/emitKeypressEvents.js", ++ "lib/internal/readline/interface.js", ++ "lib/internal/readline/promises.js", ++ "lib/internal/readline/utils.js", ++ "lib/internal/repl.js", ++ "lib/internal/repl/await.js", ++ "lib/internal/repl/completion.js", ++ "lib/internal/repl/history.js", ++ "lib/internal/repl/utils.js", ++ "lib/internal/socket_list.js", ++ "lib/internal/socketaddress.js", ++ "lib/internal/source_map/prepare_stack_trace.js", ++ "lib/internal/source_map/source_map.js", ++ "lib/internal/source_map/source_map_cache.js", ++ "lib/internal/source_map/source_map_cache_map.js", ++ "lib/internal/stream_base_commons.js", ++ "lib/internal/streams/add-abort-signal.js", ++ "lib/internal/streams/compose.js", ++ "lib/internal/streams/destroy.js", ++ "lib/internal/streams/duplex.js", ++ "lib/internal/streams/duplexify.js", ++ "lib/internal/streams/duplexpair.js", ++ "lib/internal/streams/end-of-stream.js", ++ "lib/internal/streams/fast-utf8-stream.js", ++ "lib/internal/streams/from.js", ++ "lib/internal/streams/iter/broadcast.js", ++ "lib/internal/streams/iter/classic.js", ++ "lib/internal/streams/iter/consumers.js", ++ "lib/internal/streams/iter/duplex.js", ++ "lib/internal/streams/iter/from.js", ++ "lib/internal/streams/iter/pull.js", ++ "lib/internal/streams/iter/push.js", ++ "lib/internal/streams/iter/ringbuffer.js", ++ "lib/internal/streams/iter/share.js", ++ "lib/internal/streams/iter/transform.js", ++ "lib/internal/streams/iter/types.js", ++ "lib/internal/streams/iter/utils.js", ++ "lib/internal/streams/lazy_transform.js", ++ "lib/internal/streams/legacy.js", ++ "lib/internal/streams/operators.js", ++ "lib/internal/streams/passthrough.js", ++ "lib/internal/streams/pipeline.js", ++ "lib/internal/streams/readable.js", ++ "lib/internal/streams/state.js", ++ "lib/internal/streams/transform.js", ++ "lib/internal/streams/utils.js", ++ "lib/internal/streams/writable.js", ++ "lib/internal/test/binding.js", ++ "lib/internal/test/transfer.js", ++ "lib/internal/test_runner/assert.js", ++ "lib/internal/test_runner/coverage.js", ++ "lib/internal/test_runner/harness.js", ++ "lib/internal/test_runner/mock/loader.js", ++ "lib/internal/test_runner/mock/mock.js", ++ "lib/internal/test_runner/mock/mock_timers.js", ++ "lib/internal/test_runner/reporter/dot.js", ++ "lib/internal/test_runner/reporter/junit.js", ++ "lib/internal/test_runner/reporter/lcov.js", ++ "lib/internal/test_runner/reporter/rerun.js", ++ "lib/internal/test_runner/reporter/spec.js", ++ "lib/internal/test_runner/reporter/tap.js", ++ "lib/internal/test_runner/reporter/utils.js", ++ "lib/internal/test_runner/reporter/v8-serializer.js", ++ "lib/internal/test_runner/runner.js", ++ "lib/internal/test_runner/snapshot.js", ++ "lib/internal/test_runner/tag_filter.js", ++ "lib/internal/test_runner/test.js", ++ "lib/internal/test_runner/tests_stream.js", ++ "lib/internal/test_runner/utils.js", ++ "lib/internal/timers.js", ++ "lib/internal/tls/common.js", ++ "lib/internal/tls/secure-context.js", ++ "lib/internal/tls/wrap.js", ++ "lib/internal/trace_events_async_hooks.js", ++ "lib/internal/tty.js", ++ "lib/internal/url.js", ++ "lib/internal/util.js", ++ "lib/internal/util/colors.js", ++ "lib/internal/util/comparisons.js", ++ "lib/internal/util/debuglog.js", ++ "lib/internal/util/diff.js", ++ "lib/internal/util/inspect.js", ++ "lib/internal/util/inspector.js", ++ "lib/internal/util/parse_args/parse_args.js", ++ "lib/internal/util/parse_args/utils.js", ++ "lib/internal/util/trace_sigint.js", ++ "lib/internal/util/types.js", ++ "lib/internal/v8/cpu_profiler.js", ++ "lib/internal/v8/heap_profile.js", ++ "lib/internal/v8/startup_snapshot.js", ++ "lib/internal/v8_prof_polyfill.js", ++ "lib/internal/validators.js", ++ "lib/internal/vfs/dir.js", ++ "lib/internal/vfs/errors.js", ++ "lib/internal/vfs/fd.js", ++ "lib/internal/vfs/file_handle.js", ++ "lib/internal/vfs/file_system.js", ++ "lib/internal/vfs/provider.js", ++ "lib/internal/vfs/providers/memory.js", ++ "lib/internal/vfs/providers/real.js", ++ "lib/internal/vfs/router.js", ++ "lib/internal/vfs/setup.js", ++ "lib/internal/vfs/stats.js", ++ "lib/internal/vfs/streams.js", ++ "lib/internal/vfs/watcher.js", ++ "lib/internal/vm.js", ++ "lib/internal/vm/module.js", ++ "lib/internal/wasm_web_api.js", ++ "lib/internal/watch_mode/files_watcher.js", ++ "lib/internal/watchdog.js", ++ "lib/internal/webidl.js", ++ "lib/internal/webstorage.js", ++ "lib/internal/webstreams/adapters.js", ++ "lib/internal/webstreams/compression.js", ++ "lib/internal/webstreams/encoding.js", ++ "lib/internal/webstreams/queuingstrategies.js", ++ "lib/internal/webstreams/readablestream.js", ++ "lib/internal/webstreams/transfer.js", ++ "lib/internal/webstreams/transformstream.js", ++ "lib/internal/webstreams/util.js", ++ "lib/internal/webstreams/writablestream.js", ++ "lib/internal/worker.js", ++ "lib/internal/worker/clone_dom_exception.js", ++ "lib/internal/worker/io.js", ++ "lib/internal/worker/js_transferable.js", ++ "lib/internal/worker/messaging.js", ++ "lib/module.js", ++ "lib/net.js", ++ "lib/os.js", ++ "lib/path.js", ++ "lib/path/posix.js", ++ "lib/path/win32.js", ++ "lib/perf_hooks.js", ++ "lib/process.js", ++ "lib/punycode.js", ++ "lib/querystring.js", ++ "lib/quic.js", ++ "lib/readline.js", ++ "lib/readline/promises.js", ++ "lib/repl.js", ++ "lib/sea.js", ++ "lib/sqlite.js", ++ "lib/stream.js", ++ "lib/stream/consumers.js", ++ "lib/stream/iter.js", ++ "lib/stream/promises.js", ++ "lib/stream/web.js", ++ "lib/string_decoder.js", ++ "lib/sys.js", ++ "lib/test.js", ++ "lib/test/reporters.js", ++ "lib/timers.js", ++ "lib/timers/promises.js", ++ "lib/tls.js", ++ "lib/trace_events.js", ++ "lib/tty.js", ++ "lib/url.js", ++ "lib/util.js", ++ "lib/util/types.js", ++ "lib/v8.js", ++ "lib/vfs.js", ++ "lib/vm.js", ++ "lib/wasi.js", ++ "lib/worker_threads.js", ++ "lib/zlib.js", ++ "lib/zlib/iter.js" ++ ], ++ "node_cctest_sources": [ ++ "test/cctest/inspector/test_network_requests_buffer.cc", ++ "test/cctest/inspector/test_node_protocol.cc", ++ "test/cctest/node_test_fixture.cc", ++ "test/cctest/test_aliased_buffer.cc", ++ "test/cctest/test_base64.cc", ++ "test/cctest/test_base_object_ptr.cc", ++ "test/cctest/test_cppgc.cc", ++ "test/cctest/test_crypto_clienthello.cc", ++ "test/cctest/test_dataqueue.cc", ++ "test/cctest/test_diagnostics_channel.cc", ++ "test/cctest/test_environment.cc", ++ "test/cctest/test_inspector_socket.cc", ++ "test/cctest/test_inspector_socket_server.cc", ++ "test/cctest/test_json_utils.cc", ++ "test/cctest/test_linked_binding.cc", ++ "test/cctest/test_lru_cache.cc", ++ "test/cctest/test_node_api.cc", ++ "test/cctest/test_node_crypto.cc", ++ "test/cctest/test_node_crypto_env.cc", ++ "test/cctest/test_node_ipc_serdes.cc", ++ "test/cctest/test_node_postmortem_metadata.cc", ++ "test/cctest/test_node_task_runner.cc", ++ "test/cctest/test_path.cc", ++ "test/cctest/test_per_process.cc", ++ "test/cctest/test_platform.cc", ++ "test/cctest/test_quic_arena.cc", ++ "test/cctest/test_quic_cid.cc", ++ "test/cctest/test_quic_error.cc", ++ "test/cctest/test_quic_preferredaddress.cc", ++ "test/cctest/test_quic_tokenbucket.cc", ++ "test/cctest/test_quic_tokens.cc", ++ "test/cctest/test_report.cc", ++ "test/cctest/test_sockaddr.cc", ++ "test/cctest/test_string_bytes.cc", ++ "test/cctest/test_traced_value.cc", ++ "test/cctest/test_util.cc", ++ "test/cctest/node_test_fixture.h" ++ ], ++ "napi_build_version": "10", ++ "node_shared_zlib": "false", ++ "node_shared_http_parser": "false", ++ "node_shared_libuv": "false", ++ "node_shared_ada": "false", ++ "node_shared_simdjson": "false", ++ "node_shared_simdutf": "false", ++ "node_shared_brotli": "false", ++ "node_shared_cares": "false", ++ "node_shared_gtest": "false", ++ "node_shared_hdr_histogram": "false", ++ "node_shared_merve": "false", ++ "node_shared_nbytes": "false", ++ "node_shared_nghttp2": "false", ++ "node_shared_nghttp3": "false", ++ "node_shared_ngtcp2": "false", ++ "node_shared_lief": "false", ++ "node_use_sqlite": "true", ++ "node_shared_sqlite": "false", ++ "node_use_ffi": "true", ++ "node_shared_ffi": "false", ++ "node_shared_temporal_capi": "false", ++ "node_shared_uvwasi": "false", ++ "node_shared_zstd": "false", ++ "v8_enable_webassembly": 1, ++ "v8_enable_javascript_promise_hooks": 1, ++ "v8_enable_lite_mode": 0, ++ "v8_enable_gdbjit": 1, ++ "v8_optimized_debug": 1, ++ "dcheck_always_on": 0, ++ "v8_enable_object_print": 1, ++ "v8_random_seed": 0, ++ "v8_promise_internal_field_count": 1, ++ "v8_use_siphash": 1, ++ "v8_enable_maglev": 1, ++ "v8_enable_pointer_compression": 0, ++ "v8_enable_sandbox": 0, ++ "v8_enable_pointer_compression_shared_cage": 0, ++ "v8_enable_external_code_space": 0, ++ "v8_enable_31bit_smis_on_64bit_arch": 0, ++ "v8_enable_extensible_ro_snapshot": 0, ++ "v8_enable_temporal_support": 0, ++ "v8_trace_maps": 0, ++ "node_use_v8_platform": "true", ++ "node_use_bundled_v8": "true", ++ "force_dynamic_crt": 0, ++ "node_enable_d8": "false", ++ "node_enable_v8windbg": "false", ++ "v8_enable_hugepage": 0, ++ "v8_enable_short_builtin_calls": 1, ++ "v8_enable_wasm_simd256_revec": 1, ++ "node_use_openssl": "true", ++ "node_shared_openssl": "false", ++ "openssl_is_fips": "false", ++ "node_fipsinstall": "false", ++ "node_without_node_options": "false", ++ "openssl_version": 810549375, ++ "node_use_quic": "false", ++ "node_use_dtls": "false", ++ "icu_small": "false", ++ "icu_system": "false", ++ "v8_enable_i18n_support": 1, ++ "icu_gyp_path": "tools/icu/icu-generic.gyp", ++ "icu_path": "deps/icu-small", ++ "icu_ver_major": "78", ++ "icu_endianness": "l", ++ "icu_data_in": "../../deps/icu-tmp/icudt78l.dat", ++ "v8_enable_inspector": 1, ++ "node_section_ordering_info": "", ++ "node_builtin_shareable_builtins": [ ++ "deps/undici/undici.js", ++ "deps/amaro/dist/index.js" ++ ], ++ "ossfuzz": "false", ++ "v8_enable_v8_checks": 0, ++ "nodedir": "/home/rafaelgss/repos/os/node-private/out/Release/addons_headers", ++ "python": "/usr/bin/python3.12", ++ "standalone_static_library": 1 ++ }, ++ "target_defaults": { ++ "include_dirs": [], ++ "libraries": [], ++ "defines": [], ++ "cflags": [], ++ "conditions": [], ++ "default_configuration": "Release", ++ "configurations": { ++ "Release": {}, ++ "Debug": {} ++ } ++ } ++} +--- /dev/null ++++ b/test/ffi/fixture_library/build/ffi_test_library.target.mk +@@ -0,0 +1,155 @@ ++# This file is generated by gyp; do not edit. ++ ++TOOLSET := target ++TARGET := ffi_test_library ++DEFS_Debug := \ ++ '-DNODE_GYP_MODULE_NAME=ffi_test_library' \ ++ '-DUSING_UV_SHARED=1' \ ++ '-DUSING_V8_SHARED=1' \ ++ '-DV8_DEPRECATION_WARNINGS=1' \ ++ '-D_GLIBCXX_USE_CXX11_ABI=1' \ ++ '-D_FILE_OFFSET_BITS=64' \ ++ '-D_LARGEFILE_SOURCE' \ ++ '-D__STDC_FORMAT_MACROS' \ ++ '-DOPENSSL_NO_PINSHARED' \ ++ '-DOPENSSL_THREADS' \ ++ '-DDEBUG' \ ++ '-D_DEBUG' ++ ++# Flags passed to all source files. ++CFLAGS_Debug := \ ++ -fPIC \ ++ -pthread \ ++ -Wall \ ++ -Wextra \ ++ -Wno-unused-parameter \ ++ -m64 \ ++ -g \ ++ -O0 ++ ++# Flags passed to only C files. ++CFLAGS_C_Debug := ++ ++# Flags passed to only C++ files. ++CFLAGS_CC_Debug := \ ++ -fno-rtti \ ++ -fno-exceptions \ ++ -fno-strict-aliasing \ ++ -std=gnu++20 ++ ++INCS_Debug := \ ++ -I/home/rafaelgss/repos/os/node-private/out/Release/addons_headers/include/node \ ++ -I/home/rafaelgss/repos/os/node-private/out/Release/addons_headers/src \ ++ -I/home/rafaelgss/repos/os/node-private/out/Release/addons_headers/deps/openssl/config \ ++ -I/home/rafaelgss/repos/os/node-private/out/Release/addons_headers/deps/openssl/openssl/include \ ++ -I/home/rafaelgss/repos/os/node-private/out/Release/addons_headers/deps/uv/include \ ++ -I/home/rafaelgss/repos/os/node-private/out/Release/addons_headers/deps/zlib \ ++ -I/home/rafaelgss/repos/os/node-private/out/Release/addons_headers/deps/v8/include ++ ++DEFS_Release := \ ++ '-DNODE_GYP_MODULE_NAME=ffi_test_library' \ ++ '-DUSING_UV_SHARED=1' \ ++ '-DUSING_V8_SHARED=1' \ ++ '-DV8_DEPRECATION_WARNINGS=1' \ ++ '-D_GLIBCXX_USE_CXX11_ABI=1' \ ++ '-D_FILE_OFFSET_BITS=64' \ ++ '-D_LARGEFILE_SOURCE' \ ++ '-D__STDC_FORMAT_MACROS' \ ++ '-DOPENSSL_NO_PINSHARED' \ ++ '-DOPENSSL_THREADS' ++ ++# Flags passed to all source files. ++CFLAGS_Release := \ ++ -fPIC \ ++ -pthread \ ++ -Wall \ ++ -Wextra \ ++ -Wno-unused-parameter \ ++ -m64 \ ++ -O3 \ ++ -fno-omit-frame-pointer ++ ++# Flags passed to only C files. ++CFLAGS_C_Release := ++ ++# Flags passed to only C++ files. ++CFLAGS_CC_Release := \ ++ -fno-rtti \ ++ -fno-exceptions \ ++ -fno-strict-aliasing \ ++ -std=gnu++20 ++ ++INCS_Release := \ ++ -I/home/rafaelgss/repos/os/node-private/out/Release/addons_headers/include/node \ ++ -I/home/rafaelgss/repos/os/node-private/out/Release/addons_headers/src \ ++ -I/home/rafaelgss/repos/os/node-private/out/Release/addons_headers/deps/openssl/config \ ++ -I/home/rafaelgss/repos/os/node-private/out/Release/addons_headers/deps/openssl/openssl/include \ ++ -I/home/rafaelgss/repos/os/node-private/out/Release/addons_headers/deps/uv/include \ ++ -I/home/rafaelgss/repos/os/node-private/out/Release/addons_headers/deps/zlib \ ++ -I/home/rafaelgss/repos/os/node-private/out/Release/addons_headers/deps/v8/include ++ ++OBJS := \ ++ $(obj).target/$(TARGET)/ffi_test_library.o ++ ++# Add to the list of files we specially track dependencies for. ++all_deps += $(OBJS) ++ ++# CFLAGS et al overrides must be target-local. ++# See "Target-specific Variable Values" in the GNU Make manual. ++$(OBJS): TOOLSET := $(TOOLSET) ++$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) ++$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) ++ ++# Suffix rules, putting all outputs into $(obj). ++ ++$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD ++ @$(call do_cmd,cc,1) ++ ++# Try building from generated source, too. ++ ++$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD ++ @$(call do_cmd,cc,1) ++ ++$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD ++ @$(call do_cmd,cc,1) ++ ++# End of this set of suffix rules ++### Rules for final target. ++LDFLAGS_Debug := \ ++ -pthread \ ++ -rdynamic \ ++ -m64 ++ ++LDFLAGS_Release := \ ++ -pthread \ ++ -rdynamic \ ++ -m64 ++ ++LIBS := ++ ++$(obj).target/ffi_test_library.so: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) ++$(obj).target/ffi_test_library.so: LIBS := $(LIBS) ++$(obj).target/ffi_test_library.so: LD_INPUTS := $(OBJS) ++$(obj).target/ffi_test_library.so: TOOLSET := $(TOOLSET) ++$(obj).target/ffi_test_library.so: $(OBJS) FORCE_DO_CMD ++ $(call do_cmd,solink) ++ ++all_deps += $(obj).target/ffi_test_library.so ++# Add target alias ++.PHONY: ffi_test_library ++ffi_test_library: $(builddir)/ffi_test_library.so ++ ++# Copy this to the shared library output path. ++$(builddir)/ffi_test_library.so: TOOLSET := $(TOOLSET) ++$(builddir)/ffi_test_library.so: $(obj).target/ffi_test_library.so FORCE_DO_CMD ++ $(call do_cmd,copy) ++ ++all_deps += $(builddir)/ffi_test_library.so ++# Short alias for building this shared library. ++.PHONY: ffi_test_library.so ++ffi_test_library.so: $(obj).target/ffi_test_library.so $(builddir)/ffi_test_library.so ++ ++# Add shared library to "all" target. ++.PHONY: all ++all: $(builddir)/ffi_test_library.so ++ +--- /dev/null ++++ b/test/node_trace.1.log +@@ -0,0 +1 @@ ++{"traceEvents":[{"pid":1610388,"tid":1610388,"ts":105658229815,"tts":7219,"ph":"b","cat":"node,node.environment","name":"Environment","dur":0,"tdur":0,"id":"0x62b917612c10","args":{"args":{"args":["out/Release/node","/home/rafaelgss/repos/os/node-private/test/parallel/test-permission-fs-write-trace-events.js"],"exec_args":["--expose-internals","--trace-event-categories=node"]}}},{"pid":1610388,"tid":1610388,"ts":105658229790,"tts":8908,"ph":"I","cat":"node,node.bootstrap","name":"environment","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658223625,"tts":8909,"ph":"I","cat":"node,node.bootstrap","name":"nodeStart","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658224800,"tts":8909,"ph":"I","cat":"node,node.bootstrap","name":"v8Start","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658231956,"tts":9360,"ph":"I","cat":"node,node.bootstrap","name":"bootstrapComplete","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232063,"tts":9467,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.lstat","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232065,"tts":9468,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.lstat","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232068,"tts":9472,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.lstat","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232069,"tts":9472,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.lstat","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232070,"tts":9474,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.lstat","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232071,"tts":9475,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.lstat","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232072,"tts":9476,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.lstat","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232073,"tts":9476,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.lstat","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232074,"tts":9477,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.lstat","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232075,"tts":9478,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.lstat","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232076,"tts":9480,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.lstat","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232077,"tts":9481,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.lstat","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232078,"tts":9482,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.lstat","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232079,"tts":9482,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.lstat","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232080,"tts":9484,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.lstat","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232081,"tts":9485,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.lstat","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232149,"tts":9552,"ph":"b","cat":"node,node.module_timer","name":"require('/home/rafaelgss/repos/os/node-private/test/parallel/test-permission-fs-write-trace-events.js')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658232226,"tts":9630,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.open","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232229,"tts":9632,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.open","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232229,"tts":9633,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.read","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232232,"tts":9635,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.read","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232233,"tts":9636,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.close","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232234,"tts":9638,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.close","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232341,"tts":9744,"ph":"b","cat":"node,node.module_timer","name":"require('../common')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658232466,"tts":9870,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.lstat","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232468,"tts":9871,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.lstat","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232472,"tts":9875,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.lstat","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232473,"tts":9876,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.lstat","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232501,"tts":9905,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.open","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232503,"tts":9906,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.open","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232503,"tts":9907,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.read","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232508,"tts":9912,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.read","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232526,"tts":9930,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.close","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232527,"tts":9931,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.close","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658232956,"tts":10360,"ph":"b","cat":"node,node.module_timer","name":"require('assert')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658234040,"tts":11431,"ph":"e","cat":"node,node.module_timer","name":"require('assert')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658234046,"tts":11437,"ph":"b","cat":"node,node.module_timer","name":"require('fs')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658234067,"tts":11459,"ph":"e","cat":"node,node.module_timer","name":"require('fs')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658234070,"tts":11461,"ph":"b","cat":"node,node.module_timer","name":"require('net')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658234449,"tts":11841,"ph":"e","cat":"node,node.module_timer","name":"require('net')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658234454,"tts":11845,"ph":"b","cat":"node,node.module_timer","name":"require('path')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658234465,"tts":11856,"ph":"e","cat":"node,node.module_timer","name":"require('path')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658234467,"tts":11858,"ph":"b","cat":"node,node.module_timer","name":"require('util')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658234475,"tts":11866,"ph":"e","cat":"node,node.module_timer","name":"require('util')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658234477,"tts":11868,"ph":"b","cat":"node,node.module_timer","name":"require('worker_threads')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658234770,"tts":12161,"ph":"e","cat":"node,node.module_timer","name":"require('worker_threads')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658234773,"tts":12164,"ph":"b","cat":"node,node.module_timer","name":"require('./tmpdir')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658234859,"tts":12250,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.lstat","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658234860,"tts":12251,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.lstat","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658234890,"tts":12281,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.open","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658234893,"tts":12285,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.open","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658234894,"tts":12285,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.read","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658234896,"tts":12287,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.read","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658234898,"tts":12289,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.close","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658234899,"tts":12290,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.close","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658234997,"tts":12389,"ph":"b","cat":"node,node.module_timer","name":"require('child_process')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658235317,"tts":12708,"ph":"e","cat":"node,node.module_timer","name":"require('child_process')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658235321,"tts":12712,"ph":"b","cat":"node,node.module_timer","name":"require('fs')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658235329,"tts":12720,"ph":"e","cat":"node,node.module_timer","name":"require('fs')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658235330,"tts":12722,"ph":"b","cat":"node,node.module_timer","name":"require('path')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658235339,"tts":12730,"ph":"e","cat":"node,node.module_timer","name":"require('path')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658235343,"tts":12734,"ph":"b","cat":"node,node.module_timer","name":"require('url')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658235353,"tts":12744,"ph":"e","cat":"node,node.module_timer","name":"require('url')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658235356,"tts":12747,"ph":"b","cat":"node,node.module_timer","name":"require('worker_threads')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658235360,"tts":12751,"ph":"e","cat":"node,node.module_timer","name":"require('worker_threads')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658235381,"tts":12772,"ph":"e","cat":"node,node.module_timer","name":"require('./tmpdir')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658235384,"tts":12775,"ph":"b","cat":"node,node.module_timer","name":"require('buffer')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658235391,"tts":12782,"ph":"e","cat":"node,node.module_timer","name":"require('buffer')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658235443,"tts":12834,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.open","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658235448,"tts":12839,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.open","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658235448,"tts":12839,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.read","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658235542,"tts":12933,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.read","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658235548,"tts":12939,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.close","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658235550,"tts":12941,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.close","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658237967,"tts":15354,"ph":"b","cat":"node,node.module_timer","name":"require('node:worker_threads')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658237982,"tts":15368,"ph":"e","cat":"node,node.module_timer","name":"require('node:worker_threads')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658238050,"tts":15436,"ph":"e","cat":"node,node.module_timer","name":"require('../common')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658238052,"tts":15438,"ph":"b","cat":"node,node.module_timer","name":"require('../common/child_process')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658238095,"tts":15481,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.lstat","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658238096,"tts":15482,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.lstat","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658238134,"tts":15520,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.open","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658238137,"tts":15523,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.open","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658238138,"tts":15524,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.read","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658238140,"tts":15526,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.read","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658238142,"tts":15528,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.close","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658238143,"tts":15529,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.close","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658238250,"tts":15636,"ph":"b","cat":"node,node.module_timer","name":"require('assert')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658238258,"tts":15644,"ph":"e","cat":"node,node.module_timer","name":"require('assert')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658238260,"tts":15646,"ph":"b","cat":"node,node.module_timer","name":"require('child_process')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658238263,"tts":15649,"ph":"e","cat":"node,node.module_timer","name":"require('child_process')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658238264,"tts":15650,"ph":"b","cat":"node,node.module_timer","name":"require('./')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658238299,"tts":15685,"ph":"e","cat":"node,node.module_timer","name":"require('./')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658238300,"tts":15686,"ph":"b","cat":"node,node.module_timer","name":"require('util')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658238302,"tts":15688,"ph":"e","cat":"node,node.module_timer","name":"require('util')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658238323,"tts":15709,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.open","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658238326,"tts":15712,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.open","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658238327,"tts":15713,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.read","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658238417,"tts":15803,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.read","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658238422,"tts":15808,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.close","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658238424,"tts":15810,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.close","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658238462,"tts":15848,"ph":"e","cat":"node,node.module_timer","name":"require('../common/child_process')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658238464,"tts":15850,"ph":"b","cat":"node,node.module_timer","name":"require('worker_threads')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658238468,"tts":15854,"ph":"e","cat":"node,node.module_timer","name":"require('worker_threads')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658238469,"tts":15855,"ph":"b","cat":"node,node.module_timer","name":"require('assert')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658238471,"tts":15857,"ph":"e","cat":"node,node.module_timer","name":"require('assert')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658238472,"tts":15858,"ph":"b","cat":"node,node.module_timer","name":"require('fs')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658238474,"tts":15860,"ph":"e","cat":"node,node.module_timer","name":"require('fs')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658238475,"tts":15861,"ph":"b","cat":"node,node.module_timer","name":"require('../common/tmpdir')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658238496,"tts":15882,"ph":"e","cat":"node,node.module_timer","name":"require('../common/tmpdir')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658238497,"tts":15883,"ph":"b","cat":"node,node.module_timer","name":"require('trace_events')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658238532,"tts":15918,"ph":"e","cat":"node,node.module_timer","name":"require('trace_events')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658238588,"tts":15974,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.mkdir","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658238617,"tts":16003,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.mkdir","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658238638,"tts":16024,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.mkdir","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658238647,"tts":16033,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.mkdir","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658238648,"tts":16034,"ph":"B","cat":"node,node.fs,node.fs.sync","name":"fs.sync.mkdir","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658238655,"tts":16041,"ph":"E","cat":"node,node.fs,node.fs.sync","name":"fs.sync.mkdir","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658256686,"tts":16845,"ph":"e","cat":"node,node.module_timer","name":"require('/home/rafaelgss/repos/os/node-private/test/parallel/test-permission-fs-write-trace-events.js')","dur":0,"tdur":0,"id":"0x0","args":{}},{"pid":1610388,"tid":1610388,"ts":105658256692,"tts":16851,"ph":"I","cat":"node,node.bootstrap","name":"loopStart","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658256982,"tts":17141,"ph":"X","cat":"node,node.environment","name":"BeforeExit","dur":5,"tdur":5,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658256987,"tts":17147,"ph":"I","cat":"node,node.bootstrap","name":"loopExit","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658257177,"tts":17336,"ph":"X","cat":"node,node.environment","name":"RunCleanup","dur":17,"tdur":17,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658257179,"tts":17338,"ph":"X","cat":"node,node.environment","name":"RunAndClearNativeImmediates","dur":0,"tdur":1,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658257189,"tts":17348,"ph":"X","cat":"node,node.realm","name":"RunCleanup","dur":4,"tdur":4,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658257193,"tts":17353,"ph":"X","cat":"node,node.environment","name":"RunAndClearNativeImmediates","dur":0,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658257194,"tts":17354,"ph":"X","cat":"node,node.environment","name":"AtExit","dur":1,"tdur":0,"args":{}},{"pid":1610388,"tid":1610388,"ts":105658257202,"tts":17361,"ph":"e","cat":"node,node.environment","name":"Environment","dur":0,"tdur":0,"id":"0x62b917612c10","args":{}},{"pid":1610388,"tid":1610388,"ts":105658224566,"tts":2008,"ph":"M","cat":"__metadata","name":"process_name","dur":0,"tdur":0,"args":{"name":"out/Release/node"}},{"pid":1610388,"tid":1610388,"ts":105658224568,"tts":2010,"ph":"M","cat":"__metadata","name":"version","dur":0,"tdur":0,"args":{"node":"24.18.1-pre"}},{"pid":1610388,"tid":1610388,"ts":105658224568,"tts":2010,"ph":"M","cat":"__metadata","name":"thread_name","dur":0,"tdur":0,"args":{"name":"JavaScriptMainThread"}},{"pid":1610388,"tid":1610388,"ts":105658224575,"tts":2017,"ph":"M","cat":"__metadata","name":"node","dur":0,"tdur":0,"args":{"process":{"versions":{"acorn":"8.16.0","ada":"3.4.4","amaro":"1.1.9","ares":"1.34.6","brotli":"1.2.0","cldr":"48.0","icu":"78.3","llhttp":"9.4.2","merve":"1.2.2","modules":"137","napi":"10","nbytes":"0.1.4","ncrypto":"0.0.1","nghttp2":"1.69.0","nghttp3":"","ngtcp2":"","node":"24.18.1-pre","openssl":"3.5.7","simdjson":"4.6.4","simdutf":"6.4.0","sqlite":"3.53.1","tz":"2026b","undici":"7.28.0","unicode":"17.0","uv":"1.52.1","uvwasi":"0.0.23","v8":"13.6.233.17-node.50","zlib":"1.3.1-e00f703","zstd":"1.5.7"},"arch":"x64","platform":"linux","release":{"name":"node","lts":"Krypton"}}}},{"pid":1610388,"tid":1610400,"ts":105658224596,"tts":13,"ph":"M","cat":"__metadata","name":"thread_name","dur":0,"tdur":0,"args":{"name":"WorkerThreadsTaskRunner::DelayedTaskScheduler"}},{"pid":1610388,"tid":1610401,"ts":105658224646,"tts":15,"ph":"M","cat":"__metadata","name":"thread_name","dur":0,"tdur":0,"args":{"name":"PlatformWorkerThread"}},{"pid":1610388,"tid":1610402,"ts":105658224654,"tts":16,"ph":"M","cat":"__metadata","name":"thread_name","dur":0,"tdur":0,"args":{"name":"PlatformWorkerThread"}},{"pid":1610388,"tid":1610403,"ts":105658224666,"tts":20,"ph":"M","cat":"__metadata","name":"thread_name","dur":0,"tdur":0,"args":{"name":"PlatformWorkerThread"}},{"pid":1610388,"tid":1610404,"ts":105658224670,"tts":8,"ph":"M","cat":"__metadata","name":"thread_name","dur":0,"tdur":0,"args":{"name":"PlatformWorkerThread"}},{"pid":1610388,"tid":1610388,"ts":105658224566,"tts":2008,"ph":"M","cat":"__metadata","name":"process_name","dur":0,"tdur":0,"args":{"name":"out/Release/node"}},{"pid":1610388,"tid":1610388,"ts":105658224568,"tts":2010,"ph":"M","cat":"__metadata","name":"version","dur":0,"tdur":0,"args":{"node":"24.18.1-pre"}},{"pid":1610388,"tid":1610388,"ts":105658224568,"tts":2010,"ph":"M","cat":"__metadata","name":"thread_name","dur":0,"tdur":0,"args":{"name":"JavaScriptMainThread"}},{"pid":1610388,"tid":1610388,"ts":105658224575,"tts":2017,"ph":"M","cat":"__metadata","name":"node","dur":0,"tdur":0,"args":{"process":{"versions":{"acorn":"8.16.0","ada":"3.4.4","amaro":"1.1.9","ares":"1.34.6","brotli":"1.2.0","cldr":"48.0","icu":"78.3","llhttp":"9.4.2","merve":"1.2.2","modules":"137","napi":"10","nbytes":"0.1.4","ncrypto":"0.0.1","nghttp2":"1.69.0","nghttp3":"","ngtcp2":"","node":"24.18.1-pre","openssl":"3.5.7","simdjson":"4.6.4","simdutf":"6.4.0","sqlite":"3.53.1","tz":"2026b","undici":"7.28.0","unicode":"17.0","uv":"1.52.1","uvwasi":"0.0.23","v8":"13.6.233.17-node.50","zlib":"1.3.1-e00f703","zstd":"1.5.7"},"arch":"x64","platform":"linux","release":{"name":"node","lts":"Krypton"}}}},{"pid":1610388,"tid":1610400,"ts":105658224596,"tts":13,"ph":"M","cat":"__metadata","name":"thread_name","dur":0,"tdur":0,"args":{"name":"WorkerThreadsTaskRunner::DelayedTaskScheduler"}},{"pid":1610388,"tid":1610401,"ts":105658224646,"tts":15,"ph":"M","cat":"__metadata","name":"thread_name","dur":0,"tdur":0,"args":{"name":"PlatformWorkerThread"}},{"pid":1610388,"tid":1610402,"ts":105658224654,"tts":16,"ph":"M","cat":"__metadata","name":"thread_name","dur":0,"tdur":0,"args":{"name":"PlatformWorkerThread"}},{"pid":1610388,"tid":1610403,"ts":105658224666,"tts":20,"ph":"M","cat":"__metadata","name":"thread_name","dur":0,"tdur":0,"args":{"name":"PlatformWorkerThread"}},{"pid":1610388,"tid":1610404,"ts":105658224670,"tts":8,"ph":"M","cat":"__metadata","name":"thread_name","dur":0,"tdur":0,"args":{"name":"PlatformWorkerThread"}}]} +\ No newline at end of file +--- /dev/null ++++ b/test/parallel/test-permission-fs-write-trace-events.js +@@ -0,0 +1,70 @@ ++// Flags: --expose-internals ++'use strict'; ++ ++const common = require('../common'); ++const { spawnSyncAndExitWithoutError } = require('../common/child_process'); ++const { isMainThread } = require('worker_threads'); ++ ++if (!isMainThread) { ++ common.skip('This test only works on a main thread'); ++} ++ ++const assert = require('assert'); ++const fs = require('fs'); ++const tmpdir = require('../common/tmpdir'); ++ ++try { ++ require('trace_events'); ++} catch { ++ common.skip('missing trace events'); ++} ++ ++if (!process.permission) { ++ tmpdir.refresh(); ++ ++ const allowed = tmpdir.resolve('allowed'); ++ const outside = tmpdir.resolve('outside'); ++ const traceFilePattern = tmpdir.resolve( ++ 'outside', ++ // eslint-disable-next-line no-template-curly-in-string ++ 'denied-node-trace.${rotation}.log'); ++ const traceFile = tmpdir.resolve('outside', 'denied-node-trace.1.log'); ++ fs.mkdirSync(allowed); ++ fs.mkdirSync(outside); ++ ++ spawnSyncAndExitWithoutError(process.execPath, [ ++ '--experimental-permission', ++ '--allow-fs-read=*', ++ `--allow-fs-write=${allowed}`, ++ '--trace-event-file-pattern', ++ traceFilePattern, ++ __filename, ++ 'child', ++ traceFile, ++ ], { cwd: outside }); ++ return; ++} ++ ++assert.strictEqual(process.argv[2], 'child'); ++const traceFile = process.argv[3]; ++ ++assert.throws(() => { ++ fs.writeFileSync('canary', 'x'); ++}, common.expectsError({ ++ code: 'ERR_ACCESS_DENIED', ++ permission: 'FileSystemWrite', ++})); ++ ++const tracing = require('trace_events').createTracing({ ++ categories: ['node', 'v8', 'node.perf'], ++}); ++ ++assert.throws(() => { ++ tracing.enable(); ++}, common.expectsError({ ++ code: 'ERR_ACCESS_DENIED', ++ permission: 'FileSystemWrite', ++ resource: traceFile, ++})); ++ ++assert.strictEqual(fs.existsSync(traceFile), false); diff -Nru nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-56848.patch nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-56848.patch --- nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-56848.patch 1970-01-01 00:00:00.000000000 +0000 +++ nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-56848.patch 2026-08-31 15:10:49.000000000 +0000 @@ -0,0 +1,116 @@ +From: Matteo Collina +Date: Wed, 1 Jul 2026 09:43:01 +0200 +Subject: http2: defer rst stream while in scope + +Signed-off-by: Matteo Collina +PR-URL: https://github.com/nodejs-private/node-private/pull/921 +Refs: https://hackerone.com/reports/3833629 +Reviewed-By: Rafael Gonzaga +CVE-ID: CVE-2026-56848 +origin: https://github.com/nodejs/node/commit/daa6d25e3dceb30edb832a778ec0610c8bc2dd12 +bug: https://nodejs.org/en/blog/vulnerability/july-2026-security-releases#http2-re-entrant-send-can-cause-heap-use-after-free-cve-2026-56848---high +--- + src/node_http2.cc | 12 +++- + test/parallel/test-http2-rst-stream-reentrancy.js | 68 +++++++++++++++++++++++ + 2 files changed, 78 insertions(+), 2 deletions(-) + create mode 100644 test/parallel/test-http2-rst-stream-reentrancy.js + +diff --git a/src/node_http2.cc b/src/node_http2.cc +index 2367d0a..8a58794 100644 +--- a/src/node_http2.cc ++++ b/src/node_http2.cc +@@ -2507,10 +2507,18 @@ void Http2Stream::SubmitRstStream(const uint32_t code) { + // if RST_STREAM received is not in scope and added to the list + // causing endpoint to hang. + if (session_->is_in_scope() && is_stream_cancel(code)) { +- session_->AddPendingRstStream(id_); +- return; ++ session_->AddPendingRstStream(id_); ++ return; + } + ++ // If RST_STREAM is submitted while nghttp2 is processing callbacks for ++ // a refused stream, don't force purge pending data. Sending pending data ++ // here can re-enter nghttp2 and close streams that are still being used ++ // by the active receive operation. ++ if (session_->is_in_scope() && code == NGHTTP2_REFUSED_STREAM) { ++ FlushRstStream(); ++ return; ++ } + + // If possible, force a purge of any currently pending data here to make sure + // it is sent before closing the stream. If it returns non-zero then we need +diff --git a/test/parallel/test-http2-rst-stream-reentrancy.js b/test/parallel/test-http2-rst-stream-reentrancy.js +new file mode 100644 +index 0000000..8added7 +--- /dev/null ++++ b/test/parallel/test-http2-rst-stream-reentrancy.js +@@ -0,0 +1,68 @@ ++'use strict'; ++const common = require('../common'); ++if (!common.hasCrypto) ++ common.skip('missing crypto'); ++ ++const http2 = require('http2'); ++const net = require('net'); ++ ++const PREFACE = Buffer.from('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'); ++ ++function frame(type, flags, sid, payload = Buffer.alloc(0)) { ++ const header = Buffer.alloc(9); ++ header.writeUIntBE(payload.length, 0, 3); ++ header[3] = type; ++ header[4] = flags; ++ header.writeUInt32BE(sid & 0x7fffffff, 5); ++ return Buffer.concat([header, payload]); ++} ++ ++function goaway(lastStreamID, code) { ++ const payload = Buffer.alloc(8); ++ payload.writeUInt32BE(lastStreamID, 0); ++ payload.writeUInt32BE(code, 4); ++ return frame(7, 0, 0, payload); ++} ++ ++function headers(sid) { ++ return frame(1, 0x05, sid, Buffer.from([ ++ 0x82, // :method: GET ++ 0x86, // :scheme: http ++ 0x84, // :path: / ++ 0x41, 0x01, 0x78, // :authority: x ++ ])); ++} ++ ++const server = http2.createServer(); ++ ++server.listen(0, common.mustCall(() => { ++ const socket = net.connect(server.address().port); ++ let sent = false; ++ ++ socket.on('connect', common.mustCall(() => { ++ socket.write(Buffer.concat([PREFACE, frame(4, 0, 0)])); ++ })); ++ ++ socket.on('error', () => {}); ++ ++ socket.on('data', common.mustCallAtLeast(() => { ++ if (sent) ++ return; ++ sent = true; ++ ++ socket.write(frame(4, 1, 0)); ++ socket.write(headers(1)); ++ ++ setImmediate(() => { ++ socket.write(Buffer.concat([ ++ goaway(0, 0), ++ headers(3), ++ headers(5), ++ headers(7), ++ ])); ++ setTimeout(() => socket.destroy(), common.platformTimeout(50)); ++ }); ++ })); ++ ++ socket.on('close', common.mustCall(() => server.close())); ++})); diff -Nru nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-56850.patch nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-56850.patch --- nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-56850.patch 1970-01-01 00:00:00.000000000 +0000 +++ nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-56850.patch 2026-08-31 15:10:50.000000000 +0000 @@ -0,0 +1,183 @@ +From: RafaelGSS +Date: Mon, 20 Jul 2026 13:15:10 -0300 +Subject: https: distinguish PFX object-array agent keys + +Signed-off-by: RafaelGSS +PR-URL: https://github.com/nodejs-private/node-private/pull/930 +Refs: https://hackerone.com/reports/3816840 +CVE-ID: CVE-2026-56850 +bug: https://nodejs.org/en/blog/vulnerability/july-2026-security-releases#https-agent-can-reuse-mtls-identities-across-pfx-certificates-cve-2026-56850---medium +origin: https://github.com/nodejs/node/commit/acaf4266b2be7958e3d7cb44b5ee1c2b96eca278 +--- + lib/https.js | 17 ++++++- + test/parallel/test-https-agent-getname.js | 44 ++++++++++++++++ + .../test-https-agent-pfx-object-array-reuse.js | 59 ++++++++++++++++++++++ + 3 files changed, 119 insertions(+), 1 deletion(-) + create mode 100644 test/parallel/test-https-agent-pfx-object-array-reuse.js + +diff --git a/lib/https.js b/lib/https.js +index 21b4027..3cb4862 100644 +--- a/lib/https.js ++++ b/lib/https.js +@@ -22,6 +22,7 @@ + 'use strict'; + + const { ++ ArrayIsArray, + ArrayPrototypeIndexOf, + ArrayPrototypePush, + ArrayPrototypeShift, +@@ -223,6 +224,20 @@ ObjectSetPrototypeOf(Agent.prototype, HttpAgent.prototype); + ObjectSetPrototypeOf(Agent, HttpAgent); + Agent.prototype.createConnection = createConnection; + ++function getPfxAgentKey(pfx, passphrase) { ++ if (!ArrayIsArray(pfx)) ++ return pfx; ++ ++ let key = ''; ++ for (let i = 0; i < pfx.length; i++) { ++ const value = pfx[i]; ++ const raw = value?.buf || value; ++ const pass = value?.passphrase || passphrase; ++ key += `:${raw}:${pass}`; ++ } ++ return key; ++} ++ + /** + * Gets a unique name for a set of options. + * @param {{ +@@ -258,7 +273,7 @@ Agent.prototype.getName = function getName(options = kEmptyObject) { + + name += ':'; + if (options.pfx) +- name += options.pfx; ++ name += getPfxAgentKey(options.pfx, options.passphrase); + + name += ':'; + if (options.rejectUnauthorized !== undefined) +diff --git a/test/parallel/test-https-agent-getname.js b/test/parallel/test-https-agent-getname.js +index 2a13ab1..8ead852 100644 +--- a/test/parallel/test-https-agent-getname.js ++++ b/test/parallel/test-https-agent-getname.js +@@ -6,6 +6,7 @@ if (!common.hasCrypto) + + const assert = require('assert'); + const https = require('https'); ++const fixtures = require('../common/fixtures'); + + const agent = new https.Agent(); + +@@ -52,3 +53,46 @@ assert.strictEqual( + '::secureProtocol:c,r,l:false:ecdhCurve:dhparam:0:sessionIdContext:' + + '"sigalgs":privateKeyIdentifier:privateKeyEngine' + ); ++ ++{ ++ const baseOptions = { ++ host: '0.0.0.0', ++ port: 443, ++ }; ++ ++ const agent1 = fixtures.readKey('agent1.pfx'); ++ const agent6 = fixtures.readKey('agent6.pfx'); ++ ++ assert.notStrictEqual( ++ agent.getName({ ++ ...baseOptions, ++ pfx: [{ buf: agent1, passphrase: 'sample' }], ++ }), ++ agent.getName({ ++ ...baseOptions, ++ pfx: [{ buf: agent6, passphrase: 'sample' }], ++ }) ++ ); ++ ++ assert.notStrictEqual( ++ agent.getName({ ++ ...baseOptions, ++ pfx: [{ buf: agent1, passphrase: 'sample' }], ++ }), ++ agent.getName({ ++ ...baseOptions, ++ pfx: [{ buf: agent1, passphrase: 'different' }], ++ }) ++ ); ++ ++ assert.notStrictEqual( ++ agent.getName({ ++ ...baseOptions, ++ pfx: [{ __proto__: { buf: agent1, passphrase: 'sample' } }], ++ }), ++ agent.getName({ ++ ...baseOptions, ++ pfx: [{ __proto__: { buf: agent6, passphrase: 'sample' } }], ++ }) ++ ); ++} +diff --git a/test/parallel/test-https-agent-pfx-object-array-reuse.js b/test/parallel/test-https-agent-pfx-object-array-reuse.js +new file mode 100644 +index 0000000..9513485 +--- /dev/null ++++ b/test/parallel/test-https-agent-pfx-object-array-reuse.js +@@ -0,0 +1,59 @@ ++'use strict'; ++ ++const common = require('../common'); ++if (!common.hasCrypto) ++ common.skip('missing crypto'); ++ ++const assert = require('assert'); ++const https = require('https'); ++const fixtures = require('../common/fixtures'); ++ ++const server = https.createServer({ ++ key: fixtures.readKey('agent2-key.pem'), ++ cert: fixtures.readKey('agent2-cert.pem'), ++ requestCert: true, ++ rejectUnauthorized: false, ++}, common.mustCall((req, res) => { ++ res.end(req.socket.getPeerCertificate().subject.CN); ++}, 2)); ++ ++server.listen(0, common.mustCall(async () => { ++ const agent = new https.Agent({ keepAlive: true, maxSockets: 1 }); ++ const port = server.address().port; ++ ++ const first = await request({ ++ agent, ++ port, ++ pfx: [{ buf: fixtures.readKey('agent1.pfx'), passphrase: 'sample' }], ++ }); ++ assert.strictEqual(first.body, 'agent1'); ++ assert.strictEqual(first.reusedSocket, false); ++ ++ const second = await request({ ++ agent, ++ port, ++ pfx: [{ buf: fixtures.readKey('agent10.pfx'), passphrase: 'sample' }], ++ }); ++ assert.strictEqual(second.body, 'agent10.example.com'); ++ assert.strictEqual(second.reusedSocket, false); ++ ++ agent.destroy(); ++ server.close(); ++})); ++ ++function request(options) { ++ return new Promise((resolve, reject) => { ++ const req = https.get({ ++ ...options, ++ rejectUnauthorized: false, ++ }, common.mustCall((res) => { ++ let body = ''; ++ res.setEncoding('utf8'); ++ res.on('data', (chunk) => body += chunk); ++ res.on('end', common.mustCall(() => { ++ resolve({ body, reusedSocket: req.reusedSocket }); ++ })); ++ })); ++ req.on('error', reject); ++ }); ++} diff -Nru nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-58039.patch nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-58039.patch --- nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-58039.patch 1970-01-01 00:00:00.000000000 +0000 +++ nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-58039.patch 2026-09-22 17:02:25.000000000 +0000 @@ -0,0 +1,195 @@ +From: RafaelGSS +Date: Fri, 26 Jun 2026 19:08:22 -0300 +Subject: permission: check final report output path + +Refs: https://hackerone.com/reports/3815767 +Signed-off-by: RafaelGSS +PR-URL: https://github.com/nodejs-private/node-private/pull/926 +CVE-ID: CVE-2026-58039 +origin: backport, https://github.com/nodejs/node/commit/ed18b9cc073f0b63268d6f2c84730b18e90fdd20 +bug: https://nodejs.org/en/blog/vulnerability/july-2026-security-releases#permission-model-allows-process-reports-to-write-outside-the-allowlist-cve-2026-58039---low +--- + lib/internal/process/report.js | 10 +++- + src/node_report.cc | 24 ++++----- + test/parallel/test-permission-fs-write-report.js | 65 ++++++++++++++++++++++++ + 3 files changed, 86 insertions(+), 13 deletions(-) + +--- a/lib/internal/process/report.js ++++ b/lib/internal/process/report.js +@@ -4,6 +4,7 @@ + ERR_SYNTHETIC, + } = require('internal/errors').codes; + const { getValidatedPath } = require('internal/fs/utils'); ++const { sep } = require('path'); + const permission = require('internal/process/permission'); + const { + validateBoolean, +@@ -26,7 +27,14 @@ + } + + if (permission.isEnabled()) { +- const resource = file ?? process.cwd(); ++ let resource = file; ++ if (resource !== undefined) { ++ const directory = nr.getDirectory(); ++ if (directory !== '') ++ resource = `${directory}${sep}${resource}`; ++ } else { ++ resource = process.cwd(); ++ } + if (!permission.has('fs.write', resource)) { + throw new ERR_ACCESS_DENIED( + 'Access to this API has been restricted', +--- a/src/node_report.cc ++++ b/src/node_report.cc +@@ -866,13 +866,6 @@ + filename = *DiagnosticFilename( + env != nullptr ? env->thread_id() : 0, "report", "json"); + } +- if (env != nullptr) { +- THROW_IF_INSUFFICIENT_PERMISSIONS( +- env, +- permission::PermissionScope::kFileSystemWrite, +- std::string_view(Environment::GetCwd(env->exec_path())), +- filename); +- } + } + + // Open the report file stream for writing. Supports stdout/err, +@@ -890,14 +883,21 @@ + report_directory = per_process::cli_options->report_directory; + } + // Regular file. Append filename to directory path if one was specified ++ std::string pathname; + if (report_directory.length() > 0) { +- std::string pathname = report_directory; +- pathname += kPathSeparator; +- pathname += filename; +- outfile.open(pathname, std::ios::out | std::ios::binary); ++ pathname = report_directory + kPathSeparator + filename; + } else { +- outfile.open(filename, std::ios::out | std::ios::binary); ++ pathname = filename; ++ } ++ ++ // We may not always be in a great state when generating a node report. ++ // Allow for the case where we don't have an env. ++ if (env != nullptr) { ++ THROW_IF_INSUFFICIENT_PERMISSIONS( ++ env, permission::PermissionScope::kFileSystemWrite, pathname, ""); + } ++ ++ outfile.open(pathname, std::ios::out | std::ios::binary); + // Check for errors on the file open + if (!outfile.is_open()) { + std::cerr << "\nFailed to open Node.js report file: " << filename; +--- a/test/parallel/test-permission-fs-write-report.js ++++ b/test/parallel/test-permission-fs-write-report.js +@@ -1,4 +1,3 @@ +-// Flags: --experimental-permission --allow-fs-read=* + 'use strict'; + + const { spawnSyncAndExitWithoutError } = require('../common/child_process'); +@@ -11,7 +10,7 @@ + // We need to define the flags dynamically to account for the `NODE_TEST_DIR` env var. + if (!process.permission) { + spawnSyncAndExitWithoutError(process.execPath, [ +- '--permission', ++ '--experimental-permission', + '--allow-fs-read=*', `--allow-fs-write=${process.env.NODE_TEST_DIR || './test'}/.tmp.*`, '--allow-child-process', + __filename, + ]); +@@ -19,6 +18,7 @@ + } + + const assert = require('assert'); ++const fs = require('fs'); + const path = require('path'); + const tmpdir = require('../common/tmpdir'); + +@@ -49,7 +49,7 @@ + spawnSyncAndExitWithoutError( + process.execPath, + [ +- '--permission', ++ '--experimental-permission', + '--allow-fs-read=*', + `--allow-fs-write=${tmpdir.path}/*`, + '-e', +@@ -61,7 +61,7 @@ + spawnSyncAndExitWithoutError( + process.execPath, + [ +- '--permission', ++ '--experimental-permission', + '--allow-fs-read=*', + `--allow-fs-write=${tmpdir.path}`, + '-e', +@@ -69,3 +69,67 @@ + ], + { cwd: tmpdir.path } + ); ++ ++{ ++ const allowedDir = path.join(tmpdir.path, 'report-allowed'); ++ const deniedDir = path.join(tmpdir.path, 'report-denied'); ++ fs.mkdirSync(allowedDir); ++ fs.mkdirSync(deniedDir); ++ ++ const deniedFile = path.join(deniedDir, 'report.json'); ++ fs.writeFileSync(deniedFile, 'existing content'); ++ spawnSyncAndExitWithoutError( ++ process.execPath, ++ [ ++ '--experimental-permission', ++ '--allow-fs-read=*', ++ `--allow-fs-write=${allowedDir}`, ++ '-e', ++ ` ++ const assert = require('assert'); ++ process.report.directory = ${JSON.stringify(deniedDir)}; ++ assert.throws(() => { ++ process.report.writeReport('report.json'); ++ }, { ++ code: 'ERR_ACCESS_DENIED', ++ permission: 'FileSystemWrite', ++ resource: ${JSON.stringify(deniedFile)}, ++ }); ++ `, ++ ], ++ { cwd: allowedDir }, ++ ); ++ assert.strictEqual(fs.readFileSync(deniedFile, 'utf8'), 'existing content'); ++} ++ ++{ ++ const allowedDir = path.join(tmpdir.path, 'report-filename-allowed'); ++ const deniedDir = path.join(tmpdir.path, 'report-filename-denied'); ++ fs.mkdirSync(allowedDir); ++ fs.mkdirSync(deniedDir); ++ ++ const deniedFile = path.join(deniedDir, 'report.json'); ++ spawnSyncAndExitWithoutError( ++ process.execPath, ++ [ ++ '--experimental-permission', ++ '--allow-fs-read=*', ++ `--allow-fs-write=${allowedDir}`, ++ '-e', ++ ` ++ const assert = require('assert'); ++ process.report.directory = ${JSON.stringify(deniedDir)}; ++ process.report.filename = 'report.json'; ++ assert.throws(() => { ++ process.report.writeReport(); ++ }, { ++ code: 'ERR_ACCESS_DENIED', ++ permission: 'FileSystemWrite', ++ resource: ${JSON.stringify(deniedFile)}, ++ }); ++ `, ++ ], ++ { cwd: allowedDir }, ++ ); ++ assert.strictEqual(fs.existsSync(deniedFile), false); ++} diff -Nru nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-58040.patch nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-58040.patch --- nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-58040.patch 1970-01-01 00:00:00.000000000 +0000 +++ nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-58040.patch 2026-08-31 15:10:50.000000000 +0000 @@ -0,0 +1,237 @@ +From: Matteo Collina +Date: Tue, 23 Jun 2026 12:47:47 +0200 +Subject: https: bind identity checks to session reuse + +PR-URL: https://github.com/nodejs-private/node-private/pull/934 +Reviewed-By: Rafael Gonzaga +CVE-ID: CVE-2026-58040 +origin: backport, https://github.com/nodejs/node/commit/51123159fe863073d7dcaf5c88b23bad8aae1a62 +--- + lib/https.js | 44 +++++++- + .../test-https-agent-checkserveridentity-reuse.js | 113 +++++++++++++++++++++ + 2 files changed, 155 insertions(+), 2 deletions(-) + create mode 100644 test/parallel/test-https-agent-checkserveridentity-reuse.js + +diff --git a/lib/https.js b/lib/https.js +index 3cb4862..3e36566 100644 +--- a/lib/https.js ++++ b/lib/https.js +@@ -34,6 +34,7 @@ const { + ObjectSetPrototypeOf, + ReflectApply, + ReflectConstruct, ++ Symbol, + } = primordials; + + const { +@@ -46,6 +47,8 @@ assertCrypto(); + + const tls = require('tls'); + const { Agent: HttpAgent } = require('_http_agent'); ++const kPerRequestCheckServerIdentity = Symbol('per-request checkServerIdentity'); ++let perRequestCheckServerIdentityIndex = 0; + const { + httpServerPreClose, + Server: HttpServer, +@@ -160,7 +163,9 @@ function createConnection(port, host, options) { + + debug('createConnection', options); + +- if (options._agentKey) { ++ const reuseSession = options._agentKey && ++ !options[kPerRequestCheckServerIdentity]; ++ if (reuseSession) { + const session = this._getSession(options._agentKey); + if (session) { + debug('reuse session for %j', options._agentKey); +@@ -173,7 +178,10 @@ function createConnection(port, host, options) { + + const socket = tls.connect(options); + +- if (options._agentKey) { ++ if (options[kPerRequestCheckServerIdentity]) ++ socket[kPerRequestCheckServerIdentity] = true; ++ ++ if (reuseSession) { + // Cache new session for reuse + socket.on('session', (session) => { + this._cacheSession(options._agentKey, session); +@@ -223,6 +231,12 @@ function Agent(options) { + ObjectSetPrototypeOf(Agent.prototype, HttpAgent.prototype); + ObjectSetPrototypeOf(Agent, HttpAgent); + Agent.prototype.createConnection = createConnection; ++Agent.prototype.keepSocketAlive = function keepSocketAlive(socket) { ++ if (socket[kPerRequestCheckServerIdentity]) ++ return false; ++ ++ return FunctionPrototypeCall(HttpAgent.prototype.keepSocketAlive, this, socket); ++}; + + function getPfxAgentKey(pfx, passphrase) { + if (!ArrayIsArray(pfx)) +@@ -331,6 +345,9 @@ Agent.prototype.getName = function getName(options = kEmptyObject) { + if (options.privateKeyEngine) + name += options.privateKeyEngine; + ++ if (options[kPerRequestCheckServerIdentity]) ++ name += `:${options[kPerRequestCheckServerIdentity]}`; ++ + return name; + }; + +@@ -371,6 +388,22 @@ Agent.prototype._evictSession = function _evictSession(key) { + + const globalAgent = new Agent({ keepAlive: true, scheduling: 'lifo', timeout: 5000 }); + ++function hasAgentCheckServerIdentity(options) { ++ let { agent } = options; ++ if (agent === false) ++ return false; ++ ++ if (agent === null || agent === undefined) { ++ if (typeof options.createConnection === 'function') ++ return false; ++ agent = module.exports.globalAgent; ++ } ++ ++ return agent?.options?.checkServerIdentity !== undefined; ++} ++ ++ ++ + /** + * Makes a request to a secure web server. + * @param {...any} args +@@ -390,6 +423,13 @@ function request(...args) { + ObjectAssign(options, ArrayPrototypeShift(args)); + } + ++ if (options.checkServerIdentity !== undefined && ++ options.checkServerIdentity !== tls.checkServerIdentity && ++ !hasAgentCheckServerIdentity(options)) { ++ options[kPerRequestCheckServerIdentity] = ++ ++perRequestCheckServerIdentityIndex; ++ } ++ + options._defaultAgent = module.exports.globalAgent; + ArrayPrototypeUnshift(args, options); + +diff --git a/test/parallel/test-https-agent-checkserveridentity-reuse.js b/test/parallel/test-https-agent-checkserveridentity-reuse.js +new file mode 100644 +index 0000000..2c339c3 +--- /dev/null ++++ b/test/parallel/test-https-agent-checkserveridentity-reuse.js +@@ -0,0 +1,113 @@ ++'use strict'; ++const common = require('../common'); ++if (!common.hasCrypto) ++ common.skip('missing crypto'); ++ ++const assert = require('assert'); ++const fixtures = require('../common/fixtures'); ++const https = require('https'); ++const { once } = require('events'); ++ ++const key = fixtures.readKey('agent1-key.pem'); ++const cert = fixtures.readKey('agent1-cert.pem'); ++const ca = fixtures.readKey('ca1-cert.pem'); ++const expectedError = /rejected by callback/; ++ ++function request(options) { ++ return new Promise((resolve, reject) => { ++ const req = https.get({ ++ host: '127.0.0.1', ++ servername: 'agent1', ++ ca: [ca], ++ ...options, ++ }, (res) => { ++ const socket = res.socket; ++ res.resume(); ++ res.on('end', () => resolve({ ++ socket, ++ reusedSocket: req.reusedSocket, ++ })); ++ }); ++ ++ req.on('error', reject); ++ }); ++} ++ ++const server = https.createServer({ ++ key, ++ cert, ++ minVersion: 'TLSv1.2', ++ maxVersion: 'TLSv1.2', ++}, (req, res) => { ++ res.end('ok'); ++}); ++ ++(async function() { ++ server.listen(0); ++ await once(server, 'listening'); ++ ++ const port = server.address().port; ++ let acceptCalls = 0; ++ let rejectCalls = 0; ++ const acceptingCheck = () => { ++ acceptCalls++; ++ }; ++ const rejectingCheck = () => { ++ rejectCalls++; ++ return new Error('rejected by callback'); ++ }; ++ ++ const sessionAgent = new https.Agent(); ++ const keepAliveAgent = new https.Agent({ ++ keepAlive: true, ++ maxCachedSessions: 0, ++ }); ++ const agentLevelAgent = new https.Agent({ ++ checkServerIdentity: acceptingCheck, ++ }); ++ ++ try { ++ await request({ ++ port, ++ agent: sessionAgent, ++ checkServerIdentity: acceptingCheck, ++ }); ++ assert.deepStrictEqual(sessionAgent._sessionCache.map, {}); ++ await assert.rejects(request({ ++ port, ++ agent: sessionAgent, ++ checkServerIdentity: rejectingCheck, ++ }), expectedError); ++ ++ await request({ ++ port, ++ agent: keepAliveAgent, ++ checkServerIdentity: acceptingCheck, ++ }); ++ await assert.rejects(request({ ++ port, ++ agent: keepAliveAgent, ++ checkServerIdentity: rejectingCheck, ++ }), expectedError); ++ ++ const first = await request({ ++ port, ++ agent: agentLevelAgent, ++ }); ++ assert.strictEqual(first.socket.isSessionReused(), false); ++ const second = await request({ ++ port, ++ agent: agentLevelAgent, ++ }); ++ assert.strictEqual(second.socket.isSessionReused(), true); ++ ++ assert.strictEqual(acceptCalls, 3); ++ assert.strictEqual(rejectCalls, 2); ++ } finally { ++ sessionAgent.destroy(); ++ keepAliveAgent.destroy(); ++ agentLevelAgent.destroy(); ++ server.close(); ++ await once(server, 'close'); ++ } ++})().then(common.mustCall()); diff -Nru nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-58042.patch nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-58042.patch --- nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-58042.patch 1970-01-01 00:00:00.000000000 +0000 +++ nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-58042.patch 2026-08-31 15:23:15.000000000 +0000 @@ -0,0 +1,216 @@ +From 22efc051a3c3b3bbddbb3cb06ce1ca5775923c01 Mon Sep 17 00:00:00 2001 +From: RafaelGSS +Date: Sun, 19 Jul 2026 11:38:25 -0300 +Subject: [PATCH] dns: handle large resolveAny address replies + +Refs: https://hackerone.com/reports/3795657 +Signed-off-by: RafaelGSS +PR-URL: https://github.com/nodejs-private/node-private/pull/929 +Reviewed-By: Matteo Collina +CVE-ID: CVE-2026-58042 + +--- a/src/cares_wrap.cc ++++ b/src/cares_wrap.cc +@@ -40,6 +40,10 @@ + #include + #include + ++#ifndef T_TLSA ++#define T_TLSA 52 /* TLSA certificate association */ ++#endif ++ + #ifndef T_CAA + # define T_CAA 257 /* Certification Authority Authorization */ + #endif +@@ -195,6 +199,23 @@ + return Array::New(env->isolate(), ttls.out(), naddrttls); + } + ++int GetAnswerCountForTTLBuffer(const unsigned char* buf, int len) { ++ static constexpr int kDNSAnswerCountOffset = 6; ++ static constexpr int kAresDefaultTTLBufferLength = 256; ++ if (len <= kDNSAnswerCountOffset + 1) { ++ return kAresDefaultTTLBufferLength; ++ } ++ ++ const int answer_count = (static_cast(buf[kDNSAnswerCountOffset]) << 8) | ++ static_cast(buf[kDNSAnswerCountOffset + 1]); ++ return answer_count == 0 ? 1 : answer_count; ++} ++ ++template ++std::vector MakeAddrTTLBuffer(const unsigned char* buf, int len) { ++ return std::vector(GetAnswerCountForTTLBuffer(buf, len)); ++} ++ + int ParseGeneralReply( + Environment* env, + const unsigned char* buf, +@@ -901,8 +922,9 @@ + int type, status, old_count; + + /* Parse A records or CNAME records */ +- ares_addrttl addrttls[256]; +- int naddrttls = arraysize(addrttls); ++ std::vector addrttls = ++ MakeAddrTTLBuffer(buf, len); ++ int naddrttls = static_cast(addrttls.size()); + + type = ns_t_cname_or_a; + status = ParseGeneralReply(env, +@@ -910,7 +932,7 @@ + len, + &type, + ret, +- addrttls, ++ addrttls.data(), + &naddrttls); + uint32_t a_count = ret->Length(); + if (status != ARES_SUCCESS && status != ARES_ENODATA) +@@ -946,8 +968,9 @@ + } + + /* Parse AAAA records */ +- ares_addr6ttl addr6ttls[256]; +- int naddr6ttls = arraysize(addr6ttls); ++ std::vector addr6ttls = ++ MakeAddrTTLBuffer(buf, len); ++ int naddr6ttls = static_cast(addr6ttls.size()); + + type = ns_t_aaaa; + status = ParseGeneralReply(env, +@@ -955,7 +978,7 @@ + len, + &type, + ret, +- addr6ttls, ++ addr6ttls.data(), + &naddr6ttls); + uint32_t aaaa_count = ret->Length() - a_count; + if (status != ARES_SUCCESS && status != ARES_ENODATA) +@@ -1064,8 +1087,9 @@ + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + +- ares_addrttl addrttls[256]; +- int naddrttls = arraysize(addrttls), status; ++ std::vector addrttls = ++ MakeAddrTTLBuffer(buf, len); ++ int naddrttls = static_cast(addrttls.size()), status; + Local ret = Array::New(env->isolate()); + + int type = ns_t_a; +@@ -1074,12 +1098,12 @@ + len, + &type, + ret, +- addrttls, ++ addrttls.data(), + &naddrttls); + if (status != ARES_SUCCESS) + return status; + +- Local ttls = AddrTTLToArray(env, addrttls, naddrttls); ++ Local ttls = AddrTTLToArray(env, addrttls.data(), naddrttls); + + wrap->CallOnComplete(ret, ttls); + return ARES_SUCCESS; +@@ -1098,8 +1122,9 @@ + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + +- ares_addr6ttl addrttls[256]; +- int naddrttls = arraysize(addrttls), status; ++ std::vector addrttls = ++ MakeAddrTTLBuffer(buf, len); ++ int naddrttls = static_cast(addrttls.size()), status; + Local ret = Array::New(env->isolate()); + + int type = ns_t_aaaa; +@@ -1108,12 +1133,12 @@ + len, + &type, + ret, +- addrttls, ++ addrttls.data(), + &naddrttls); + if (status != ARES_SUCCESS) + return status; + +- Local ttls = AddrTTLToArray(env, addrttls, naddrttls); ++ Local ttls = AddrTTLToArray(env, addrttls.data(), naddrttls); + + wrap->CallOnComplete(ret, ttls); + return ARES_SUCCESS; +--- /dev/null ++++ b/test/parallel/test-dns-resolveany-ttl-overflow.js +@@ -0,0 +1,69 @@ ++'use strict'; ++const common = require('../common'); ++const dnstools = require('../common/dns'); ++const assert = require('assert'); ++const dgram = require('dgram'); ++const dns = require('dns'); ++ ++const dnsPromises = dns.promises; ++ ++const kRecordCount = 257; ++const kADomain = 'many-a.example.org'; ++ ++const server = dgram.createSocket('udp4'); ++ ++server.on('message', common.mustCall((msg, { address, port }) => { ++ const parsed = dnstools.parseDNSPacket(msg); ++ const question = parsed.questions[0]; ++ const { domain } = question; ++ ++ assert.strictEqual(question.type, 'ANY'); ++ assert.strictEqual(domain, kADomain); ++ ++ server.send(dnstools.writeDNSPacket({ ++ id: parsed.id, ++ questions: parsed.questions, ++ answers: createARecords(domain), ++ }), port, address); ++}, 2)); ++ ++server.bind(0, common.mustCall(async () => { ++ const { port } = server.address(); ++ const callbackResolver = new dns.Resolver({ timeout: 1000, tries: 1 }); ++ const promiseResolver = new dnsPromises.Resolver({ timeout: 1000, tries: 1 }); ++ callbackResolver.setServers([`127.0.0.1:${port}`]); ++ promiseResolver.setServers([`127.0.0.1:${port}`]); ++ ++ validateRecords(await promiseResolver.resolveAny(kADomain), 'A'); ++ validateRecords(await resolveAny(callbackResolver, kADomain), 'A'); ++ ++ server.close(); ++})); ++ ++function createARecords(domain) { ++ return Array.from({ length: kRecordCount }, (_, i) => ({ ++ type: 'A', ++ address: `10.0.${i >> 8}.${i & 0xff}`, ++ ttl: 60 + i, ++ domain, ++ })); ++} ++ ++function resolveAny(resolver, domain) { ++ return new Promise((resolve) => { ++ resolver.resolveAny(domain, common.mustSucceed(resolve)); ++ }); ++} ++ ++function validateRecords(records, type) { ++ assert.strictEqual(records.length, kRecordCount); ++ for (const record of records) { ++ assert.strictEqual(record.type, type); ++ } ++ ++ assert.strictEqual(records[0].ttl, 60); ++ assert.strictEqual(records[255].ttl, 315); ++ assert.strictEqual(records[256].ttl, 316); ++ assert.strictEqual(records[0].address, '10.0.0.0'); ++ assert.strictEqual(records[256].address, '10.0.1.0'); ++} diff -Nru nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-58043.patch nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-58043.patch --- nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-58043.patch 1970-01-01 00:00:00.000000000 +0000 +++ nodejs-20.19.2+dfsg/debian/patches/sec/CVE-2026-58043.patch 2026-09-22 17:02:25.000000000 +0000 @@ -0,0 +1,105 @@ +From: RafaelGSS +Date: Tue, 30 Jun 2026 18:07:04 -0300 +Subject: permission: avoid granting radix split nodes + +Signed-off-by: RafaelGSS +PR-URL: https://github.com/nodejs-private/node-private/pull/911 +Refs: https://hackerone.com/reports/3761342 +CVE-ID: CVE-2026-58043 +origin: https://github.com/nodejs/node/commit/440329f6247c8abb4c7cb4217e5a546ad41cd5ed +bug: https://nodejs.org/en/blog/vulnerability/july-2026-security-releases#permission-model-path-matching-can-over-grant-filesystem-access-cve-2026-58043---high +--- + src/permission/fs_permission.h | 7 +++-- + test/parallel/test-permission-fs-read.js | 46 ++++++++++++++++++++++++++++++++ + 2 files changed, 51 insertions(+), 2 deletions(-) + +--- a/src/permission/fs_permission.h ++++ b/src/permission/fs_permission.h +@@ -48,12 +48,13 @@ + children[label] = new Node(path_prefix); + return children[label]; + } ++ bool child_was_end_node = child->IsEndNode(); + + // swap prefix + size_t i = 0; + size_t prefix_len = path_prefix.length(); + for (; i < child->prefix.length(); ++i) { +- if (i > prefix_len || path_prefix[i] != child->prefix[i]) { ++ if (i >= prefix_len || path_prefix[i] != child->prefix[i]) { + std::string parent_prefix = child->prefix.substr(0, i); + std::string child_prefix = child->prefix.substr(i); + +@@ -65,7 +66,9 @@ + return split_child->CreateChild(path_prefix.substr(i)); + } + } +- child->is_leaf = true; ++ if (child_was_end_node) { ++ child->is_leaf = true; ++ } + return child->CreateChild(path_prefix.substr(i)); + } + +--- a/test/parallel/test-permission-fs-read.js ++++ b/test/parallel/test-permission-fs-read.js +@@ -11,6 +11,7 @@ + const assert = require('assert'); + const fixtures = require('../common/fixtures'); + const tmpdir = require('../common/tmpdir'); ++const fs = require('fs'); + const { spawnSync } = require('child_process'); + const path = require('path'); + +@@ -25,6 +26,51 @@ + } + + { ++ const boundaryFile = path.join(tmpdir.path, 'secret'); ++ const grantedFiles = ['secret1', 'secret2', 'secret3'] ++ .map((file) => path.join(tmpdir.path, file)); ++ ++ fs.writeFileSync(boundaryFile, 'protected'); ++ for (const file of grantedFiles) { ++ fs.writeFileSync(file, 'granted'); ++ } ++ ++ const { status, stderr } = spawnSync( ++ process.execPath, ++ [ ++ '--experimental-permission', ++ ...grantedFiles.map((file) => `--allow-fs-read=${file}`), ++ ...grantedFiles.map((file) => `--allow-fs-write=${file}`), ++ '-e', ++ ` ++ const assert = require('assert'); ++ const fs = require('fs'); ++ const target = process.env.BOUNDARY_FILE; ++ ++ assert.strictEqual(process.permission.has('fs.read', target), false); ++ assert.strictEqual(process.permission.has('fs.write', target), false); ++ assert.throws( ++ () => fs.readFileSync(target, 'utf8'), ++ { code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead' } ++ ); ++ assert.throws( ++ () => fs.writeFileSync(target, 'modified'), ++ { code: 'ERR_ACCESS_DENIED', permission: 'FileSystemWrite' } ++ ); ++ `, ++ ], ++ { ++ env: { ++ ...process.env, ++ BOUNDARY_FILE: boundaryFile, ++ }, ++ } ++ ); ++ assert.strictEqual(status, 0, stderr.toString()); ++ assert.strictEqual(fs.readFileSync(boundaryFile, 'utf8'), 'protected'); ++} ++ ++{ + const { status, stderr } = spawnSync( + process.execPath, + [ diff -Nru nodejs-20.19.2+dfsg/debian/patches/series nodejs-20.19.2+dfsg/debian/patches/series --- nodejs-20.19.2+dfsg/debian/patches/series 2026-03-24 21:07:05.000000000 +0000 +++ nodejs-20.19.2+dfsg/debian/patches/series 2026-09-22 17:10:07.000000000 +0000 @@ -1,3 +1,4 @@ +build/openssl_tests.patch build/more_shareable_builtins.patch build/builtins_module_paths_not_shareable.patch build/doc.patch @@ -39,3 +40,22 @@ sec/54-add-permission-check-to-realpath-native.patch sec/55-handle-NGHTTP2_ERR_FLOW_CONTROL-error-code.patch sec/56-tls-wrap-SNICallback-invocation-in-try-catch.patch +sec/CVE-2026-48617.patch +sec/CVE-2026-48618.patch +sec/CVE-2026-48619.patch +sec/CVE-2026-48928.patch +sec/CVE-2026-48930.patch +sec/CVE-2026-48931.patch +sec/CVE-2026-48933.patch +sec/CVE-2026-48934.patch +sec/CVE-2026-48935.patch +sec/CVE-2026-48937_pre1.patch +sec/CVE-2026-48937.patch +sec/CVE-2026-56846.patch +sec/CVE-2026-56847.patch +sec/CVE-2026-56848.patch +sec/CVE-2026-56850.patch +sec/CVE-2026-58039.patch +sec/CVE-2026-58042.patch +sec/CVE-2026-58043.patch +sec/CVE-2026-58040.patch