Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`

<!-- YAML
added: v26.0.0
-->

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`

<!-- YAML
Expand Down
5 changes: 3 additions & 2 deletions src/api/environment.cc
Original file line number Diff line number Diff line change
Expand Up @@ -822,7 +822,8 @@ Maybe<void> InitializeContextRuntime(Local<Context> 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,
Expand Down Expand Up @@ -923,7 +924,7 @@ Maybe<void> InitializeMainContextForSnapshot(Local<Context> context) {
context->SetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration,
True(isolate));
context->SetEmbedderData(
ContextEmbedderIndex::kAllowCodeGenerationFromStrings, True(isolate));
ContextEmbedderIndex::kAllowCodeGenerationFromStrings, False(isolate));

if (InitializeBaseContextForSnapshot(context).IsNothing()) {
return Nothing<void>();
Expand Down
3 changes: 1 addition & 2 deletions src/node_errors.cc
Original file line number Diff line number Diff line change
Expand Up @@ -659,8 +659,7 @@ v8::ModifyCodeGenerationFromStringsResult ModifyCodeGenerationFromStrings(

Local<Value> 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,
{},
Expand Down
5 changes: 5 additions & 0 deletions src/node_options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions src/node_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ class PerIsolateOptions : public Options {
PerIsolateOptions(PerIsolateOptions&&) = default;

std::shared_ptr<EnvironmentOptions> per_env { new EnvironmentOptions() };
bool enable_eval = false;
bool track_heap_objects = false;
bool report_uncaught_exception = false;
bool report_on_signal = false;
Expand Down
38 changes: 38 additions & 0 deletions test/parallel/test-enable-eval-flag.js
Original file line number Diff line number Diff line change
@@ -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');
Loading