GitHub Actions¶
Never Combine pull_request_target with PR Head Checkout — Critical¶
pull_request_target runs in the base repo context. When triggered by an external fork, untrusted code can potentially repo gain write permissions and access repo secrets.
on: pull_request_target
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
- run: npm install && npm test # Untrusted code executed in privileged context
# Can write and access secrets, etc.
# Unprivileged: runs untrusted code, no secrets
# Repo token is read-only
name: Receive PR
on: pull_request
steps:
- uses: actions/checkout@v4
- run: npm test
- uses: actions/upload-artifact@v4
with: { name: results, path: test-output/ }
# Privileged: consumes artifact, never touches untrusted PR code
# Repo token is read-write
name: Comment on PR
on:
workflow_run:
workflows: ["Receive PR"]
types: [completed]
permissions:
pull-requests: write
steps:
- name: "Download artifact"
uses: actions/download-artifact@v4
- name: "Do the thing..."
Ref: GitHub Security Lab — Preventing Pwn Requests (2021)
Pin All Actions to Full Commit SHAs — Critical¶
Branch and tag refs (@main, @v3) are mutable. A compromised upstream action is indistinguishable from the legitimate one at runtime.
Hash pinning does add some management overhead. Use Dependabot or Renovate to automate this process.
Ref: GitHub Docs — Security hardening: using third-party actions
Scope permissions: Minimally on Every Workflow — High¶
Without an explicit block, workflows inherit the repo's default token permissions. The TanStack OIDC token extraction worked because id-token: write was globally scoped on the release workflow.
permissions: {} # deny all at workflow level
jobs:
publish:
permissions:
id-token: write # only what this job needs
contents: read
Ref: GitHub Docs — Workflow syntax: permissions
Never Interpolate Untrusted Input into run: Steps — High¶
Issue bodies, PR titles, commit messages, and branch names are attacker-controlled. Direct interpolation into shell is command injection.
Applies to issue.body, head.ref, actor, and all other user-supplied context values.
Ref: GitHub Security Lab Part 2 — Untrusted Input
Use OIDC Trusted Publishing — Medium¶
OIDC eliminates long-lived static publishing tokens, but OIDC bindings still get abused if an attacker can execute code within the bound workflow job (as TanStack demonstrated). Layer with GitHub Environments and required reviewers:
Scope OIDC trust conditions as tightly as the registry permits: workflow file, ref, and environment.