I reviewed our GitHub Actions workflows for exposure to the attack chain described in https://adnanthekhan.com/posts/clinejection/ (LLM prompt injection → agent executes commands in CI → GitHub Actions cache poisoning → pivot into a more-privileged workflow to steal publish secrets).
What I reviewed
- Enumerated all 23 workflows in
.github/workflows/.
- Flagged workflows using higher-risk triggers:
issues, pull_request_target, workflow_run.
- Looked for:
- LLM/agent execution (OpenHands / Claude / LLM usage)
- secret exposure (
secrets.*)
- checkout of untrusted refs (PR head SHA/ref in
pull_request_target)
- cache usage (explicit
setup-uv enable-cache: true, or other caching in privileged contexts)
- overly-broad
permissions: (e.g., id-token: write, actions: write) in jobs that could run untrusted code
Key findings
High risk
pr-review-by-openhands.yml + .github/actions/pr-review composite action
- Triggered via
pull_request_target on opened/ready_for_review/labeled/review_requested.
- The composite action checked out:
- the SDK tooling at
sdk-version: ${{ github.event.pull_request.head.sha }} (i.e., untrusted PR code), and
- the PR repo
head.ref (untrusted),
then ran the PR review agent with secrets available.
- This is a direct untrusted-code-execution path in a secrets-bearing
pull_request_target workflow.
- It also enabled
setup-uv GitHub Actions caching, which can contribute to cross-workflow cache poisoning/pivot patterns.
Mitigation: opened PR #2119 to harden this (details below).
Medium risk / defense-in-depth
auto-label-issues.yml
- Runs on
issues: opened (public trigger) and used a long-lived PAT secret to add a label.
- The workflow does not need a PAT; the ephemeral
GITHUB_TOKEN with issues: write is sufficient.
Mitigation: included in PR #2119.
integration-runner.yml
- Uses
pull_request_target and checks out PR head SHA for label-triggered runs (expected for integration tests).
setup-matrix previously ran on any label event (even non-test labels) and imported python code from the checked-out PR.
run-integration-tests granted id-token: write (OIDC) despite no usage.
Mitigation: included in PR #2119 (gate setup-matrix to relevant triggers + remove id-token: write).
condenser-runner.yml
- Similar label-triggered
pull_request_target pattern. Also granted id-token: write without usage.
Mitigation: included in PR #2119.
PR with fixes
Summary of changes:
- PR review now runs only on maintainer-applied
review-this label.
- Composite PR review action no longer checks out PR head code (uses trusted base commit + GitHub API diffs).
- Disabled
setup-uv Actions cache for the PR review action.
- Switched issue auto-label to
GITHUB_TOKEN.
- Removed unused
id-token: write from label-triggered pull_request_target jobs.
Recommended follow-ups (not included)
- Consider additional guardrails for any CI LLM/agent runs:
- run agents with a minimal tool allowlist (avoid unrestricted bash/network where possible)
- use fine-grained tokens with least privilege for commenting/reviewing
- consider GitHub environments + required reviewers for workflows that must use highly-privileged secrets
- avoid using shared caches in any workflow that has publish credentials, or salt cache keys with a secret only available to publish workflows
I reviewed our GitHub Actions workflows for exposure to the attack chain described in https://adnanthekhan.com/posts/clinejection/ (LLM prompt injection → agent executes commands in CI → GitHub Actions cache poisoning → pivot into a more-privileged workflow to steal publish secrets).
What I reviewed
.github/workflows/.issues,pull_request_target,workflow_run.secrets.*)pull_request_target)setup-uv enable-cache: true, or other caching in privileged contexts)permissions:(e.g.,id-token: write,actions: write) in jobs that could run untrusted codeKey findings
High risk
pr-review-by-openhands.yml+.github/actions/pr-reviewcomposite actionpull_request_targetonopened/ready_for_review/labeled/review_requested.sdk-version: ${{ github.event.pull_request.head.sha }}(i.e., untrusted PR code), andhead.ref(untrusted),then ran the PR review agent with secrets available.
pull_request_targetworkflow.setup-uvGitHub Actions caching, which can contribute to cross-workflow cache poisoning/pivot patterns.Mitigation: opened PR #2119 to harden this (details below).
Medium risk / defense-in-depth
auto-label-issues.ymlissues: opened(public trigger) and used a long-lived PAT secret to add a label.GITHUB_TOKENwithissues: writeis sufficient.Mitigation: included in PR #2119.
integration-runner.ymlpull_request_targetand checks out PR head SHA for label-triggered runs (expected for integration tests).setup-matrixpreviously ran on any label event (even non-test labels) and imported python code from the checked-out PR.run-integration-testsgrantedid-token: write(OIDC) despite no usage.Mitigation: included in PR #2119 (gate
setup-matrixto relevant triggers + removeid-token: write).condenser-runner.ymlpull_request_targetpattern. Also grantedid-token: writewithout usage.Mitigation: included in PR #2119.
PR with fixes
Summary of changes:
review-thislabel.setup-uvActions cache for the PR review action.GITHUB_TOKEN.id-token: writefrom label-triggeredpull_request_targetjobs.Recommended follow-ups (not included)