Skip to main content

What the check does

Flags a run: 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_ENV where TITLE: ${{ 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 PATH so a later make/npm/git invocation runs their binary.
Crucially, this is not fixed by the usual shell-injection mitigation. Moving the value into an 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-scoped env: and consume it only in the step that needs it, after validating/allowlisting: