Skip to content

Back to the catalog

The complete GitHub Actions security checklist
aikido.devarticle

The complete GitHub Actions security checklist

A detailed 33-point checklist for hardening GitHub Actions workflows, covering trigger risks (pull_request_target, workflow_run), script/prompt injection into run: steps and AI agent prompts, mutable action/package references, artifact handling, secrets scoping, OIDC over static cloud credentials, runner isolation, and token/org-level permission defaults. Each section is backed by real-world incidents (Trivy tag hijack, tj-actions/changed-files compromise, Ultralytics PyPI attack, Nx/s1ngularity, Shai-Hulud, TanStack npm trusted-publisher abuse, PyTorch self-hosted runner takeover), making it a practical reference for securing CI/CD pipelines against the supply-chain attacks that have hit open source in 2025–2026.

TL;DR A 33-item, incident-backed checklist for locking down GitHub Actions against pwn requests, script injection, mutable action/package references, and secrets/runner misconfigurations.
Document

What it is

Aikido's GitHub Actions security checklist, focused on the trigger-configuration attack surface that has driven recent CI/CD supply-chain incidents. The fetched text covers the "start here" top-five priorities plus two detailed sections: pull_request_target (pwn requests) and workflow_run chaining. It cites the March 2026 Trivy attack and a new actions/checkout v7 default behavior (effective June 18, 2026).

The five priorities

If you do nothing else:

  • Pin all third-party actions to a full commit SHA
  • Set default GITHUB_TOKEN permissions to read-only
  • Never use pull_request_target in public repos
  • Never interpolate ${{ github.* }} directly into run: steps
  • Use OIDC for cloud credentials instead of long-lived secrets

How the trigger attacks work

pull_request_target runs in the base repository's context regardless of PR origin, loading referenced secrets into the runner. The trigger is safe by default (it runs base-branch code, not fork code); the danger is only when you explicitly check out the fork's code, which then executes with secret access — a "pwn request." Any external contributor can open a PR to trigger it, and injected code exfiltrates secrets via e.g. os.environ.get('MY_SECRET') with no merge and no trace.

workflow_run reaches the same place via an extra hop: the downstream workflow runs with write permissions and secret access regardless of what triggered the upstream one. If an upstream pull_request workflow is vulnerable to script injection, an attacker poisons the output artifact via a PR, and the privileged downstream workflow consumes that attacker-controlled content.

What to do

  • pull_request_target: avoid in public repos. In private repos, require maintainer approval for first-time contributors under Settings > Actions > Fork pull request workflows.
  • workflow_run: prefer triggering directly (e.g. push on main) rather than chaining. If you must chain, gate privileged jobs on the upstream trigger type:
jobs:
  deploy:
    if: github.event.workflow_run.event == 'push'
  • actions/checkout v7 (as of 2026-06-18) blocks common pwn-request checkouts in both pull_request_target and workflow_run by default, refusing fork PR code unless you set allow-unsafe-pr-checkout: true. Upgrading is a concrete mitigation; do not set that flag.

Caveats

The page states best practices won't make workflows bulletproof — a compromised maintainer or a GitHub-infrastructure zero-day remains out of scope; these steps close the vectors attackers are actively exploiting. Note the pull_request_target default state is safe; the risk is specifically adding a fork checkout, so blanket "never use it" advice is stricter than the mechanism strictly requires. The In-the-wild claim: after the Trivy compromise, an attacker scanned GitHub for pull_request_target-enabled repos and opened several hundred PRs in roughly a day.

Organisations named here

  • Shai-Hulud

CVEs named here

  • CVE-2025-30066

Technologies named here

  • Aikido
  • GitHub Actions
  • actions/checkout
  • Aikido Safe Chain
  • Dependabot
  • tj-actions/changed-files
  • reviewdog/action-setup
  • Renovate
  • aquasecurity/trivy-action
  • pinact
  • Harden-Runner
  • npm
  • bullfrog
  • PyPI
  • TanStack/router
  • Actions Runner Controller
  • PyTorch
  • OpenSSF Scorecard
  • CODEOWNERS
  • Ultralytics
  • Nx
  • GitHub Environments
  • OIDC
  • Trivy
  • Aikido Security

Techniques named here

  • pull_request_target abuse / pwn request
  • workflow_run privilege chaining
  • Script injection via github.* context values
  • Prompt injection against AI agents in workflows
  • LLM output injection
  • GITHUB_ENV / GITHUB_PATH injection
  • Artifact overwrite via extraction into workspace
  • Secret leakage via broad artifact upload
  • Mutable action/tag repointing
  • Transitive mutable dependency compromise
  • Floating package version compromise
  • Secret exposure via command-line arguments
  • Secret masking bypass in logs
  • Cache poisoning via untrusted PR-influenced cache writes
  • Self-hosted runner code execution via public PR
  • Persistent runner backdoor via stolen token
  • npm trusted publisher token minting via orphaned commit

Threat actors named here

  • Shai-Hulud
  • tj-actions compromise
  • Trivy attack
  • aquasecurity/trivy-action tag repointing attack
  • Ultralytics attack
  • Nx / s1ngularity attack
  • PromptPwnd
  • prt-scan
  • Mini Shai-Hulud TanStack attack
  • PyTorch self-hosted runner attack
  • Mini Shai-Hulud