From bd26f2ffc3e1f6ef6f128030f32e0a455368519e Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Mon, 27 Apr 2026 11:08:17 +0000 Subject: [PATCH] fix(ext/node): brand-check SecureContext._external accessor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Node's `_external` accessor on a TLS SecureContext returns the C++ external pointer; reading it on a non-context receiver hits a slot check and throws. Deno had no such accessor at all, so accessing `secureContext.context._external` returned `undefined` regardless of the receiver — silently passing prototype-tampering tests instead of matching Node's behaviour. Define `_external` as an own getter on the context that brand-checks the receiver via a `WeakSet` and throws `TypeError` when it's not the same context object the SecureContext constructed. Enables `parallel/test-tls-external-accessor.js`. --- ext/node/polyfills/_tls_common.ts | 18 ++++++++++++++++++ tests/node_compat/config.jsonc | 1 + 2 files changed, 19 insertions(+) diff --git a/ext/node/polyfills/_tls_common.ts b/ext/node/polyfills/_tls_common.ts index 9df3a1c86e2427..3161773c1ce380 100644 --- a/ext/node/polyfills/_tls_common.ts +++ b/ext/node/polyfills/_tls_common.ts @@ -146,6 +146,8 @@ function validateKeyCertOption( ); } +const secureContextBrand = new WeakSet(); + export class SecureContext { context: { ca?: string | string[]; @@ -199,6 +201,22 @@ export class SecureContext { sigalgs: options.sigalgs, ecdhCurve: options.ecdhCurve, }; + secureContextBrand.add(this.context); + Object.defineProperty(this.context, "_external", { + __proto__: null, + configurable: true, + enumerable: false, + get(this: object) { + // In Node, `_external` is the C++ external pointer; reading it on a + // non-context receiver hits an internal slot check and throws. Match + // that behaviour so prototype-tampering tests don't get a silent + // undefined. + if (!secureContextBrand.has(this)) { + throw new TypeError("Illegal invocation"); + } + return this; + }, + }); } // Backward compat: current _tls_wrap.js accesses .ca, .cert, .key directly diff --git a/tests/node_compat/config.jsonc b/tests/node_compat/config.jsonc index 7660342d6c7fca..0ee024520c0c08 100644 --- a/tests/node_compat/config.jsonc +++ b/tests/node_compat/config.jsonc @@ -3066,6 +3066,7 @@ "parallel/test-tls-enable-trace-cli.js": {}, "parallel/test-tls-enable-trace.js": {}, "parallel/test-tls-env-extra-ca-no-crypto.js": {}, + "parallel/test-tls-external-accessor.js": {}, "parallel/test-tls-fast-writing.js": {}, "parallel/test-tls-finished.js": {}, "parallel/test-tls-friendly-error-message.js": {},