> ## 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 — Untrusted input written to GITHUB_ENV/GITHUB_PATH

> Poisoning the GITHUB_ENV / GITHUB_PATH files injects env vars and PATH entries into later steps.

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

## 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](/rules/cicd-sec-4).

## Vulnerable example

```yaml theme={null}
on: pull_request_target
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - env:
          TITLE: ${{ github.event.pull_request.title }}
        run: echo "PR_TITLE=$TITLE" >> "$GITHUB_ENV"   # ← poisons later steps
```

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

```yaml theme={null}
      - env:
          TITLE: ${{ github.event.pull_request.title }}
        run: |
          # use "$TITLE" locally; do NOT write it to $GITHUB_ENV/$GITHUB_PATH
          echo "Title length: ${#TITLE}"
```
