Skip to content

Support NODE_OPTIONS environment variable - #28818

Closed
robobun wants to merge 5 commits into
mainfrom
farm/a5eed730/node-options-support
Closed

robobun wants to merge 5 commits into
mainfrom
farm/a5eed730/node-options-support

Conversation

@robobun

@robobun robobun commented Apr 3, 2026 •

Copy link
Copy Markdown
Collaborator

What

Parse and honor the NODE_OPTIONS environment variable, the same way Node.js does.

Why

Node honors flags like NODE_OPTIONS=--dns-result-order=ipv4first that set runtime options via the environment. Bun ignored NODE_OPTIONS entirely, which meant tools like bun install would hang in environments with broken IPv6 even when the user tried the Node-compatible workaround.

Reported in #28817: NODE_OPTIONS="--dns-result-order=ipv4first" bun install stalled at 🔍 Resolving.

How

src/bun_core/util.rs — argv_view_init already splices BUN_OPTIONS tokens into argv after argv[0]. Right after that, do the same for NODE_OPTIONS via a new append_node_options_env:

  1. Tokenize the env var with a quote- and escape-aware tokenizer (POSIX semantics: backslash is inert inside single quotes; --flag "" preserves the empty value).
  2. Filter tokens through an allowlist (node_option_kind) of Bun-supported flags that are safe to set via the environment. Unknown flags and positional args are dropped, so the env var cannot inject a script or change the entrypoint.
  3. Required-value flags (e.g. --require, --dns-result-order) that appear bare with no following value are dropped entirely, so a bare NODE_OPTIONS="--require" cannot bind the user's entrypoint as the missing value.

Injected tokens count toward bun_options_argc (alongside BUN_OPTIONS) so standalone compiled binaries compute the correct passthrough offset.

src/bun_core/env_var.rs — register the NODE_OPTIONS accessor.

The allowlist covers the flags Bun implements that Node allows in NODE_OPTIONS: --dns-result-order, --conditions/-C, --import, --require/-r, --preserve-symlinks, --preserve-symlinks-main, --title, --max-http-header-size, --inspect/--inspect-brk/--inspect-wait, --expose-gc, --no-addons, --use-system-ca/--use-openssl-ca/--use-bundled-ca, --no-deprecation/--throw-deprecation, --zero-fill-buffers, --unhandled-rejections, --cpu-prof*, and --heap-prof*.

Note on this PR's history

This PR was originally written against the Zig tree. main has since migrated the runtime to Rust, so the branch was reset onto current main and the feature reimplemented in Rust (src/bun_core/util.rs + src/bun_core/env_var.rs). All the earlier review feedback is carried over in the Rust port:

  • bun_options_argc includes NODE_OPTIONS-injected args (correct passthrough offset for standalone binaries).
  • Bare required-value flags are dropped (no entrypoint hijack).
  • Empty quoted values (--flag "") are preserved.
  • Backslash is inert inside single quotes (POSIX).

