# Unicode case-folding path confusion in FastCGI dispatch

Index or match computed on folded/lowercased path applied to original bytes, or Unicode fallback in ASCII loop

_Area: Web infrastructure and server-side requests. Seen in 3 disclosed reports; latest 2026-05-18. Updated 2026-10-30._

## How it works
Path dispatch assumes the string used to find a split point (`.php`) and the string later sliced are byte-identical in length and content. Two assumptions break: (1) Unicode case folding maps non-ASCII lookalikes onto ASCII `.php`, so a non-PHP file matches; (2) case folding can change UTF-8 byte length (e.g. U+023A expands), so an index computed on the lowercased copy no longer aligns with the original path. Either way the server routes a non-PHP file to the PHP interpreter.

## Where it shows up
- A fast ASCII byte loop with a fallback to a Unicode-aware search (`search.IgnoreCase`, `language.Und`) for non-ASCII input.
- A `match`/found flag set before an inner loop that `break`s on failure without clearing it.
- Index computed on `strings.ToLower(path)` (or any normalized copy) then applied to the original path in a later function.
- FastCGI/CGI `split_path` / `splitPos` / `buildEnv` resolving SCRIPT_NAME and PATH_INFO.

## How to review
Check the invariant: the byte offset or match result must be derived from, and applied to, the same string. If a lowercased/normalized copy is searched, either slice that same copy or recompute the index on the original. Confirm the fallback matcher's semantics are ASCII-only (no Unicode folding or compatibility decomposition). Walk the control flow at every `break`/early exit and confirm the match flag reflects the actual outcome. Test with non-ASCII inputs whose case mapping changes byte length.

## Variants seen
- Unicode IgnoreCase folding matches non-ASCII lookalikes to `.php`, plus stale match flag on failed fallback [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-m675-2p33-xv9g.md)[E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-3g8v-8r37-cgjm.md).
- Case-folding length expansion desynchronizes split index between lowercased and original path [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-5r3v-vc8m-m96g.md).

## Impact and severity
Execution of non-PHP files as PHP, leading to RCE in FrankenPHP [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-3g8v-8r37-cgjm.md); same class in Caddy's php_fastcgi [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-m675-2p33-xv9g.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-5r3v-vc8m-m96g.md). Severe because it turns file-upload or static content into code execution. Worse when the fallback silently widens matching beyond the documented ASCII-only guarantee.

## Why fixes fail
Fixes that only clear the stale flag miss the length-shift desync [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-5r3v-vc8m-m96g.md); fixes that only recompute the index miss the folding match [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-m675-2p33-xv9g.md)[E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-3g8v-8r37-cgjm.md). Both must be addressed together.

## Evidence
- E1: https://bugbountylibrary.com/r/ghsa%3AGHSA-m675-2p33-xv9g.md
- E2: https://bugbountylibrary.com/r/ghsa%3AGHSA-3g8v-8r37-cgjm.md
- E3: https://bugbountylibrary.com/r/ghsa%3AGHSA-5r3v-vc8m-m96g.md
