Skip to content

fix: .env loading no longer overrides already-set environment variables - #252

Open
mattpodwysocki wants to merge 4 commits into
mainfrom
fix/dotenv-no-override
Open

fix: .env loading no longer overrides already-set environment variables#252
mattpodwysocki wants to merge 4 commits into
mainfrom
fix/dotenv-no-override

Conversation

@mattpodwysocki

Copy link
Copy Markdown
Contributor

Summary

  • .env loading previously applied every key onto process.env with override semantics, so a .env file present in the working directory could take precedence over variables already set by the host process (e.g. MAPBOX_API_ENDPOINT, MAPBOX_ACCESS_TOKEN).
  • Extracted the loading logic into src/utils/loadDotEnv.ts and changed it to skip any key that's already set, matching the intent of Node's own process.loadEnvFile(). Already-set keys are now reported in the startup log message and the config.load_env tracing span instead of being silently skipped.

Test plan

  • npx vitest run — all tests pass, including new regression tests in test/utils/loadDotEnv.test.ts covering override precedence
  • npm run build succeeds
  • CHANGELOG.md updated

🤖 Generated with Claude Code

A malicious/updated .env in the project working directory could override
MAPBOX_API_ENDPOINT after the MCP host (Claude Desktop, VS Code, etc.) had
already set it correctly, while the host-injected MAPBOX_ACCESS_TOKEN
survived and kept being sent to the overridden endpoint -- exfiltrating it
to an attacker-controlled host with no error surfaced. .env loading now
skips any key already present in process.env, matching the intent of
Node's own loadEnvFile(), and reports skipped keys in the startup log/span
instead of silently dropping them.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@mattpodwysocki
mattpodwysocki requested a review from a team as a code owner August 25, 2026 16:00
mattpodwysocki and others added 3 commits August 25, 2026 12:01
No code behavior change -- just wording in comments, the changelog entry,
and test fixture values.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Reviewer feedback on the equivalent mcp-devkit-server PR (Mofei) pointed
out a real gap in the prior fix: skipping .env keys only when already set
doesn't help when the host never set MAPBOX_API_ENDPOINT in the first
place -- the common case, since most operators only set
MAPBOX_ACCESS_TOKEN and rely on the built-in https://api.mapbox.com/
default. In that case a malicious .env could still set
MAPBOX_API_ENDPOINT, since "already set" was false, and the real
host-injected access token would still be sent to that endpoint.

loadDotEnv now takes a set of protectedKeys that .env may never set at
all, regardless of whether the target env already has a value for them.
index.ts passes MAPBOX_ACCESS_TOKEN/MAPBOX_API_ENDPOINT. Confirmed live
both ways: before this change, a tool call with a malicious .env and no
host-set MAPBOX_API_ENDPOINT reached the attacker-controlled endpoint;
after it, the same call reaches the real API.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@mattpodwysocki

Copy link
Copy Markdown
Contributor Author

Update: pushed a fix for a gap flagged in review of the equivalent mcp-devkit-server PR (mapbox/mcp-devkit-server#138).

The original fix only skipped .env keys that were already set in the process environment. That doesn't help when the host never set MAPBOX_API_ENDPOINT in the first place — the common case, since most operators only set MAPBOX_ACCESS_TOKEN and rely on the built-in https://api.mapbox.com/ default. In that case a malicious .env could still set MAPBOX_API_ENDPOINT, since it was never "already set" to begin with, and the real host-injected access token would still be sent to that attacker-controlled endpoint.

Fixed by making MAPBOX_ACCESS_TOKEN/MAPBOX_API_ENDPOINT off-limits to .env entirely — loadDotEnv now takes a protectedKeys set that .env may never populate, regardless of whether the target env already has a value. A custom endpoint (e.g. an enterprise proxy) still works, but has to come from a real environment variable at the host/launch-config level, not a project-local .env.

Confirmed live both ways: with the prior fix, a tool call with the malicious .env and no host-set MAPBOX_API_ENDPOINT reached the attacker-controlled host; with this fix, the same call reaches the real API (401: Not Authorized - Invalid Token, the expected response for a fake token).

New regression tests cover this exact scenario in test/utils/loadDotEnv.test.ts.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant