| name | ci-agent-hardening |
| description | Audit and harden GitHub Actions workflows against prompt injection, pull_request_target exploits (Pwn Requests), expression injection, cache poisoning, credential theft, and supply chain attacks. Based on Clinejection and hackerbot-claw campaigns. Use when reviewing CI/CD security, securing AI agent workflows, hardening publishing pipelines, or checking for GitHub Actions misconfigurations. Also covers slash command authorization, CLAUDE.md protection, and network egress. NOT for general CI/CD optimization or non-security workflow issues. |
| version | 0.1.0 |
| license | Apache-2.0 |
| metadata | {"author":"Stacklok","homepage":"https://github.com/stacklok/toolhive-catalog"} |
CI Agent Hardening
Audit and fix security vulnerabilities in GitHub Actions workflows, with emphasis on AI-powered CI/CD. Based on two major 2026 incidents:
- Clinejection — Prompt injection in AI triage bot led to cache poisoning, credential theft, malicious npm publish (~4,000 developers affected)
- hackerbot-claw — Autonomous AI bot exploited 7 repos (Microsoft, DataDog, CNCF, Aqua Trivy), achieving full repo compromise on Trivy (25k+ stars) via PAT theft
Prerequisites
- Repository with
.github/workflows/ directory
bash available for running the audit script
Instructions
For background and exploit details on any pattern below, see references/ATTACK-PATTERNS.md.
Step 1: Run the Automated Audit
Run from the repository root:
bash <skill-path>/scripts/audit-workflows.sh .github/workflows
Review [CRIT] findings first (exploitable now), then [WARN] (defense gaps).
Step 2: Fix pull_request_target + Fork Checkout (CRITICAL)
This is the #1 attack vector. 4 of 7 hackerbot-claw attacks exploited it. Check every pull_request_target workflow:
on: pull_request_target
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
on: pull_request
on: pull_request_target
steps:
- uses: actions/checkout@v4
If the workflow needs both secrets AND fork code (e.g., comment on PR), split into two workflows: one that runs untrusted code (no secrets) and one that consumes artifacts (with secrets).
Step 3: Eliminate Expression Injection in run: Blocks
Any ${{ }} expression referencing user-controlled values inside a run: block is a shell injection. Branch names, filenames, PR titles, commit messages are all vectors.
- run: echo "${{ github.event.pull_request.head.ref }}"
- run: echo "$PR_HEAD_REF"
env:
PR_HEAD_REF: ${{ github.event.pull_request.head.ref }}
Check ALL run: blocks for these user-controlled fields:
github.event.issue.title / .body
github.event.pull_request.title / .body / .head.ref
github.event.comment.body
github.event.discussion.title / .body
github.event.review.body
github.event.head_commit.message
Step 4: Secure Slash Commands
Any issue_comment-triggered workflow that runs code must check author_association:
if: contains(github.event.comment.body, '/deploy')
if: >-
contains(github.event.comment.body, '/deploy') &&
(github.event.comment.author_association == 'MEMBER' ||
github.event.comment.author_association == 'OWNER')
Step 5: Harden AI Agent Workflows
- Minimize tools — Triage/review bots need
Read at most. NEVER grant Bash to workflows triggered by external users.
- Remove wildcard users — Delete
allowed_non_write_users: "*". Scope to known collaborators.
- Restrict permissions — AI workflows should have
contents: read at most.
- Protect CLAUDE.md — Add to
CODEOWNERS requiring maintainer review. Validate against expected schema in CI.
- Never checkout fork code for AI review — use base branch checkout only.
Step 6: Set Least-Privilege Permissions
Every workflow must have an explicit permissions: block:
permissions:
contents: read
pull-requests: read
A quality check script does NOT need contents: write. A GITHUB_TOKEN with write access, if stolen, allows pushing commits and merging PRs.
Step 7: Harden Credential Management
- Use OIDC provenance —
npm publish --provenance, PyPI trusted publishers. Eliminates static tokens.
- Prefer
GITHUB_TOKEN over PATs — auto-scoped, expires after workflow.
- If PAT required — use fine-grained PATs with minimal scope, stored in GitHub Environments with protection rules.
- Separate nightly/production credentials — different tokens, scopes, and workflows.
- Verify rotation — test-publish a canary after rotating to confirm old token is revoked.
Step 8: Eliminate Cache Attack Surface
- Remove
actions/cache from release workflows — install dependencies fresh.
- If cache required — use workflow-specific key prefixes that cannot be written by other workflows.
- Monitor for anomalies — cache size >10GB, sudden miss rate spikes.
Step 9: Network Egress Monitoring
Every attack in the hackerbot-claw campaign depended on curl to hackmoltrepeat.com. Consider:
- StepSecurity Harden-Runner for network egress allowlisting
- Alert on outbound calls to unknown domains during CI
- Block
curl/wget to non-allowlisted domains
Step 10: Disclosure Readiness
SECURITY.md with contact info and SLA (Cline ignored reports for 40 days).
- Monitor security inbox actively.
- Credential rotation runbook — tested before you need it.
- Incident response plan covering repo takeover scenario (Trivy: repo renamed, releases deleted, malicious extension published).
Generating Fix PRs
Apply changes per-file, explain each change. Priority order:
- Remove
pull_request_target + fork checkout patterns (or split workflows)
- Move
${{ }} expressions to env: variables in all run: blocks
- Add
author_association checks to slash command workflows
- Add explicit
permissions: blocks with least privilege
- Reduce AI agent
allowed_tools to minimum
- Remove
allowed_non_write_users: "*"
- Add
--provenance to publish commands
- Remove
actions/cache from release workflows
- Add
CLAUDE.md to CODEOWNERS
Error Handling
| Issue | Cause | Solution |
|---|
| Script finds no workflows | Wrong path | Verify repo root, check .github/workflows/ |
| False positive on AI detection | Keyword match in comments | Review context manually |
pull_request_target needed for secrets | Workflow design requires it | Split into two workflows (see Step 2) |
| OIDC provenance fails | Registry not configured | Follow npm/PyPI OIDC setup docs |
See Also
- references/ATTACK-PATTERNS.md — Full details on Clinejection + hackerbot-claw: all 7 attacks, exploit code, IOCs, comprehensive hardening checklist