The dns.getDefaultResultOrder() return-value fix that was part of the original PR is already on main (merged via #28949), so it is no longer part of this diff.

Verification

$ NODE_OPTIONS="--dns-result-order=ipv4first" bun -e 'import dns from "node:dns"; console.log(dns.getDefaultResultOrder())'
ipv4first

$ NODE_OPTIONS="--dns-result-order ipv6first" bun -e 'import dns from "node:dns"; console.log(dns.getDefaultResultOrder())'
ipv6first

$ bun -e 'import dns from "node:dns"; console.log(dns.getDefaultResultOrder())'
verbatim

# Safety: positional args, unknown flags, and bare required-value flags are dropped
$ NODE_OPTIONS="--require" bun run app.js   # app.js still runs; --require dropped
$ NODE_OPTIONS="/etc/passwd --eval console.log('HIJACK')" bun -e 'console.log("safe")'
safe

Tests in test/regression/issue/28817.test.ts cover --dns-result-order (both --flag=value and --flag value forms, quoted values), default verbatim behavior, unknown-flag and positional-arg dropping, --eval safety, --expose-gc, --title, and the bare required-value flag cases. 8 tests pass on the debug build; 6 fail on stock bun (the 2 that pass are safety cases that trivially hold when NODE_OPTIONS is ignored).

Review follow-ups (Node parity)

  • Tokenizer rewritten to match Node's ParseNodeOptionsEnvVar exactly: single quotes are literal, backslash only escapes inside double quotes (unquoted Windows paths keep their backslashes), and only ASCII space separates args. Verified against node v26.
  • Removed -C from the allowlist (Bun has no -C short form for --conditions; clap would reject it).
  • --inspect/--inspect-brk/--inspect-wait are classified as optional-value so bare NODE_OPTIONS=--inspect is kept rather than dropped.

Fixes #28817

@robobun

robobun commented Apr 3, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 11:06 PM PT - May 4th, 2026

❌ @robobun, your commit 30d9121 has 2 failures in Build #51501 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 28818

That installs a local version of the PR into your bun-28818 executable, so you can run:

bun-28818 --bun

@coderabbitai

coderabbitai Bot commented Apr 3, 2026 •

Copy link
Copy Markdown
Contributor

Walkthrough

Parses and filters NODE_OPTIONS into Bun's argv at startup via an allowlist, adds the NODE_OPTIONS env var definition, ensures DNS default result order returns the computed value, and adds regression tests covering NODE_OPTIONS behaviors and security filtering.

Changes

Cohort / File(s) Summary
Argv + NODE_OPTIONS handling
src/bun.zig
Added NodeOptionKind and pub const node_options_allowlist; implemented pub fn appendNodeOptionsEnv(env, args) to quote/backslash-aware tokenize and filter NODE_OPTIONS tokens, and updated initArgv() to merge filtered NODE_OPTIONS into argv.
Environment variable declaration
src/env_var.zig
Added pub const NODE_OPTIONS = New(kind.string, "NODE_OPTIONS", .{});.
DNS API behavior
src/js/node/dns.ts
Changed getDefaultResultOrder() to call and return defaultResultOrder() (returning the computed value rather than the function).
Tests
test/regression/issue/28817.test.ts
Added tests exercising NODE_OPTIONS propagation: --dns-result-order forms (=, space, quoted), unknown/positional flag handling, blocked disallowed flags (e.g., --eval), --expose-gc, and --title behavior.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Support NODE_OPTIONS environment variable' accurately summarizes the main change: adding NODE_OPTIONS parsing and honoring in Bun.
Description check ✅ Passed The description provides comprehensive coverage of all template sections with detailed 'What', 'Why', and 'How' explanations plus verification examples and test information.
Linked Issues check ✅ Passed The PR fully addresses issue #28817 by implementing NODE_OPTIONS parsing with allowlisted flags, enabling NODE_OPTIONS='--dns-result-order=ipv4first' to work as expected, fixing the stalled bun install behavior.
Out of Scope Changes check ✅ Passed All changes are directly related to NODE_OPTIONS support: src/bun.zig and src/env_var.zig implement parsing/filtering, src/js/node/dns.ts fixes the return value, and the test file verifies the feature.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread src/bun.zig

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/bun.zig`:
- Around line 2067-2070: NodeOptionKind's two-state enum is too coarse and lets
required-value flags (e.g., --require, --import, --title, --dns-result-order) be
treated like bare boolean flags; change NodeOptionKind to at least three states
(bool_flag, optional_value, required_value), update the parsing logic used by
appendNodeOptionsEnv and the related token-consumption code so that
required_value flags are dropped unless they include their value inline in
NODE_OPTIONS (do not consume the next argv token as the flag's value), while
optional_value flags may still be emitted bare; ensure all checks that
previously matched NodeOptionKind::option are revised to distinguish required vs
optional so bare required-value flags are never injected.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9dbe2e56-8a97-4feb-b8bf-ce2a88587390

📥 Commits

Reviewing files that changed from the base of the PR and between 85f073f and 559ef85.

📒 Files selected for processing (4)
  • src/bun.zig
  • src/env_var.zig
  • src/js/node/dns.ts
  • test/regression/issue/28817.test.ts

Comment thread src/bun.zig Outdated
Comment thread src/bun.zig Outdated
Comment thread src/bun.zig Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/regression/issue/28817.test.ts`:
- Around line 1-3: In the regression test test/regression/issue/28817.test.ts
remove the two extra comment lines "// Bun should honor
NODE_OPTIONS=--dns-result-order and other Node-compatible" and "// flags set via
the NODE_OPTIONS environment variable." so only the single-line GitHub issue URL
comment on Line 1 remains; keep the file name and test intact and do not modify
any test logic or other content.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 82c0a90d-2c3c-4ed0-81d6-9296d27625bc

📥 Commits

Reviewing files that changed from the base of the PR and between 559ef85 and d8a014f.

📒 Files selected for processing (1)
  • test/regression/issue/28817.test.ts

Comment thread test/regression/issue/28817.test.ts Outdated
Comment thread src/bun.zig Outdated
robobun and others added 5 commits May 4, 2026 23:12
Node.js honors flags passed via the NODE_OPTIONS env var. Bun ignored
it entirely, so things like NODE_OPTIONS=--dns-result-order=ipv4first
were dropped and 'bun install' would hang on hosts with broken IPv6.

Parse NODE_OPTIONS (quote- and escape-aware, like Node) and inject the
tokens into argv before clap parses them. Only flags on an allowlist
of Bun-supported Node-compatible options are honored; positionals and
unknown flags are dropped so the env var can't be used to inject
scripts or change the entrypoint.

Also fixes dns.getDefaultResultOrder() to return the order string
("verbatim"/"ipv4first"/"ipv6first") instead of the internal
function.

Fixes #28817
- Track NODE_OPTIONS-injected flags in bun_options_argc so standalone
  compiled binaries compute the correct passthrough offset and clap
  parses the full injected window.
- Drop bare required-value flags (e.g. NODE_OPTIONS="--require") when
  no value follows in the env var itself. Previously the flag was
  emitted bare and clap bound the user's entrypoint as the missing
  value — reopening the entrypoint-hijack vector this filter closes.
- Preserve empty quoted values (NODE_OPTIONS='--title ""') instead of
  dropping them.
- Backslash has no special meaning inside single quotes (POSIX), so
  it no longer eats the closing apostrophe.
- Add regression tests for bare required-value flag handling.
- Drop the prose comment from the test file per review.
@robobun
robobun force-pushed the farm/a5eed730/node-options-support branch from d8a014f to 30d9121 Compare May 4, 2026 23:19
Comment thread src/bun.zig
Comment thread src/bun.zig
Comment thread src/bun.zig
Comment thread test/regression/issue/28817.test.ts
@robobun

robobun commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator Author

Closing: this PR's implementation lives entirely in Zig source files that have since been removed from the tree as part of the Rust migration. The change can no longer merge cleanly and the files it edits no longer exist on main.

If the underlying issue is still present, it will need a fresh fix against the Rust implementation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bun not honoring NODE_OPTIONS="--dns-result-order=ipv4first

1 participant