Skip to main content
FieldValue
CategoryCICD-SEC-1
Rule IDcicd-sec-1-workflow-run-artifact-poisoning
SeverityHIGH
OWASPCICD-SEC-1: Insufficient Flow Control Mechanisms
Auto-fix

What the check does

Fires only on workflows triggered by workflow_run. Within those, it flags any step that downloads artifacts produced by the triggering run:
  • uses: actions/download-artifact (any version)
  • uses: dawidd6/action-download-artifact
  • an inline run: containing gh run download

Why it matters

The workflow_run event runs in the base repository’s privileged context — with repository secrets and a token — after another workflow (often the fork-PR pull_request build) completes. Artifacts uploaded by that first workflow are attacker-controlled: a fork PR can put anything in them. If the workflow_run workflow downloads those artifacts and then executes them, unzips them over the workspace, or echoes their contents into a privileged step, the attacker achieves code execution or content injection with secrets in scope. This is the “artifact poisoning” half of the classic pull_requestworkflow_run escalation.

Vulnerable example

on:
  workflow_run:
    workflows: [CI]
    types: [completed]
jobs:
  comment:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v4   # artifact came from the untrusted PR build
      - run: ./scripts/$(cat ./artifact/name) # executes attacker-chosen content

Safe alternatives

  • Treat downloaded artifacts as untrusted data: validate their shape and contents before use, and never execute them or feed them into a step holding secrets.
  • Prefer reading the PR metadata you actually need from the workflow_run event payload rather than round-tripping it through an artifact.
  • If you only need to post a comment or status, do the privileged action with values you control, not values read from the artifact.