Summary
The denylist regex in the setup-python-env composite action is case-sensitive, allowing an attacker with write access to a calling workflow to bypass it by using mixed-case flag names.
Details
- File:
.github/actions/setup-python-env/action.yml
- Line: 44
- Category: CI/CD and GitHub Actions Security (Category 14)
- Severity: Medium
Original Review Finding
Category: CI/CD and GitHub Actions Security (Category 14)
Severity: Medium
The denylist regex is case-sensitive. An attacker with write access to a calling workflow could bypass it by using mixed-case flag names — for example --Index-Url=(attacker.com/redacted) or --Extra-Index-Url=(evil.com/redacted). These would fail to match the denylist pattern but would pass the allowlist check (which permits uppercase letters via [a-zA-Z0-9=._:/@+-]+).
While uv may or may not accept these mixed-case flags depending on the version, the intent of the denylist is silently bypassed, leaving an unintended control gap.
Recommendation: Lowercase the token before the denylist comparison:
arg_lower="${arg,,}"
if [[ "$arg_lower" =~ ^--(index-url|extra-index-url|trusted-host|find-links)(=|$) ]]; then
echo "::error::Blocked dangerous extra-args token: '$arg'. Registry overrides are not permitted." >&2
exit 1
fi
Alternatively, use shopt -s nocasematch before the loop (and restore with shopt -u nocasematch after) to make all subsequent [[ =~ ]] comparisons case-insensitive.
Proposed Fix
Either:
- Lowercase the argument before comparing against the denylist:
arg_lower="${arg,,}" and match against $arg_lower
- Use
shopt -s nocasematch to make [[ =~ ]] comparisons case-insensitive
References
Generated by PR Review Comment — Create Issue for issue #51