| Field | Value |
|---|---|
| Category | CICD-SEC-4 |
| Severity | HIGH |
| OWASP | CICD-SEC-4: Poisoned Pipeline Execution |
| Auto-fix | ✗ |
What the check does
Flags arun: step that writes to $GITHUB_ENV or $GITHUB_PATH with a value
derived from untrusted event data — either:
- directly,
echo "X=${{ github.event.issue.title }}" >> $GITHUB_ENV, or - laundered through an
env:variable that was assigned untrusted data,echo "X=$TITLE" >> $GITHUB_ENVwhereTITLE: ${{ github.event.pull_request.title }}.
Why it matters
$GITHUB_ENV and $GITHUB_PATH set the environment and PATH for every
subsequent step in the job. An attacker who controls the written value can:
- inject loader/runtime variables (
LD_PRELOAD,NODE_OPTIONS,BASH_ENV, …) that execute code in later steps, or - prepend a directory to
PATHso a latermake/npm/gitinvocation runs their binary.
env: variable and using $TITLE protects the current
shell, but the bytes still land in the environment file — so the later-step
poisoning remains. That’s why this is its own rule, distinct from
CICD-SEC-4 shell injection.
Vulnerable example
Safe alternative
Don’t persist untrusted input into the environment files. Keep it in a step-scopedenv: and consume it only in the step that needs it, after
validating/allowlisting: