# Shell quoting context mismatch in generated scripts

Generated shell scripts quoting values with the wrong shell's rules, or re-substituting inside already-quoted values

_Area: Software supply chain and CI/CD. Seen in 3 disclosed reports; one confirmed bounty: $500; latest 2026-10-21. Updated 2026-11-02._

## How it works
A generator emits shell code and quotes attacker-influenced values with a quoter that does not match the target shell, or substitutes into text after quoting has already run. The assumption that breaks: "a value passed through quote() is inert." That only holds if the quoter's escaping rules equal the consuming shell's rules, and if nothing rewrites the text afterward. Fish treats `\` inside single quotes as an escape, so a POSIX `shlex.quote` value containing backslash-before-quote ends the literal early and exposes following command substitution [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-c947-3pg5-gm8q.md). Separately, a per-key template loop that re-scans the whole text each pass can rewrite a placeholder name that appears inside an already-quoted, substituted value, breaking out of the quote [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-8rjx-v5ww-45pp.md).

## Where it shows up
- One generic quoter (shlex.quote, shell.escape) reused across scripts for different shells (fish, csh, xonsh, POSIX sh) [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-c947-3pg5-gm8q.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-8rjx-v5ww-45pp.md)
- Template engines that substitute key-by-key over the full text rather than in one pass [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-8rjx-v5ww-45pp.md)
- Interpolation points that skip the escaping helper: env var names, labels, keys, artifact paths [E2](https://bugbountylibrary.com/r/h1%3A2221404.md)
- Values sourced from paths, prompts, or user-supplied task definitions that reach a command line [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-c947-3pg5-gm8q.md)[E2](https://bugbountylibrary.com/r/h1%3A2221404.md)

## How to review
For each generated script, identify the consuming shell and confirm the quoter's rules match it exactly (test backslash, single quote, `$()`, newline). For templates, verify substitution is single-pass or that substituted values are never re-scanned. Enumerate every interpolation into a command line and confirm each one, including names and keys, passes through the escaper. Invariant: after generation, no attacker-controlled byte can terminate a quoted literal or start a new command.

## How finders spotted it
- Noticed FishActivator defined no `quote()` and inherited `shlex.quote`; reasoned fish's single-quote escaping differs from POSIX sh, then tested backslash-before-quote [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-c947-3pg5-gm8q.md)
- Noticed `shell.escape` applied to image, command, and artifact path but not env var name; confirmed the community instance let any user reach an example worker group [E2](https://bugbountylibrary.com/r/h1%3A2221404.md)
- Noticed the per-key substitution loop added with quoting rewrites the whole text each pass, so a value containing a later placeholder name gets re-expanded after quote() ran [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-8rjx-v5ww-45pp.md)

## Variants seen
- Wrong-shell quoter inherited by a shell-specific activator [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-c947-3pg5-gm8q.md)
- Missing escape on one interpolation class (env var names) [E2](https://bugbountylibrary.com/r/h1%3A2221404.md)
- Multi-pass template substitution re-expanding inside quoted values [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-8rjx-v5ww-45pp.md)

## Impact and severity
Command execution on the host that sources the generated script or runs the task: developer machines for activation scripts [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-c947-3pg5-gm8q.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-8rjx-v5ww-45pp.md), worker hosts for task definitions [E2](https://bugbountylibrary.com/r/h1%3A2221404.md). Severity is raised when any user can supply the value (community worker groups [E2](https://bugbountylibrary.com/r/h1%3A2221404.md)) and when the script is sourced automatically on activation [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-c947-3pg5-gm8q.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-8rjx-v5ww-45pp.md).

## Seen in real software
Open-source packages and projects, and products, where this pattern was found. If the target runs one, compare the version it runs with the fix.
- **virtualenv (pip)** <= 21.14.1, fixed in 21.14.2: virtualenv activate.fish runs commands from a path or prompt with a backslash before a quote (CVE-2026-113732). Report: https://bugbountylibrary.com/r/ghsa%3AGHSA-c947-3pg5-gm8q.md. Fix: https://github.com/pypa/virtualenv/commit/3bddeaf8d02c3ee7f576503efb082f8f494cfcab
- **virtualenv (pip)** >= 20.26.6, <= 21.14.1, fixed in 21.14.2: virtualenv activation scripts run code from a path that contains a placeholder name (CVE-2026-113776). Report: https://bugbountylibrary.com/r/ghsa%3AGHSA-8rjx-v5ww-45pp.md. Fix: https://github.com/pypa/virtualenv/commit/602b26623a724827ff171bf11fef4ad186d01f74

## Evidence
- E1: https://bugbountylibrary.com/r/ghsa%3AGHSA-c947-3pg5-gm8q.md
- E2: https://bugbountylibrary.com/r/h1%3A2221404.md
- E3: https://bugbountylibrary.com/r/ghsa%3AGHSA-8rjx-v5ww-45pp.md
