Skip to content

[wrangler] Fix case-insensitive-env leaking stale duplicate keys on override - #14999

Merged
petebacondarwin merged 3 commits into
cloudflare:mainfrom
mittalpk:fix/case-insensitive-env-stale-keys
Aug 16, 2026
Merged

petebacondarwin merged 3 commits into
cloudflare:mainfrom
mittalpk:fix/case-insensitive-env-stale-keys

Conversation

@mittalpk

@mittalpk mittalpk commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Fix case-insensitive-env.ts leaking stale, differently-cased duplicate keys.

On Windows, wrangler loads .env values through a case-insensitive Proxy wrapper (packages/wrangler/src/config/case-insensitive-env.ts) so lookups like env.PATH and env.Path resolve to the same value, and this object is assigned directly to process.env (packages/wrangler/src/config/dot-env.ts). When a key is set again under a different casing (e.g. .env.local overriding a value from .env with different casing), the previous casing's property was never removed from the underlying target object. get/has still resolved correctly through the internal tracking map, but anything that enumerates process.envObject.keys, for...in, JSON.stringify, object spread, or a spawned subprocess inheriting the environment — saw both the stale and current key.

The set, defineProperty, and deleteProperty traps now keep exactly one own-property per case-insensitive key, so enumeration always reflects only the current value. (deleteProperty had the same class of bug in the other direction: it deleted from the internal tracking map by canonical key but issued Reflect.deleteProperty using whatever casing the caller passed, so delete env.path after env.PATH = ... left an orphaned, untracked PATH property on the target — fixed the same way, by resolving the tracked casing first.)

Repro (before the fix):

const env = caseInsensitiveEnv();
env.PATH = "1";
env.Path = "2";
Object.keys(env); // ["PATH", "Path"] — should be ["Path"]
JSON.stringify(env); // {"PATH":"2","Path":"2"} — should be {"Path":"2"}

  • Tests
    • Tests included/updated
    • Automated tests not possible - manual testing has been completed as follows:
    • Additional testing not necessary because:
  • Public documentation
    • Cloudflare docs PR(s):
    • Documentation not necessary because: internal bug fix, no user-facing API/behavior change beyond correcting the leak

Open in Devin Review

@changeset-bot

changeset-bot Bot commented Aug 3, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a3c5e60

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
wrangler Patch
@cloudflare/vite-plugin Patch
@cloudflare/vitest-pool-workers Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@workers-devprod
workers-devprod requested review from a team and emily-shen and removed request for a team August 3, 2026 17:05
@workers-devprod

Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • @cloudflare/wrangler
Show detailed file reviewers
  • .changeset/case-insensitive-env-stale-keys.md: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/case-insensitive-env.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/config/case-insensitive-env.ts: [@cloudflare/wrangler]

devin-ai-integration[bot]

This comment was marked as resolved.

@emily-shen
emily-shen requested review from petebacondarwin and removed request for emily-shen August 5, 2026 16:36
@workers-devprod

Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • ✅ @cloudflare/wrangler
Show detailed file reviewers

@workers-devprod workers-devprod 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.

Codeowners reviews satisfied

@github-project-automation github-project-automation Bot moved this from Untriaged to Approved in workers-sdk Aug 10, 2026
@pkg-pr-new

pkg-pr-new Bot commented Aug 10, 2026

Copy link
Copy Markdown
@cloudflare/autoconfig

npm i https://pkg.pr.new/@cloudflare/autoconfig@14999

@cloudflare/build-output-utils

npm i https://pkg.pr.new/@cloudflare/build-output-utils@14999

@cloudflare/config

npm i https://pkg.pr.new/@cloudflare/config@14999

create-cloudflare

npm i https://pkg.pr.new/create-cloudflare@14999

@cloudflare/deploy-helpers

npm i https://pkg.pr.new/@cloudflare/deploy-helpers@14999

@cloudflare/kv-asset-handler

npm i https://pkg.pr.new/@cloudflare/kv-asset-handler@14999

miniflare

npm i https://pkg.pr.new/miniflare@14999

@cloudflare/pages-functions

npm i https://pkg.pr.new/@cloudflare/pages-functions@14999

@cloudflare/pages-shared

npm i https://pkg.pr.new/@cloudflare/pages-shared@14999

@cloudflare/unenv-preset

npm i https://pkg.pr.new/@cloudflare/unenv-preset@14999

@cloudflare/vite-plugin

npm i https://pkg.pr.new/@cloudflare/vite-plugin@14999

@cloudflare/vitest-pool-workers

npm i https://pkg.pr.new/@cloudflare/vitest-pool-workers@14999

@cloudflare/workers-auth

npm i https://pkg.pr.new/@cloudflare/workers-auth@14999

@cloudflare/workers-editor-shared

npm i https://pkg.pr.new/@cloudflare/workers-editor-shared@14999

@cloudflare/workers-utils

npm i https://pkg.pr.new/@cloudflare/workers-utils@14999

wrangler

npm i https://pkg.pr.new/wrangler@14999

commit: a3c5e60

…verride

The case-insensitive Proxy used for .env loading on Windows tracked the
current casing for a key in a side Map, but its set/defineProperty/
deleteProperty traps never removed the old differently-cased property from
the underlying target object. get/has still resolved correctly through the
tracking map, but Object.keys/for...in/JSON.stringify/spread over the
resulting object (which is assigned directly to process.env) exposed both
the stale and current key.

Fix set/defineProperty to delete the previous casing's property before
writing the new one, and deleteProperty to remove the tracked casing
rather than whatever casing the caller happened to pass.
Per review feedback: changesets should describe what changed for users,
not implementation details like which Proxy traps were touched.
@petebacondarwin
petebacondarwin force-pushed the fix/case-insensitive-env-stale-keys branch from 0fc6313 to 3d5f0db Compare August 10, 2026 10:15
@petebacondarwin
petebacondarwin merged commit ba54f0d into cloudflare:main Aug 16, 2026
59 of 60 checks passed
@github-project-automation github-project-automation Bot moved this from Approved to Done in workers-sdk Aug 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants