| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent ef32069 commit 36ba54e
10 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -127,6 +127,15 @@ added: v12.0.0 | |||
| 127 | 127 | ||
| 128 | 128 | Specify the file name of the CPU profile generated by `--cpu-prof`. | |
| 129 | 129 | ||
| 130 | + ### `--disable-proto=mode` | ||
| 131 | + <!--YAML | ||
| 132 | + added: REPLACEME | ||
| 133 | + --> | ||
| 134 | + | ||
| 135 | + Disable the `Object.prototype.__proto__` property. If `mode` is `delete`, the | ||
| 136 | + property will be removed entirely. If `mode` is `throw`, accesses to the | ||
| 137 | + property will throw an exception with the code `ERR_PROTO_ACCESS`. | ||
| 138 | + | ||
| 130 | 139 | ### `--disallow-code-generation-from-strings` | |
| 131 | 140 | <!-- YAML | |
| 132 | 141 | added: v9.8.0 | |
@@ -1109,6 +1118,7 @@ node --require "./a.js" --require "./b.js" | |||
| 1109 | 1118 | ||
| 1110 | 1119 | Node.js options that are allowed are: | |
| 1111 | 1120 | <!-- node-options-node start --> | |
| 1121 | + * `--disable-proto` | ||
| 1112 | 1122 | * `--enable-fips` | |
| 1113 | 1123 | * `--enable-source-maps` | |
| 1114 | 1124 | * `--experimental-import-meta-resolve` | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1674,6 +1674,14 @@ The `package.json` [exports][] field does not export the requested subpath. | |||
| 1674 | 1674 | Because exports are encapsulated, private internal modules that are not exported | |
| 1675 | 1675 | cannot be imported through the package resolution, unless using an absolute URL. | |
| 1676 | 1676 | ||
| 1677 | + <a id="ERR_PROTO_ACCESS"></a> | ||
| 1678 | + ### `ERR_PROTO_ACCESS` | ||
| 1679 | + | ||
| 1680 | + Accessing `Object.prototype.__proto__` has been forbidden using | ||
| 1681 | + [`--disable-proto=throw`][]. [`Object.getPrototypeOf`][] and | ||
| 1682 | + [`Object.setPrototypeOf`][] should be used to get and set the prototype of an | ||
| 1683 | + object. | ||
| 1684 | + | ||
| 1677 | 1685 | <a id="ERR_REQUIRE_ESM"></a> | |
| 1678 | 1686 | ### `ERR_REQUIRE_ESM` | |
| 1679 | 1687 | ||
@@ -2490,10 +2498,13 @@ This `Error` is thrown when a read is attempted on a TTY `WriteStream`, | |||
| 2490 | 2498 | such as `process.stdout.on('data')`. | |
| 2491 | 2499 | ||
| 2492 | 2500 | [`'uncaughtException'`]: process.html#process_event_uncaughtexception | |
| 2501 | + [`--disable-proto=throw`]: cli.html#cli_disable_proto_mode | ||
| 2493 | 2502 | [`--force-fips`]: cli.html#cli_force_fips | |
| 2494 | 2503 | [`Class: assert.AssertionError`]: assert.html#assert_class_assert_assertionerror | |
| 2495 | 2504 | [`ERR_INVALID_ARG_TYPE`]: #ERR_INVALID_ARG_TYPE | |
| 2496 | 2505 | [`EventEmitter`]: events.html#events_class_eventemitter | |
| 2506 | + [`Object.getPrototypeOf`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getPrototypeOf | ||
| 2507 | + [`Object.setPrototypeOf`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf | ||
| 2497 | 2508 | [`REPL`]: repl.html | |
| 2498 | 2509 | [`Writable`]: stream.html#stream_class_stream_writable | |
| 2499 | 2510 | [`child_process`]: child_process.html | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -100,6 +100,14 @@ The default is | |||
| 100 | 100 | File name of the V8 CPU profile generated with | |
| 101 | 101 | .Fl -cpu-prof | |
| 102 | 102 | . | |
| 103 | + .It Fl -disable-proto Ns = Ns Ar mode | ||
| 104 | + Disable the `Object.prototype.__proto__` property. If | ||
| 105 | + .Ar mode | ||
| 106 | + is `delete`, the property will be removed entirely. If | ||
| 107 | + .Ar mode | ||
| 108 | + is `throw`, accesses to the property will throw an exception with the code | ||
| 109 | + `ERR_PROTO_ACCESS`. | ||
| 110 | + . | ||
| 103 | 111 | .It Fl -disallow-code-generation-from-strings | |
| 104 | 112 | Make built-in language features like `eval` and `new Function` that generate | |
| 105 | 113 | code from strings throw an exception instead. This does not affect the Node.js | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -14,6 +14,7 @@ using v8::Context; | |||
| 14 | 14 | using v8::EscapableHandleScope; | |
| 15 | 15 | using v8::FinalizationGroup; | |
| 16 | 16 | using v8::Function; | |
| 17 | + using v8::FunctionCallbackInfo; | ||
| 17 | 18 | using v8::HandleScope; | |
| 18 | 19 | using v8::Isolate; | |
| 19 | 20 | using v8::Local; | |
@@ -23,6 +24,7 @@ using v8::Null; | |||
| 23 | 24 | using v8::Object; | |
| 24 | 25 | using v8::ObjectTemplate; | |
| 25 | 26 | using v8::Private; | |
| 27 | + using v8::PropertyDescriptor; | ||
| 26 | 28 | using v8::String; | |
| 27 | 29 | using v8::Value; | |
| 28 | 30 | ||
@@ -417,6 +419,10 @@ Local<Context> NewContext(Isolate* isolate, | |||
| 417 | 419 | return context; | |
| 418 | 420 | } | |
| 419 | 421 | ||
| 422 | + void ProtoThrower(const FunctionCallbackInfo<Value>& info) { | ||
| 423 | + THROW_ERR_PROTO_ACCESS(info.GetIsolate()); | ||
| 424 | + } | ||
| 425 | + | ||
| 420 | 426 | // This runs at runtime, regardless of whether the context | |
| 421 | 427 | // is created from a snapshot. | |
| 422 | 428 | void InitializeContextRuntime(Local<Context> context) { | |
@@ -445,6 +451,32 @@ void InitializeContextRuntime(Local<Context> context) { | |||
| 445 | 451 | Local<Object> atomics = atomics_v.As<Object>(); | |
| 446 | 452 | atomics->Delete(context, wake_string).FromJust(); | |
| 447 | 453 | } | |
| 454 | + | ||
| 455 | + // Remove __proto__ | ||
| 456 | + // https://github.com/nodejs/node/issues/31951 | ||
| 457 | + Local<String> object_string = FIXED_ONE_BYTE_STRING(isolate, "Object"); | ||
| 458 | + Local<String> prototype_string = FIXED_ONE_BYTE_STRING(isolate, "prototype"); | ||
| 459 | + Local<Object> prototype = context->Global() | ||
| 460 | + ->Get(context, object_string) | ||
| 461 | + .ToLocalChecked() | ||
| 462 | + .As<Object>() | ||
| 463 | + ->Get(context, prototype_string) | ||
| 464 | + .ToLocalChecked() | ||
| 465 | + .As<Object>(); | ||
| 466 | + Local<String> proto_string = FIXED_ONE_BYTE_STRING(isolate, "__proto__"); | ||
| 467 | + if (per_process::cli_options->disable_proto == "delete") { | ||
| 468 | + prototype->Delete(context, proto_string).ToChecked(); | ||
| 469 | + } else if (per_process::cli_options->disable_proto == "throw") { | ||
| 470 | + Local<Value> thrower = | ||
| 471 | + Function::New(context, ProtoThrower).ToLocalChecked(); | ||
| 472 | + PropertyDescriptor descriptor(thrower, thrower); | ||
| 473 | + descriptor.set_enumerable(false); | ||
| 474 | + descriptor.set_configurable(true); | ||
| 475 | + prototype->DefineProperty(context, proto_string, descriptor).ToChecked(); | ||
| 476 | + } else if (per_process::cli_options->disable_proto != "") { | ||
| 477 | + // Validated in ProcessGlobalArgs | ||
| 478 | + FatalError("InitializeContextRuntime()", "invalid --disable-proto mode"); | ||
| 479 | + } | ||
| 448 | 480 | } | |
| 449 | 481 | ||
| 450 | 482 | bool InitializeContextForSnapshot(Local<Context> context) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -718,6 +718,13 @@ int ProcessGlobalArgs(std::vector<std::string>* args, | |||
| 718 | 718 | } | |
| 719 | 719 | } | |
| 720 | 720 | ||
| 721 | + if (per_process::cli_options->disable_proto != "delete" && | ||
| 722 | + per_process::cli_options->disable_proto != "throw" && | ||
| 723 | + per_process::cli_options->disable_proto != "") { | ||
| 724 | + errors->emplace_back("invalid mode passed to --disable-proto"); | ||
| 725 | + return 12; | ||
| 726 | + } | ||
| 727 | + | ||
| 721 | 728 | auto env_opts = per_process::cli_options->per_isolate->per_env; | |
| 722 | 729 | if (std::find(v8_args.begin(), v8_args.end(), | |
| 723 | 730 | "--abort-on-uncaught-exception") != v8_args.end() || | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -31,33 +31,34 @@ void OnFatalError(const char* location, const char* message); | |||
| 31 | 31 | // `node::ERR_INVALID_ARG_TYPE(isolate, "message")` returning | |
| 32 | 32 | // a `Local<Value>` containing the TypeError with proper code and message | |
| 33 | 33 | ||
| 34 | - #define ERRORS_WITH_CODE(V) \ | ||
| 35 | - V(ERR_BUFFER_CONTEXT_NOT_AVAILABLE, Error) \ | ||
| 36 | - V(ERR_BUFFER_OUT_OF_BOUNDS, RangeError) \ | ||
| 37 | - V(ERR_BUFFER_TOO_LARGE, Error) \ | ||
| 38 | - V(ERR_CONSTRUCT_CALL_REQUIRED, TypeError) \ | ||
| 39 | - V(ERR_CONSTRUCT_CALL_INVALID, TypeError) \ | ||
| 40 | - V(ERR_CRYPTO_UNKNOWN_CIPHER, Error) \ | ||
| 41 | - V(ERR_CRYPTO_UNKNOWN_DH_GROUP, Error) \ | ||
| 42 | - V(ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE, Error) \ | ||
| 43 | - V(ERR_INVALID_ARG_VALUE, TypeError) \ | ||
| 44 | - V(ERR_OSSL_EVP_INVALID_DIGEST, Error) \ | ||
| 45 | - V(ERR_INVALID_ARG_TYPE, TypeError) \ | ||
| 46 | - V(ERR_INVALID_TRANSFER_OBJECT, TypeError) \ | ||
| 47 | - V(ERR_MEMORY_ALLOCATION_FAILED, Error) \ | ||
| 48 | - V(ERR_MISSING_ARGS, TypeError) \ | ||
| 49 | - V(ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST, TypeError) \ | ||
| 50 | - V(ERR_MISSING_PASSPHRASE, TypeError) \ | ||
| 51 | - V(ERR_MISSING_PLATFORM_FOR_WORKER, Error) \ | ||
| 52 | - V(ERR_NON_CONTEXT_AWARE_DISABLED, Error) \ | ||
| 53 | - V(ERR_OUT_OF_RANGE, RangeError) \ | ||
| 54 | - V(ERR_SCRIPT_EXECUTION_INTERRUPTED, Error) \ | ||
| 55 | - V(ERR_SCRIPT_EXECUTION_TIMEOUT, Error) \ | ||
| 56 | - V(ERR_STRING_TOO_LONG, Error) \ | ||
| 57 | - V(ERR_TLS_INVALID_PROTOCOL_METHOD, TypeError) \ | ||
| 58 | - V(ERR_TRANSFERRING_EXTERNALIZED_SHAREDARRAYBUFFER, TypeError) \ | ||
| 59 | - V(ERR_TLS_PSK_SET_IDENTIY_HINT_FAILED, Error) \ | ||
| 60 | - V(ERR_VM_MODULE_CACHED_DATA_REJECTED, Error) \ | ||
| 34 | + #define ERRORS_WITH_CODE(V) \ | ||
| 35 | + V(ERR_BUFFER_CONTEXT_NOT_AVAILABLE, Error) \ | ||
| 36 | + V(ERR_BUFFER_OUT_OF_BOUNDS, RangeError) \ | ||
| 37 | + V(ERR_BUFFER_TOO_LARGE, Error) \ | ||
| 38 | + V(ERR_CONSTRUCT_CALL_REQUIRED, TypeError) \ | ||
| 39 | + V(ERR_CONSTRUCT_CALL_INVALID, TypeError) \ | ||
| 40 | + V(ERR_CRYPTO_UNKNOWN_CIPHER, Error) \ | ||
| 41 | + V(ERR_CRYPTO_UNKNOWN_DH_GROUP, Error) \ | ||
| 42 | + V(ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE, Error) \ | ||
| 43 | + V(ERR_INVALID_ARG_VALUE, TypeError) \ | ||
| 44 | + V(ERR_OSSL_EVP_INVALID_DIGEST, Error) \ | ||
| 45 | + V(ERR_INVALID_ARG_TYPE, TypeError) \ | ||
| 46 | + V(ERR_INVALID_TRANSFER_OBJECT, TypeError) \ | ||
| 47 | + V(ERR_MEMORY_ALLOCATION_FAILED, Error) \ | ||
| 48 | + V(ERR_MISSING_ARGS, TypeError) \ | ||
| 49 | + V(ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST, TypeError) \ | ||
| 50 | + V(ERR_MISSING_PASSPHRASE, TypeError) \ | ||
| 51 | + V(ERR_MISSING_PLATFORM_FOR_WORKER, Error) \ | ||
| 52 | + V(ERR_NON_CONTEXT_AWARE_DISABLED, Error) \ | ||
| 53 | + V(ERR_OUT_OF_RANGE, RangeError) \ | ||
| 54 | + V(ERR_SCRIPT_EXECUTION_INTERRUPTED, Error) \ | ||
| 55 | + V(ERR_SCRIPT_EXECUTION_TIMEOUT, Error) \ | ||
| 56 | + V(ERR_STRING_TOO_LONG, Error) \ | ||
| 57 | + V(ERR_TLS_INVALID_PROTOCOL_METHOD, TypeError) \ | ||
| 58 | + V(ERR_TRANSFERRING_EXTERNALIZED_SHAREDARRAYBUFFER, TypeError) \ | ||
| 59 | + V(ERR_TLS_PSK_SET_IDENTIY_HINT_FAILED, Error) \ | ||
| 60 | + V(ERR_VM_MODULE_CACHED_DATA_REJECTED, Error) \ | ||
| 61 | + V(ERR_PROTO_ACCESS, Error) | ||
| 61 | 62 | ||
| 62 | 63 | #define V(code, type) \ | |
| 63 | 64 | inline v8::Local<v8::Value> code(v8::Isolate* isolate, \ | |
@@ -105,7 +106,10 @@ void OnFatalError(const char* location, const char* message); | |||
| 105 | 106 | "Script execution was interrupted by `SIGINT`") \ | |
| 106 | 107 | V(ERR_TRANSFERRING_EXTERNALIZED_SHAREDARRAYBUFFER, \ | |
| 107 | 108 | "Cannot serialize externalized SharedArrayBuffer") \ | |
| 108 | - V(ERR_TLS_PSK_SET_IDENTIY_HINT_FAILED, "Failed to set PSK identity hint") | ||
| 109 | + V(ERR_TLS_PSK_SET_IDENTIY_HINT_FAILED, "Failed to set PSK identity hint") \ | ||
| 110 | + V(ERR_PROTO_ACCESS, \ | ||
| 111 | + "Accessing Object.prototype.__proto__ has been " \ | ||
| 112 | + "disallowed with --disable-proto=throw") | ||
| 109 | 113 | ||
| 110 | 114 | #define V(code, message) \ | |
| 111 | 115 | inline v8::Local<v8::Value> code(v8::Isolate* isolate) { \ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -639,7 +639,10 @@ PerProcessOptionsParser::PerProcessOptionsParser( | |||
| 639 | 639 | "", /* undocumented, only for debugging */ | |
| 640 | 640 | &PerProcessOptions::debug_arraybuffer_allocations, | |
| 641 | 641 | kAllowedInEnvironment); | |
| 642 | - | ||
| 642 | + AddOption("--disable-proto", | ||
| 643 | + "disable Object.prototype.__proto__", | ||
| 644 | + &PerProcessOptions::disable_proto, | ||
| 645 | + kAllowedInEnvironment); | ||
| 643 | 646 | ||
| 644 | 647 | // 12.x renamed this inadvertently, so alias it for consistency within the | |
| 645 | 648 | // release line, while using the original name for consistency with older | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -205,6 +205,7 @@ class PerProcessOptions : public Options { | |||
| 205 | 205 | int64_t v8_thread_pool_size = 4; | |
| 206 | 206 | bool zero_fill_all_buffers = false; | |
| 207 | 207 | bool debug_arraybuffer_allocations = false; | |
| 208 | + std::string disable_proto; | ||
| 208 | 209 | ||
| 209 | 210 | std::vector<std::string> security_reverts; | |
| 210 | 211 | bool print_bash_completion = false; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,25 @@ | |||
| 1 | + // Flags: --disable-proto=delete | ||
| 2 | + | ||
| 3 | + 'use strict'; | ||
| 4 | + | ||
| 5 | + require('../common'); | ||
| 6 | + const assert = require('assert'); | ||
| 7 | + const vm = require('vm'); | ||
| 8 | + const { Worker, isMainThread } = require('worker_threads'); | ||
| 9 | + | ||
| 10 | + // eslint-disable-next-line no-proto | ||
| 11 | + assert.strictEqual(Object.prototype.__proto__, undefined); | ||
| 12 | + assert(!Object.prototype.hasOwnProperty('__proto__')); | ||
| 13 | + | ||
| 14 | + const ctx = vm.createContext(); | ||
| 15 | + const ctxGlobal = vm.runInContext('this', ctx); | ||
| 16 | + | ||
| 17 | + // eslint-disable-next-line no-proto | ||
| 18 | + assert.strictEqual(ctxGlobal.Object.prototype.__proto__, undefined); | ||
| 19 | + assert(!ctxGlobal.Object.prototype.hasOwnProperty('__proto__')); | ||
| 20 | + | ||
| 21 | + if (isMainThread) { | ||
| 22 | + new Worker(__filename); | ||
| 23 | + } else { | ||
| 24 | + process.exit(); | ||
| 25 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,44 @@ | |||
| 1 | + // Flags: --disable-proto=throw | ||
| 2 | + | ||
| 3 | + 'use strict'; | ||
| 4 | + | ||
| 5 | + require('../common'); | ||
| 6 | + const assert = require('assert'); | ||
| 7 | + const vm = require('vm'); | ||
| 8 | + const { Worker, isMainThread } = require('worker_threads'); | ||
| 9 | + | ||
| 10 | + assert(Object.prototype.hasOwnProperty('__proto__')); | ||
| 11 | + | ||
| 12 | + assert.throws(() => { | ||
| 13 | + // eslint-disable-next-line no-proto | ||
| 14 | + ({}).__proto__; | ||
| 15 | + }, { | ||
| 16 | + code: 'ERR_PROTO_ACCESS' | ||
| 17 | + }); | ||
| 18 | + | ||
| 19 | + assert.throws(() => { | ||
| 20 | + // eslint-disable-next-line no-proto | ||
| 21 | + ({}).__proto__ = {}; | ||
| 22 | + }, { | ||
| 23 | + code: 'ERR_PROTO_ACCESS', | ||
| 24 | + }); | ||
| 25 | + | ||
| 26 | + const ctx = vm.createContext(); | ||
| 27 | + | ||
| 28 | + assert.throws(() => { | ||
| 29 | + vm.runInContext('({}).__proto__;', ctx); | ||
| 30 | + }, { | ||
| 31 | + code: 'ERR_PROTO_ACCESS' | ||
| 32 | + }); | ||
| 33 | + | ||
| 34 | + assert.throws(() => { | ||
| 35 | + vm.runInContext('({}).__proto__ = {};', ctx); | ||
| 36 | + }, { | ||
| 37 | + code: 'ERR_PROTO_ACCESS', | ||
| 38 | + }); | ||
| 39 | + | ||
| 40 | + if (isMainThread) { | ||
| 41 | + new Worker(__filename); | ||
| 42 | + } else { | ||
| 43 | + process.exit(); | ||
| 44 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments