Summary
.github/workflows/_build.yml has multiple run: steps that interpolate ${{ github.* }} context data directly into shell scripts. This is the classic GitHub Actions shell injection pattern (CWE-78, OWASP A03).
Found via static scan (semgrep yaml.github-actions.security.run-shell-injection).
Findings
Lines with ${{ github.* }} context interpolation in run: steps:
.github/workflows/_build.yml:55
.github/workflows/_build.yml:68
.github/workflows/_build.yml:111
.github/workflows/_build.yml:123
.github/workflows/_build.yml:162
(Plus any I missed — these are the top 5 from the semgrep output.)
Why this matters
Fields like github.event.pull_request.title, github.event.issue.title, github.head_ref, and github.event.comment.body can be attacker-controlled via crafted PR titles, branch names, or comments. When they get interpolated directly into a run: script, the attacker's input becomes shell code.
Reference: https://securitylab.github.com/research/github-actions-untrusted-input/
Impact: code execution on the runner, which typically has access to GITHUB_TOKEN, release signing keys, PyPI publishing credentials, etc.
Remediation pattern
Replace direct interpolation with an intermediate env var:
# Vulnerable:
run: |
echo "Processing ${{ github.event.pull_request.title }}"
# Safe:
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
echo "Processing $PR_TITLE"
The env: block treats the value as a plain string; referencing $PR_TITLE in the script goes through normal shell variable expansion which is not vulnerable to injection.
Notes
Environment
- Scan date: 2026-04-13
- Tool: semgrep (default ruleset, rule:
yaml.github-actions.security.run-shell-injection)
- Repo state: HEAD of main as of scan time
Summary
.github/workflows/_build.ymlhas multiplerun:steps that interpolate${{ github.* }}context data directly into shell scripts. This is the classic GitHub Actions shell injection pattern (CWE-78, OWASP A03).Found via static scan (semgrep
yaml.github-actions.security.run-shell-injection).Findings
Lines with
${{ github.* }}context interpolation inrun:steps:.github/workflows/_build.yml:55.github/workflows/_build.yml:68.github/workflows/_build.yml:111.github/workflows/_build.yml:123.github/workflows/_build.yml:162(Plus any I missed — these are the top 5 from the semgrep output.)
Why this matters
Fields like
github.event.pull_request.title,github.event.issue.title,github.head_ref, andgithub.event.comment.bodycan be attacker-controlled via crafted PR titles, branch names, or comments. When they get interpolated directly into arun:script, the attacker's input becomes shell code.Reference: https://securitylab.github.com/research/github-actions-untrusted-input/
Impact: code execution on the runner, which typically has access to
GITHUB_TOKEN, release signing keys, PyPI publishing credentials, etc.Remediation pattern
Replace direct interpolation with an intermediate env var:
The
env:block treats the value as a plain string; referencing$PR_TITLEin the script goes through normal shell variable expansion which is not vulnerable to injection.Notes
.github/workflows/ci.yml. The current findings are in_build.ymlwhich appears to be a newer workflow file — same pattern, different file.run:step that referencesgithub.*context: https://docs.github.com/en/actions/learn-github-actions/security-hardening-for-github-actions#understanding-the-risk-of-script-injectionsEnvironment
yaml.github-actions.security.run-shell-injection)