From 1c01440eb01b346c461383612b5f4c4df7fa0ac0 Mon Sep 17 00:00:00 2001 From: PR3C14D0 Date: Wed, 25 Mar 2026 21:20:04 +0100 Subject: [PATCH 1/4] src: add --enable-eval CLI option This introduces a new boolean option `enable_eval` to PerIsolateOptions and registers the `--enable-eval` flag. This flag will be used to explicitly opt-in to dynamic code generation. --- src/node_options.cc | 5 +++++ src/node_options.h | 1 + 2 files changed, 6 insertions(+) diff --git a/src/node_options.cc b/src/node_options.cc index d48641ae3ffe07..a9eb8758800bb4 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -1206,6 +1206,11 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { PerIsolateOptionsParser::PerIsolateOptionsParser( const EnvironmentOptionsParser& eop) { + AddOption("--enable-eval", + "explicitly enable eval() and Function() " + "(disabled by default for security)", + &PerIsolateOptions::enable_eval, + kAllowedInEnvvar); AddOption("--track-heap-objects", "track heap object allocations for heap snapshots", &PerIsolateOptions::track_heap_objects, diff --git a/src/node_options.h b/src/node_options.h index 2f0adb5ae491ec..7437758ab36eab 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -287,6 +287,7 @@ class PerIsolateOptions : public Options { PerIsolateOptions(PerIsolateOptions&&) = default; std::shared_ptr per_env { new EnvironmentOptions() }; + bool enable_eval = false; bool track_heap_objects = false; bool report_uncaught_exception = false; bool report_on_signal = false; From 902c8ec4cf95c4873ee3d373528aa692922e012d Mon Sep 17 00:00:00 2001 From: PR3C14D0 Date: Wed, 25 Mar 2026 21:21:25 +0100 Subject: [PATCH 2/4] src: disable eval() by default and add --enable-eval support Modify context initialization to set kAllowCodeGenerationFromStrings to false by default in both snapshot and runtime. Update ModifyCodeGenerationFromStrings callback to strictly respect the embedder data, effectively disabling eval() and new Function() unless the --enable-eval flag is provided. --- src/api/environment.cc | 5 +++-- src/node_errors.cc | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/api/environment.cc b/src/api/environment.cc index 8c14caa9c95f43..045ee993d17053 100644 --- a/src/api/environment.cc +++ b/src/api/environment.cc @@ -822,7 +822,8 @@ Maybe InitializeContextRuntime(Local context) { // The `IsCodeGenerationFromStringsAllowed` can be refreshed by V8 according // to the runtime flags, propagate the value to the embedder data. bool is_code_generation_from_strings_allowed = - context->IsCodeGenerationFromStringsAllowed(); + context->IsCodeGenerationFromStringsAllowed() && + per_process::cli_options->per_isolate->enable_eval; context->AllowCodeGenerationFromStrings(false); context->SetEmbedderData( ContextEmbedderIndex::kAllowCodeGenerationFromStrings, @@ -923,7 +924,7 @@ Maybe InitializeMainContextForSnapshot(Local context) { context->SetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration, True(isolate)); context->SetEmbedderData( - ContextEmbedderIndex::kAllowCodeGenerationFromStrings, True(isolate)); + ContextEmbedderIndex::kAllowCodeGenerationFromStrings, False(isolate)); if (InitializeBaseContextForSnapshot(context).IsNothing()) { return Nothing(); diff --git a/src/node_errors.cc b/src/node_errors.cc index 23ca0bc50f68c6..1b517df77ebcd2 100644 --- a/src/node_errors.cc +++ b/src/node_errors.cc @@ -659,8 +659,7 @@ v8::ModifyCodeGenerationFromStringsResult ModifyCodeGenerationFromStrings( Local allow_code_gen = context->GetEmbedderData( ContextEmbedderIndex::kAllowCodeGenerationFromStrings); - bool codegen_allowed = - allow_code_gen->IsUndefined() || allow_code_gen->IsTrue(); + bool codegen_allowed = allow_code_gen->IsTrue(); return { codegen_allowed, {}, From bee7accb4d7a57e91f69280dba9ef081f09098f8 Mon Sep 17 00:00:00 2001 From: PR3C14D0 Date: Wed, 25 Mar 2026 21:23:50 +0100 Subject: [PATCH 3/4] test: add test for --enable-eval flag --- test/parallel/test-enable-eval-flag.js | 38 ++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 test/parallel/test-enable-eval-flag.js diff --git a/test/parallel/test-enable-eval-flag.js b/test/parallel/test-enable-eval-flag.js new file mode 100644 index 00000000000000..8efc3a3dcb50f1 --- /dev/null +++ b/test/parallel/test-enable-eval-flag.js @@ -0,0 +1,38 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { spawnSync } = require('child_process'); + +const message = /Code generation from strings disallowed for this context/; + +// Check default behavior (blocked) +// We test this in a subprocess to ensure a clean state +const blockedChild = spawnSync(process.execPath, [ + '-e', + 'eval("1")' +]); +assert.notStrictEqual(blockedChild.status, 0); +assert.match(blockedChild.stderr.toString(), message); + +// Check --enable-eval behavior (allowed) +const allowedChild = spawnSync(process.execPath, [ + '--enable-eval', + '-e', + 'console.log(eval("1 + 1")); console.log(new Function("return 2")())' +]); +assert.strictEqual(allowedChild.status, 0); +assert.strictEqual(allowedChild.stdout.toString().trim(), '2\n2'); + +// Check behavior within the current process (should be blocked by default) +assert.throws(() => eval('1'), { + name: 'EvalError', + message: message +}); + +assert.throws(() => new Function('return 1'), { + name: 'EvalError', + message: message +}); + +console.log('All tests passed'); From 0e5b15deeb89fc834947b577765f2d8c3e8e2d79 Mon Sep 17 00:00:00 2001 From: PR3C14D0 Date: Wed, 25 Mar 2026 21:25:25 +0100 Subject: [PATCH 4/4] docs: add --enable-eval documentation --- doc/api/cli.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/api/cli.md b/doc/api/cli.md index 18205fa6ca790c..115f91b843db77 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -826,6 +826,16 @@ Set the default value of `order` in [`dns.lookup()`][] and The default is `verbatim` and [`dns.setDefaultResultOrder()`][] have higher priority than `--dns-result-order`. +### `--enable-eval` + + + +Enables the use of `eval()` and `new Function()`. By default, these are disabled +for security reasons to prevent arbitrary code execution vulnerabilities. This +does not affect the Node.js `node:vm` module. + ### `--enable-fips`