Skip to content

dotenv: expose auto-loaded .env values as non-enumerable on process.env - #35481

Closed
robobun wants to merge 14 commits into
mainfrom
farm/5353a8ba/dotenv-conditional-vars
Closed

dotenv: expose auto-loaded .env values as non-enumerable on process.env#35481
robobun wants to merge 14 commits into
mainfrom
farm/5353a8ba/dotenv-conditional-vars

dotenv: expose auto-loaded .env values as non-enumerable on process.env

83b193e
Select commit
Loading
Failed to load commit list.
Claude / Claude Code Review completed Jul 24, 2026 in 18m 4s

Code review found 1 important issue

Found 5 candidates, confirmed 3. See review comments for details.

Details

Severity Count
🔴 Important 1
🟡 Nit 2
🟣 Pre-existing 0
Severity File:Line Issue
🔴 Important src/jsc/bindings/JSEnvironmentVariableMap.cpp:891-897 SHARE_ENV worker founding drops auto-loaded .env values from main thread's process.env
🟡 Nit src/jsc/bindings/JSEnvironmentVariableMap.cpp:831-841 Special-cased and integer-keyed env vars ignore the conditional flag
🟡 Nit test/cli/run/env.test.ts:722-729 'empty string disables default dotenv behavior' test is now vacuous

Annotations

Check failure on line 897 in src/jsc/bindings/JSEnvironmentVariableMap.cpp

See this annotation in the file changed.

@claude claude / Claude Code Review

SHARE_ENV worker founding drops auto-loaded .env values from main thread's process.env

Marking auto-loaded `.env` keys `DontEnum` breaks the SHARE_ENV worker path: `ensureSharedEnvStoreForWorker` seeds the shared store via `getOwnPropertyNames(..., DontEnumPropertiesMode::Exclude)` and then swaps the main thread's `process.env` to a `JSSharedEnvMap` that reads only from that store. After the first `new Worker(url, { env: SHARE_ENV })`, `process.env.API_KEY` on the **main thread** returns `undefined` for any key that came only from an auto-discovered `.env*` file — a regression tha

Check warning on line 841 in src/jsc/bindings/JSEnvironmentVariableMap.cpp

See this annotation in the file changed.

@claude claude / Claude Code Review

Special-cased and integer-keyed env vars ignore the conditional flag

The `conditional` flag isn't honored by every arm of the per-key loop: the special-cased names (`TZ`, `NODE_TLS_REJECT_UNAUTHORIZED`, `BUN_CONFIG_VERBOSE_FETCH`, and the six proxy vars) set `hasXXX = true; continue;` before it's consulted, and the integer-index fast path passes attributes `0` to `putDirectIndex` unconditionally. So a `.env`-only `HTTP_PROXY`, `TZ`, or `123=foo` is still installed *enumerable* on POSIX — while on Windows the `if (!conditional)` guard on `keyArray->push` correctly

Check warning on line 729 in test/cli/run/env.test.ts

See this annotation in the file changed.

@claude claude / Claude Code Review

'empty string disables default dotenv behavior' test is now vacuous

The `empty string disables default dotenv behavior` test (and `should ignore a file that doesn't exist` at line 741) still uses the shared `index.ts` that enumerates via `Object.entries(process.env)`. After this PR, if `--env-file=''` regressed and auto-loaded `.env` again, `BUNTEST_DOTENV` would be installed `DontEnum` and `Object.entries` would still emit `""` — the assertion can no longer fail for the property it protects. Apply the same direct-read fix used just above: spawn with `-e 'consol