fix(security): make same-repo gate on claude-auto-fix-ci.yml explicit#1046
fix(security): make same-repo gate on claude-auto-fix-ci.yml explicit#1046aaronjmars wants to merge 1 commit into
Conversation
workflow_run + checkout of github.event.workflow_run.head_branch + bun install runs in a base-repo context with access to repository secrets (CLAUDE_CODE_OAUTH_TOKEN, SUPERMEMORY_API_KEY, write-scoped GITHUB_TOKEN) — the canonical "pwn request" pattern. Today the implicit pull_requests[0] gate happens to skip fork PRs (documented but non-contractual GitHub behavior), but the security boundary should be visible in the workflow file. Add an explicit head_repository.full_name == repository.full_name check plus an inline comment so the gate isn't accidentally removed by a future editor. Detected by Aeon + Semgrep p/security-audit (yaml.github-actions.security.workflow-run-target-code-checkout). Severity: medium · CWE-913
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub. |
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
Summary
.github/workflows/claude-auto-fix-ci.ymlruns onworkflow_runafter the public-input CI workflow completes, then checks out PR code (ref: github.event.workflow_run.head_branch) and runsbun install+ Claude withBash(*)in a context that has access to repository secrets (CLAUDE_CODE_OAUTH_TOKEN,SUPERMEMORY_API_KEY, write-scopedGITHUB_TOKEN). This is the canonical "pwn request" pattern documented by GitHub Security Lab — checking out untrusted code in a privileged workflow.The existing gate
github.event.workflow_run.pull_requests[0]happens to be empty for fork PRs today (documented GitHub behavior), but the security boundary is implicit and brittle to future GitHub behavior changes. This PR makes the boundary explicit.Impact
In the current implementation, with a
workflow_runtrigger plus untrusted-PR checkout plusbun install, a PR whose package manifest carries apostinstallscript would execute that script in the privileged workflow context. Today the implicit guard prevents that for fork PRs; the addition here makes the same guarantee explicit and surface-visible so the security intent doesn't depend on a documented-but-non-contractual GitHub behavior.Residual risk (out of scope for this PR): the workflow still trusts collaborators who can push branches to this repository. That trust boundary is intentional — auto-fix has to be able to read and modify PR code by design — but is worth documenting separately in a SECURITY.md or contributor guide.
Location
.github/workflows/claude-auto-fix-ci.yml:18-31Fix
Adds an explicit head-repository identity check to the job-level
if:condition:github.event.workflow_run.head_repository.full_name == github.event.workflow_run.repository.full_nameThis means the job only runs when the failing CI run originated from a branch in this repository, not a fork. Same behavior as today via the implicit
pull_requests[0]guard, but encoded as a first-class gate that any future workflow editor will see and not accidentally remove.A multi-line YAML comment above the condition documents the threat model and links to the GitHub Security Lab article on pwn-request mitigation, so the next person to touch this file understands why the gate exists.
Detected by
Aeon + Semgrep
p/security-audit(ruleyaml.github-actions.security.workflow-run-target-code-checkout).pull_requests[0]gate covers the most exploitable fork-PR case today, but the explicit gate removes the reliance on undocumented behavior)Verification
python3 -c "import yaml; yaml.safe_load(open('claude-auto-fix-ci.yml'))"parses cleanly. Theif:condition still triggers on the intended same-repo failing-PR case (verified by re-reading the parsed condition in the YAML AST). All steps, permissions, and secrets references unchanged.Filed by Aeon.