From d1f18b0bf16efbc1e54ba04a54735ce4683cb936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Tue, 4 Jun 2024 08:45:39 +0200 Subject: [PATCH] vm,src: add property query interceptors Fixes: https://github.com/nodejs/node/issues/52720 PR-URL: https://github.com/nodejs/node/pull/53172 Reviewed-By: Joyee Cheung --- src/node_contextify.cc | 68 ++++++++++++++- src/node_contextify.h | 5 ++ .../test-vm-global-property-prototype.js | 83 +++++++++++++++++++ 3 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 test/parallel/test-vm-global-property-prototype.js diff --git a/src/node_contextify.cc b/src/node_contextify.cc index 53062af31eea5a..8dacdc45a3e400 100644 --- a/src/node_contextify.cc +++ b/src/node_contextify.cc @@ -51,6 +51,7 @@ using v8::FunctionTemplate; using v8::HandleScope; using v8::IndexedPropertyHandlerConfiguration; using v8::Int32; +using v8::Integer; using v8::Intercepted; using v8::Isolate; using v8::Just; @@ -176,20 +177,22 @@ void ContextifyContext::InitializeGlobalTemplates(IsolateData* isolate_data) { NamedPropertyHandlerConfiguration config( PropertyGetterCallback, PropertySetterCallback, - PropertyDescriptorCallback, + PropertyQueryCallback, PropertyDeleterCallback, PropertyEnumeratorCallback, PropertyDefinerCallback, + PropertyDescriptorCallback, {}, PropertyHandlerFlags::kHasNoSideEffect); IndexedPropertyHandlerConfiguration indexed_config( IndexedPropertyGetterCallback, IndexedPropertySetterCallback, - IndexedPropertyDescriptorCallback, + IndexedPropertyQueryCallback, IndexedPropertyDeleterCallback, PropertyEnumeratorCallback, IndexedPropertyDefinerCallback, + IndexedPropertyDescriptorCallback, {}, PropertyHandlerFlags::kHasNoSideEffect); @@ -354,12 +357,14 @@ void ContextifyContext::RegisterExternalReferences( ExternalReferenceRegistry* registry) { registry->Register(MakeContext); registry->Register(CompileFunction); + registry->Register(PropertyQueryCallback); registry->Register(PropertyGetterCallback); registry->Register(PropertySetterCallback); registry->Register(PropertyDescriptorCallback); registry->Register(PropertyDeleterCallback); registry->Register(PropertyEnumeratorCallback); registry->Register(PropertyDefinerCallback); + registry->Register(IndexedPropertyQueryCallback); registry->Register(IndexedPropertyGetterCallback); registry->Register(IndexedPropertySetterCallback); registry->Register(IndexedPropertyDescriptorCallback); @@ -458,6 +463,51 @@ bool ContextifyContext::IsStillInitializing(const ContextifyContext* ctx) { return ctx == nullptr || ctx->context_.IsEmpty(); } +// static +Intercepted ContextifyContext::PropertyQueryCallback( + Local property, const PropertyCallbackInfo& args) { + ContextifyContext* ctx = ContextifyContext::Get(args); + + // Still initializing + if (IsStillInitializing(ctx)) { + return Intercepted::kNo; + } + + Local context = ctx->context(); + Local sandbox = ctx->sandbox(); + + PropertyAttribute attr; + + Maybe maybe_has = sandbox->HasRealNamedProperty(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)) { + return Intercepted::kNo; + } + args.GetReturnValue().Set(attr); + return Intercepted::kYes; + } else { + maybe_has = ctx->global_proxy()->HasRealNamedProperty(context, property); + if (maybe_has.IsNothing()) { + return Intercepted::kNo; + } else if (maybe_has.FromJust()) { + Maybe maybe_attr = + ctx->global_proxy()->GetRealNamedPropertyAttributes(context, + property); + if (!maybe_attr.To(&attr)) { + return Intercepted::kNo; + } + args.GetReturnValue().Set(attr); + return Intercepted::kYes; + } + } + + return Intercepted::kNo; +} + // static Intercepted ContextifyContext::PropertyGetterCallback( Local property, const PropertyCallbackInfo& args) { @@ -709,6 +759,20 @@ void ContextifyContext::PropertyEnumeratorCallback( args.GetReturnValue().Set(properties); } +// static +Intercepted ContextifyContext::IndexedPropertyQueryCallback( + uint32_t index, const PropertyCallbackInfo& args) { + ContextifyContext* ctx = ContextifyContext::Get(args); + + // Still initializing + if (IsStillInitializing(ctx)) { + return Intercepted::kNo; + } + + return ContextifyContext::PropertyQueryCallback( + Uint32ToName(ctx->context(), index), args); +} + // static Intercepted ContextifyContext::IndexedPropertyGetterCallback( uint32_t index, const PropertyCallbackInfo& args) { diff --git a/src/node_contextify.h b/src/node_contextify.h index afefe54cbbe4b9..1accfb4693767f 100644 --- a/src/node_contextify.h +++ b/src/node_contextify.h @@ -96,6 +96,9 @@ class ContextifyContext : public BaseObject { const errors::TryCatchScope& try_catch); static void WeakCallback( const v8::WeakCallbackInfo& data); + static v8::Intercepted PropertyQueryCallback( + v8::Local property, + const v8::PropertyCallbackInfo& args); static v8::Intercepted PropertyGetterCallback( v8::Local property, const v8::PropertyCallbackInfo& args); @@ -115,6 +118,8 @@ class ContextifyContext : public BaseObject { const v8::PropertyCallbackInfo& args); static void PropertyEnumeratorCallback( const v8::PropertyCallbackInfo& args); + static v8::Intercepted IndexedPropertyQueryCallback( + uint32_t index, const v8::PropertyCallbackInfo& args); static v8::Intercepted IndexedPropertyGetterCallback( uint32_t index, const v8::PropertyCallbackInfo& args); static v8::Intercepted IndexedPropertySetterCallback( diff --git a/test/parallel/test-vm-global-property-prototype.js b/test/parallel/test-vm-global-property-prototype.js new file mode 100644 index 00000000000000..fe8abc8be45ee2 --- /dev/null +++ b/test/parallel/test-vm-global-property-prototype.js @@ -0,0 +1,83 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +const sandbox = { + onSelf: 'onSelf', +}; + +function onSelfGetter() { + return 'onSelfGetter'; +} + +Object.defineProperty(sandbox, 'onSelfGetter', { + get: onSelfGetter, +}); + +Object.defineProperty(sandbox, 1, { + value: 'onSelfIndexed', + writable: false, + enumerable: false, + configurable: true, +}); + +const ctx = vm.createContext(sandbox); + +const result = vm.runInContext(` +Object.prototype.onProto = 'onProto'; +Object.defineProperty(Object.prototype, 'onProtoGetter', { + get() { + return 'onProtoGetter'; + }, +}); +Object.defineProperty(Object.prototype, 2, { + value: 'onProtoIndexed', + writable: false, + enumerable: false, + configurable: true, +}); + +const resultHasOwn = { + onSelf: Object.hasOwn(this, 'onSelf'), + onSelfGetter: Object.hasOwn(this, 'onSelfGetter'), + onSelfIndexed: Object.hasOwn(this, 1), + onProto: Object.hasOwn(this, 'onProto'), + onProtoGetter: Object.hasOwn(this, 'onProtoGetter'), + onProtoIndexed: Object.hasOwn(this, 2), +}; + +const getDesc = (prop) => Object.getOwnPropertyDescriptor(this, prop); +const resultDesc = { + onSelf: getDesc('onSelf'), + onSelfGetter: getDesc('onSelfGetter'), + onSelfIndexed: getDesc(1), + onProto: getDesc('onProto'), + onProtoGetter: getDesc('onProtoGetter'), + onProtoIndexed: getDesc(2), +}; +({ + resultHasOwn, + resultDesc, +}); +`, ctx); + +// eslint-disable-next-line no-restricted-properties +assert.deepEqual(result, { + resultHasOwn: { + onSelf: true, + onSelfGetter: true, + onSelfIndexed: true, + onProto: false, + onProtoGetter: false, + onProtoIndexed: false, + }, + resultDesc: { + onSelf: { value: 'onSelf', writable: true, enumerable: true, configurable: true }, + onSelfGetter: { get: onSelfGetter, set: undefined, enumerable: false, configurable: false }, + onSelfIndexed: { value: 'onSelfIndexed', writable: false, enumerable: false, configurable: true }, + onProto: undefined, + onProtoGetter: undefined, + onProtoIndexed: undefined, + }, +});