Skip to content

fix: Windows env-var deny/mask matching must fold name case - #477

Open
ig-ant wants to merge 2 commits into
mainfrom
ig/windows-env-deny-case-fold
Open

fix: Windows env-var deny/mask matching must fold name case#477
ig-ant wants to merge 2 commits into
mainfrom
ig/windows-env-deny-case-fold

Conversation

@ig-ant

@ig-ant ig-ant commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Windows env names are case-insensitive; credential env matching compared byte-for-byte, so GiThUb_ToKeN escaped a mask of GITHUB_TOKEN
  • New helpers: windowsEnvNameKey (ASCII-ordinal fold, same as srt-win's to_ascii_uppercase), envNameComparisonKey, readEnvCaseAware (exact key wins, then first case-insensitive match — deterministic), findByEnvName (one matching rule shared by config validation and runtime)
  • Folded on win32 only: mask lookups, AWS pair linking (specs, sentinels, real values), awsPairs config validation; POSIX stays byte-exact (pinned by tests)
  • Config validation now rejects fold-equal duplicate credentials.envVars names on win32 (two case-variant mask entries would mint conflicting sentinels and silently break SigV4 re-signing); AWS pair registration reads the matched entry's own spelling so validation and runtime cannot diverge
  • Windows --env overlay dedupes case-variant spellings (later writer wins when values differ; same-value HTTP_PROXY/http_proxy twins kept for case-sensitive POSIX-ported tools) and filters ambient PATH/PATHEXT forwards against mode:'deny' names case-insensitively
  • mode:'deny' remains structurally enforced on Windows (fresh profile env); the overlay filter closes the case-variant hole for anything the overlay forwards

Test plan

  • win32-mocked: mixed-case host key vs mask entry; exact-spelling preference; case-variant overlay dedupe; pAtH deny drops PATH; mask beats deny; fold-duplicate config rejected (accepted as distinct on POSIX); fold-twin extract entry registers no AWS pair
  • POSIX pins: mixed-case spellings stay distinct (no mask, no pair, validation accepts)
  • typecheck, eslint, prettier clean; touched suites 200 pass / 0 fail

ig-ant added 2 commits August 14, 2026 20:52
Windows environment-variable names are case-insensitive, but credential
env-var matching compared names byte-for-byte, so a host spelling like
GiThUb_ToKeN escaped a mask of GITHUB_TOKEN and case-variant duplicates
could reach the sandboxed child's env block.

- mask lookups (buildMaskedEnvVars), AWS pair linking (registerAwsPairs),
  and awsPairs config validation now compare env names with an ordinal
  ASCII case fold on win32 (new windowsEnvNameKey / envNameComparisonKey /
  readEnvCaseAware helpers); POSIX comparisons stay exact, where names
  really are case-sensitive
- the Windows --env overlay dedupes case-variant spellings on the same
  fold srt-win's build_env_block uses: different values collapse to the
  later writer so the documented setEnvVars -> generated -> gitCfg
  precedence holds across spellings, while same-value case twins
  (HTTP_PROXY/http_proxy) are kept for case-sensitive POSIX-ported tools
- mode:'deny' names now also filter the overlay's ambient PATH/PATHEXT
  forwards case-insensitively (deny is otherwise structural on Windows:
  the child starts from a fresh profile env); masks and the sandbox's own
  plumbing still win over a deny, matching the POSIX env -u ordering
- regression tests: mixed-case host key vs mask entry (win32) with the
  exact-spelling preference, case-variant overlay dedupe and deny
  filtering, AWS pair case-fold linking and validation, and POSIX pins
  that mixed-case spellings stay distinct there
… rule

- config validation now rejects credentials.envVars entries whose names
  collide under the platform fold (case-variants on Windows, exact
  duplicates everywhere): duplicates mint conflicting mask values for
  one variable and silently break AWS SigV4 re-signing when the
  registry binds a sentinel the child never holds
- new shared findByEnvName helper is the one entry-matching rule used
  by both config validation and registerAwsPairs; the pair layer now
  matches the FIRST fold-equal entry and reads its sentinel under that
  entry's own spelling, so validation and runtime cannot diverge even
  on unvalidated input (a non-whole-value first match registers no
  pair instead of borrowing a fold-twin's sentinel)
- windowsEnvNameKey: same ASCII-only fold, expressed as toUpperCase
  inside the [a-z] replace instead of char-code arithmetic
- winsrt overlay tests now compare names with the production fold
- tests: fold-duplicate config rejected on win32 / accepted as distinct
  on POSIX, exact duplicates rejected everywhere, and a runtime pin
  that a fold-twin's sentinel is never picked up
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