From a0c14ed81898f045f72094efba574c0d9fc92044 Mon Sep 17 00:00:00 2001 From: Brian Meek <55990082+brianathere@users.noreply.github.com> Date: Wed, 3 Jun 2026 16:54:41 -0700 Subject: [PATCH] vm: fix property queries for proxy sandboxes Signed-off-by: Brian Meek <55990082+brianathere@users.noreply.github.com> --- src/node_contextify.cc | 8 +- .../test-vm-proxy-sandbox-property-query.js | 74 +++++++++++++++++++ 2 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 test/parallel/test-vm-proxy-sandbox-property-query.js diff --git a/src/node_contextify.cc b/src/node_contextify.cc index f319420ae02f35..40ed019a73d83b 100644 --- a/src/node_contextify.cc +++ b/src/node_contextify.cc @@ -493,13 +493,13 @@ Intercepted ContextifyContext::PropertyQueryCallback( PropertyAttribute attr; - Maybe maybe_has = sandbox->HasRealNamedProperty(context, property); + Maybe maybe_has = sandbox->HasOwnProperty(context, property); if (maybe_has.IsNothing()) { return Intercepted::kNo; } else if (maybe_has.FromJust()) { - Maybe maybe_attr = - sandbox->GetRealNamedPropertyAttributes(context, property); - if (!maybe_attr.To(&attr)) { + Maybe maybe_attr = + sandbox->GetPropertyAttributes(context, property, &attr); + if (!maybe_attr.FromMaybe(false)) { return Intercepted::kNo; } args.GetReturnValue().Set(attr); diff --git a/test/parallel/test-vm-proxy-sandbox-property-query.js b/test/parallel/test-vm-proxy-sandbox-property-query.js new file mode 100644 index 00000000000000..b12e6a71b43cf1 --- /dev/null +++ b/test/parallel/test-vm-proxy-sandbox-property-query.js @@ -0,0 +1,74 @@ +'use strict'; + +require('../common'); +const assert = require('node:assert'); +const vm = require('node:vm'); + +// This test ensures that Proxy-backed vm sandboxes report own properties +// consistently across descriptor and membership queries. + +const sandbox = new Proxy({}, {}); +const ctx = vm.createContext(sandbox); + +vm.runInContext(` +Object.defineProperty(this, 'foo', { + value: 42, + writable: true, + enumerable: true, + configurable: true, +}); +Object.defineProperty(this, 'hidden', { + value: 1, + enumerable: false, + configurable: true, +}); +Object.defineProperty(this, 'readonly', { + value: 2, + writable: false, + enumerable: true, + configurable: true, +}); +`, ctx); + +const result = vm.runInContext(`({ + value: foo, + descriptor: Object.getOwnPropertyDescriptor(globalThis, 'foo'), + ownNamesIncludes: Object.getOwnPropertyNames(globalThis).includes('foo'), + hasOwnProperty: + Object.prototype.hasOwnProperty.call(globalThis, 'foo'), + objectHasOwn: Object.hasOwn(globalThis, 'foo'), + inGlobalThis: 'foo' in globalThis, + reflectHas: Reflect.has(globalThis, 'foo'), + keysIncludesFoo: Object.keys(globalThis).includes('foo'), + keysIncludesHidden: Object.keys(globalThis).includes('hidden'), + hiddenIsEnumerable: + Object.prototype.propertyIsEnumerable.call(globalThis, 'hidden'), + readonlyDescriptor: + Object.getOwnPropertyDescriptor(globalThis, 'readonly'), + hasOwnToString: Object.hasOwn(globalThis, 'toString'), + toStringInGlobalThis: 'toString' in globalThis, +})`, ctx); + +assert.strictEqual(result.value, 42); +assert.deepStrictEqual({ ...result.descriptor }, { + value: 42, + writable: true, + enumerable: true, + configurable: true, +}); +assert.strictEqual(result.ownNamesIncludes, true); +assert.strictEqual(result.hasOwnProperty, true); +assert.strictEqual(result.objectHasOwn, true); +assert.strictEqual(result.inGlobalThis, true); +assert.strictEqual(result.reflectHas, true); +assert.strictEqual(result.keysIncludesFoo, true); +assert.strictEqual(result.keysIncludesHidden, false); +assert.strictEqual(result.hiddenIsEnumerable, false); +assert.deepStrictEqual({ ...result.readonlyDescriptor }, { + value: 2, + writable: false, + enumerable: true, + configurable: true, +}); +assert.strictEqual(result.hasOwnToString, false); +assert.strictEqual(result.toStringInGlobalThis, true);