You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Harden CI + agent workflows for untrusted third-party PRs against prompt-injection and supply-chain attacks, while preserving contributor velocity through staged rollout.
Threat Model
Confused deputy in CI: privileged automation processes attacker-controlled PR inputs.
Privileged trigger misuse (pull_request_target/workflow_run) when untrusted code is checked out/executed.
Supply-chain compromise via mutable action tags/dependency ingestion.
Self-hosted runner persistence risk.
AI prompt-injection (direct and indirect) with excessive agent permissions.
Trust Tiers
Tier 0: untrusted scan (pull_request, no secrets, read-only token).
Tier 1: maintainer metadata ops (optional; currently out of scope unless a concrete automation need appears).
Tier 2: promoted privileged CI (maintainer-approved, isolated runner, short-lived creds).
Scoping decision: do not SHA-pin Tier 2 (pypa/*) or Tier 3 (actions/*, github/*). Trusting these orgs' account security in exchange for fast CVE patching is the better tradeoff for this team size. Historical attack pattern is on smaller third-party orgs (tj-actions, reviewdog), which is what we deprecate via Tier 1.
Provider safelist: configured .github/zizmor.ymlunpinned-uses rule with allowlist for actions/*, github/*, pypa/*. Anything outside must be SHA-pinned and explicitly listed.
Workflow-security gate threshold lowered from --min-severity high to medium so policy violations actually fail CI.
Bonus: closed 5 PR-B credential-persistence gaps (persist-credentials: false on LFS / fetch-depth checkouts). Bonus: fixed a silent fail-closed BLOCKER in the changes job (force-pushed branch with orphaned event.before, or rebased PR base — were silently skipping all gated tests). Both surfaced via the multi-wave /review skill on the PR; see plans/1130-pr-g-7a/ audit trail.
GitHub UI: Settings → Actions → General → Allow specified actions and reusable workflows
Active allowlist: "Allow actions created by GitHub" (covers actions/* and github/*) + explicit github/*, pypa/*. Stale dorny/paths-filter@v3 and styfle/cancel-workflow-action@0.11.0 entries removed (deprecated by 7a).
"Require actions to be pinned to a full-length commit SHA" deliberately NOT enabled — would force SHA pinning of trusted orgs, contrary to the scoping decision above.
Layered defense now in place with 7a: repo setting blocks at runner level; zizmor unpinned-uses catches in PRs before merge.
7c) Agent-credential exposure audit — descoped 2026-04-27. Different threat surface from CI hardening (local agent runtimes ≠ untrusted-PR-in-CI). Without a concrete trigger (new agent integration, incident, compliance ask), the deliverable would be a write-for-writing's-sake doc nobody references. Revisit only when one of those triggers appears.
Descoped (from prior PR-G)
The original PR-G bundled three items that did not pass a "what attack does this stop?" test. Removed from active scope:
Incident response playbook — useful only if tied to tabletop practice; for a small team the runbook is not what you reach for during a real incident. Revisit if/when a compliance driver appears.
Measurable CI security metrics — only valuable when a non-zero signal triggers an action; without an owner and review cadence it is a dashboard for show.
Reproducibility targets for critical artifacts — large engineering cost (deterministic builds across Python versions, sdist contents, timestamps) for marginal additional protection over the trusted-publisher attestation chain delivered by PR-F3. Revisit only if the threat model shifts (e.g., distrust of GitHub-hosted publish identity).
Agent-credential exposure audit (originally PR-G 7c) — different threat surface from CI hardening (local agent runtimes ≠ untrusted-PR-in-CI). Without a concrete trigger (new agent integration, incident, compliance driver), the deliverable would be a write-for-writing's-sake doc nobody references. Revisit only when one of those triggers appears.
Pressure-Tested Rollout Order
PR-B first (fastest code-level blast-radius reduction).
PR-A in parallel (policy-level controls, may require admin coordination).
PR-D early (highest practical risk area: self-hosted/GPU path).
Completed: PR-G 7c descoped 2026-04-27 — agent-credential audit doesn't pass the "what attack does this stop, with this team's resources" test without a concrete trigger.
All in-scope work has landed. PR-C (re-enable GPU lane) remains deferred pending a GPU strategy. The umbrella can close, or stay open as a maintenance bookmark for the deferred item.
Acceptance Criteria
Untrusted PR code runs only in least-privilege contexts with no secrets.
Any workflow with write/secrets access does not execute untrusted PR code directly.
Privileged CI requires explicit maintainer promotion/approval.
Self-hosted/GPU usage is gated and isolated.
Action usage, token scopes, and exceptions are auditable.
Goal
Harden CI + agent workflows for untrusted third-party PRs against prompt-injection and supply-chain attacks, while preserving contributor velocity through staged rollout.
Threat Model
pull_request_target/workflow_run) when untrusted code is checked out/executed.Trust Tiers
pull_request, no secrets, read-only token).Child Issues
1) PR-B: Explicit workflow/job token permissions + credential handling
permissions:in CI workflowspersist-credentials: falseon untrusted checkouts2) PR-A: Repo/org Actions policy hardening (parallel)
GITHUB_TOKENread-only, disable Actions PR approval, require approval for all external contributorspublish-pypi.ymlexplicitcontents: read+id-token: write3) PR-D: GPU/self-hosted runner isolation + promotion gate (temporary lockdown)
ci-gpu.ymlnow fails fast and keepsgpu_publicexecution disabled pending re-enable criteria4) PR-C: Re-enable promoted GPU lane in-place (no workflow split)
ci-gpu.yml; do not add separatemaintainer-ops/promotedworkflow files unless needed5) PR-E: Actions/dependency supply-chain hardening (scheduled scope)
workflow-security.yml) withactionlint+zizmoractionlintnow selects latest stable release older than 6 days;zizmorusesPIP_EXCLUDE_NEWER: 6 daysactionlint,zizmor, optionally Scorecard)6) PR-F: Trusted release hardening
publish-pypi.ymlwith environment approval + trusted-ref verificationpypi.orglinks)7) PR-G: Provider safelist (agent-credential audit descoped)
pypa/*) or Tier 3 (actions/*,github/*). Trusting these orgs' account security in exchange for fast CVE patching is the better tradeoff for this team size. Historical attack pattern is on smaller third-party orgs (tj-actions, reviewdog), which is what we deprecate via Tier 1.styfle/cancel-workflow-action→ nativeconcurrency:block;dorny/paths-filter→ inlinegit diffshell job.github/zizmor.ymlunpinned-usesrule with allowlist foractions/*,github/*,pypa/*. Anything outside must be SHA-pinned and explicitly listed.--min-severity hightomediumso policy violations actually fail CI.persist-credentials: falseon LFS / fetch-depth checkouts). Bonus: fixed a silent fail-closed BLOCKER in thechangesjob (force-pushed branch with orphanedevent.before, or rebased PR base — were silently skipping all gated tests). Both surfaced via the multi-wave/reviewskill on the PR; seeplans/1130-pr-g-7a/audit trail.Settings → Actions → General → Allow specified actions and reusable workflowsactions/*andgithub/*) + explicitgithub/*,pypa/*. Staledorny/paths-filter@v3andstyfle/cancel-workflow-action@0.11.0entries removed (deprecated by 7a).unpinned-usescatches in PRs before merge.7c) Agent-credential exposure audit— descoped 2026-04-27. Different threat surface from CI hardening (local agent runtimes ≠ untrusted-PR-in-CI). Without a concrete trigger (new agent integration, incident, compliance ask), the deliverable would be a write-for-writing's-sake doc nobody references. Revisit only when one of those triggers appears.Descoped (from prior PR-G)
The original PR-G bundled three items that did not pass a "what attack does this stop?" test. Removed from active scope:
Pressure-Tested Rollout Order
Go/No-Go Gates
ci.ymlandci-gpu.yml, no unsafe credential persistence.Milestone Checkpoints
#1150,#1157).#1163,#1184,#1206) — trusted release hardening done.unpinned-usessafelist live at medium gate, 5 PR-B credential gaps closed, silent fail-closed BLOCKER inchangesjob fixed.All in-scope work has landed. PR-C (re-enable GPU lane) remains deferred pending a GPU strategy. The umbrella can close, or stay open as a maintenance bookmark for the deferred item.
Acceptance Criteria
Current Files In Scope
.github/workflows/ci.yml.github/workflows/ci-gpu.yml.github/workflows/codeql-analysis.yml.github/workflows/publish-pypi.yml.github/workflows/workflow-security.yml.github/actionlint.yaml.github/zizmor.yml