diff --git a/src/env.cc b/src/env.cc index 9d7a1b21e6c9e9..179a553e96592f 100644 --- a/src/env.cc +++ b/src/env.cc @@ -273,6 +273,29 @@ void Environment::CreateProperties() { CHECK(primordials->IsObject()); set_primordials(primordials.As()); + Local prototype_string = + FIXED_ONE_BYTE_STRING(isolate(), "prototype"); + +#define V(EnvPropertyName, PrimordialsPropertyName) \ + { \ + Local ctor = \ + primordials.As() \ + ->Get(ctx, \ + FIXED_ONE_BYTE_STRING(isolate(), PrimordialsPropertyName)) \ + .ToLocalChecked(); \ + CHECK(ctor->IsObject()); \ + Local prototype = \ + ctor.As()->Get(ctx, prototype_string).ToLocalChecked(); \ + CHECK(prototype->IsObject()); \ + set_##EnvPropertyName(prototype.As()); \ + } + + V(primordials_safe_map_prototype_object, "SafeMap"); + V(primordials_safe_set_prototype_object, "SafeSet"); + V(primordials_safe_weak_map_prototype_object, "SafeWeakMap"); + V(primordials_safe_weak_set_prototype_object, "SafeWeakSet"); +#undef V + Local process_object = node::CreateProcessObject(this).FromMaybe(Local()); set_process_object(process_object); diff --git a/src/env.h b/src/env.h index 512dc878049e9a..ee172017e285bc 100644 --- a/src/env.h +++ b/src/env.h @@ -478,6 +478,10 @@ constexpr size_t kFsStatsBufferLength = V(prepare_stack_trace_callback, v8::Function) \ V(process_object, v8::Object) \ V(primordials, v8::Object) \ + V(primordials_safe_map_prototype_object, v8::Object) \ + V(primordials_safe_set_prototype_object, v8::Object) \ + V(primordials_safe_weak_map_prototype_object, v8::Object) \ + V(primordials_safe_weak_set_prototype_object, v8::Object) \ V(promise_hook_handler, v8::Function) \ V(promise_reject_callback, v8::Function) \ V(script_data_constructor_function, v8::Function) \ diff --git a/src/node_options.cc b/src/node_options.cc index e57e6d46c78552..979a5150b55327 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -882,6 +882,12 @@ void GetOptions(const FunctionCallbackInfo& args) { }); Local options = Map::New(isolate); + if (options + ->SetPrototype(context, env->primordials_safe_map_prototype_object()) + .IsNothing()) { + return; + } + for (const auto& item : _ppop_instance.options_) { Local value; const auto& option_info = item.second; @@ -970,6 +976,12 @@ void GetOptions(const FunctionCallbackInfo& args) { Local aliases; if (!ToV8Value(context, _ppop_instance.aliases_).ToLocal(&aliases)) return; + if (aliases.As() + ->SetPrototype(context, env->primordials_safe_map_prototype_object()) + .IsNothing()) { + return; + } + Local ret = Object::New(isolate); if (ret->Set(context, env->options_string(), options).IsNothing() || ret->Set(context, env->aliases_string(), aliases).IsNothing()) { diff --git a/src/uv.cc b/src/uv.cc index bf50c88111fa90..ba40e705c98266 100644 --- a/src/uv.cc +++ b/src/uv.cc @@ -81,6 +81,8 @@ void GetErrMap(const FunctionCallbackInfo& args) { Isolate* isolate = env->isolate(); Local context = env->context(); + // This can't return a SafeMap, because the uv binding can be referenced + // by user code by using `process.binding('uv').getErrorMap()`: Local err_map = Map::New(isolate); size_t errors_len = arraysize(per_process::uv_errors_map); diff --git a/test/parallel/test-options-binding.js b/test/parallel/test-options-binding.js new file mode 100644 index 00000000000000..b113a1ff66bcc9 --- /dev/null +++ b/test/parallel/test-options-binding.js @@ -0,0 +1,18 @@ +// Flags: --expose-internals +'use strict'; + +const common = require('../common'); +const { primordials: { SafeMap } } = require('internal/test/binding'); + +const { options, aliases, getOptionValue } = require('internal/options'); +const assert = require('assert'); + +assert(options instanceof SafeMap, + "require('internal/options').options is a SafeMap"); + +assert(aliases instanceof SafeMap, + "require('internal/options').aliases is a SafeMap"); + +Map.prototype.get = + common.mustNotCall('`getOptionValue` must not call user-mutable method'); +assert.strictEqual(getOptionValue('--expose-internals'), true);