TL;DR
GitHub Actions became the supply-chain attack vector of 2026, and the attackers stopped being human. Five disclosed incidents in twelve months — tj-actions/changed-files (CVE-2025-30066, 23,000+ repositories), Trivy (hackerbot-claw + TeamPCP, March 2026), Checkmarx, Bitwarden CLI v2026.4.0 (April 2026), and the SAP npm packages campaign (April 29, 2026) — share a single primitive: a misconfigured pull_request_target trigger that grants an untrusted PR access to a repository's secrets. The interesting question is no longer whether your CI is vulnerable — it's whether you'd notice exfiltration before the next time you log in.
What's Happening Across 2025-2026
Five disclosed campaigns in roughly twelve months, each escalating in scope:
| Date | Target | Scope | Mechanism |
|---|---|---|---|
| March 2025 | tj-actions/changed-files | 23,000+ repos | Malicious commit, base64 payload, credential dump to workflow logs |
| Feb–Mar 2026 | Trivy + Aqua Security | Multi-tenant CI/CD users worldwide | pull_request_target misconfiguration, PAT theft, /proc-based credential harvest |
| Mar 2026 | Checkmarx GitHub Actions | Enterprise security pipelines | Stolen CI credentials, downstream propagation |
| Apr 2026 | Bitwarden CLI v2026.4.0 | Millions of users, thousands of enterprises | Compromised release pipeline tied to the Checkmarx campaign |
| Apr 29, 2026 | SAP npm packages | SAP ecosystem (specific packages) | Preinstall hook + Bun runtime loader for credential stealer |
The pattern across all five: the attacker doesn't break GitHub. The attacker breaks the trust the workflow extends to a third-party action or a downstream package. The trust boundary is the supply chain — and CI/CD is where you find the most secrets in motion at any given moment.
Case 1: tj-actions/changed-files (CVE-2025-30066)
Between March 10 and March 14, 2025, an attacker pushed a malicious commit to the popular tj-actions/changed-files repository. The commit added a base64-encoded payload that, when the action ran, dumped every credential present in the CI runner's process memory into the workflow's public log output.
The catch: 23,000+ repositories used this action, often pinned by tag (@v44, @v45) rather than commit SHA. When the attacker tagged the malicious commit, every repo with a tag-pinned reference inherited the backdoor on its next CI run. Public repositories' workflow logs are world-readable. Anyone could iterate the GitHub Search index and harvest credentials at scale.
This is the playbook everyone copies. The tag-pinning instead of SHA-pinning antipattern is the load-bearing failure across all five 2025–2026 incidents.
Case 2: Trivy + hackerbot-claw (March 2026)
The Trivy compromise is the case that should keep CI/CD operators awake. Two separate threat actors hit the same project in three weeks.
Phase 1 (Feb 27 – Mar 2, 2026): An autonomous AI-driven attacker named hackerbot-claw exploited a pull_request_target misconfiguration in Trivy's GitHub Actions workflows. The misconfiguration allowed an external pull request to execute a workflow with the repository's secrets in scope. hackerbot-claw stole a Personal Access Token, achieved repository write access, and prepared the persistence groundwork.
Phase 2 (Mar 19, 2026): A separate group calling itself TeamPCP rode the access into a full supply-chain compromise. They pushed a malicious v0.69.4 tag at 17:43:37 UTC, hijacking the most-used release channel for Trivy. The malicious version harvested credentials from CI runner processes via:
/proc/<pid>/environ— environment variables for every process the runner spawned, including SSH keys, cloud credentials, Kubernetes secrets, Docker registry tokens, database credentials, and cryptocurrency wallets./proc/<pid>/mem— direct memory reads from CI processes for credentials never exposed via environment variables.
Exfiltration ran through a typosquat C2 (scan.aquasecurtiy[.]org — note the deliberate misspelling of "aquasecurity"), a Cloudflare tunnel (plug-tab-protective-relay.trycloudflare.com), and an attacker-controlled GitHub repo (tpcp-docs) as a fallback.
Safe versions: trivy v0.69.3, trivy-action v0.35.0, setup-trivy v0.2.6. Anything between those and the tag rollback is suspect.
IOCs (Trivy / TeamPCP)
- Domain:
scan.aquasecurtiy[.]org - IP:
45.148.10.212(Amsterdam) - Cloudflare tunnel:
plug-tab-protective-relay.trycloudflare.com - Fallback exfil repo:
tpcp-docs
Case 3: Checkmarx → Case 4: Bitwarden CLI (April 2026)
The Checkmarx GitHub Actions compromise was the quiet precursor. Stolen CI credentials, no immediate noise. Then Bitwarden CLI v2026.4.0 shipped through the same compromised pipeline in April. Bitwarden's enterprise audience — millions of users, thousands of companies — gives this incident the longest tail of the five. Anyone who installed bw CLI in early April should rotate every credential the CLI was used to retrieve.
This is the cascading-trust failure mode: a low-noise CI compromise at one vendor becomes a downstream credential compromise at every customer who consumes that vendor's binary releases.
Case 5: SAP npm Packages (April 29, 2026)
The newest, smallest in scope (specific SAP-related npm packages), but pattern-defining: the attacker added a preinstall hook to package.json that fetches and executes a JavaScript file (setup.mjs) using Bun as the runtime. Bun is fast, has minimal logging, and isn't yet on every blue team's radar — making it an attractive choice for credential-stealer execution. The payload propagates further by pushing identical preinstall hooks into dependencies the attacker has access to.
Lesson: your dependency lifecycle hooks are an executable surface. Most teams have telemetry on what runs in production but nothing on what runs at npm install time.
The Common Mechanism: pull_request_target Misconfiguration
If you remember one thing from this post, remember this: pull_request_target is the trigger that runs a workflow in the context of the base repository (the one being PR'd into) — meaning it has access to repository secrets — but with the code from the head repository (the PR submitter's fork). Used carelessly, it lets an external attacker submit a PR whose workflow has full access to your secrets.
The vulnerable pattern, which still ships in many template workflows:
# DANGEROUS: do NOT do this
on: pull_request_target
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }} # untrusted code
- run: npm install && npm test # runs in privileged context
The fix is to never check out untrusted PR code in a pull_request_target workflow. If you need to test PR code, use the standard pull_request trigger (which doesn't have secret access) and run privileged steps separately, only after a maintainer has reviewed.
Detection
1. Egress from GitHub Actions runners to suspect destinations
If your runners egress through a network you control (self-hosted runners, or GitHub-hosted with a corporate egress proxy), monitor for connections to:
- Cloudflare
*.trycloudflare.comtunnels (high false-positive rate, but worth flagging when paired with credential-shaped requests) - Domain typosquats of common SaaS providers (heuristic: Damerau-Levenshtein distance of 1 from
aquasecurity.com,github.com,npmjs.com, etc.) - Known IOCs from the Trivy incident (above)
- Newly-registered domains (less than 30 days) — most exfil C2 lifetimes are short
2. Workflow log auditing for credential-shaped output
The tj-actions playbook dumps credentials directly into workflow logs. A scheduled scan against your actions/runs API for log lines matching common credential formats catches this in flight:
# pseudo, run periodically against recent workflow logs
for run in gh api /repos/{owner}/{repo}/actions/runs --paginate; do
log=$(gh api /repos/{owner}/{repo}/actions/runs/$run/logs)
echo "$log" | grep -E \
'AKIA[0-9A-Z]{16}|ghp_[A-Za-z0-9]{36}|sk-[A-Za-z0-9]{48}|-----BEGIN.*PRIVATE KEY-----'
done
3. Runner-side: process-tree anomalies
For self-hosted runners, the Trivy attack signature is distinct: a CI process reading /proc/<other-pid>/environ or /proc/<other-pid>/mem for processes it didn't spawn. A Falco rule catches this:
- rule: GitHub Actions runner reads /proc memory of other processes
desc: A process running under the runner UID is reading /proc/[pid]/mem or /proc/[pid]/environ for processes it doesn't own
condition: >
open_read and proc.aname[1] = "Runner.Worker" and
fd.name pmatch ("/proc/*/mem", "/proc/*/environ") and
proc.pid != fd.dev # crude: alert when target pid != self
output: "GitHub Actions runner reading /proc memory of other process (proc=%proc.cmdline target=%fd.name)"
priority: CRITICAL
4. Dependency-graph diff alerts
Subscribe to GitHub's Dependency Graph API for any repository you ship from, and alert on tag changes that move backward (e.g. v0.69.4 rolled back to v0.69.3) — that's a vendor's signal that a release was compromised. The Trivy rollback would have surfaced within hours of TeamPCP's malicious tag push if anyone was watching.
Mitigation
- Pin every action by SHA, not by tag. The single highest-leverage change.
uses: tj-actions/changed-files@v45becomesuses: tj-actions/changed-files@4f70f0a3.... Renovate and Dependabot can both manage SHA-pinned upgrades. - Audit every
pull_request_targettrigger in every workflow file. If you must use it, never check out the PR's code in the same job that has secret access. The pattern:pull_request_targetfor the privileged step (e.g. labeling, commenting), separatepull_requesttrigger for the build step. - Rotate CI credentials on a schedule. Make every PAT, every cloud credential, every registry token short-lived enough that a six-week-old breach (the gap between Phase 1 and Phase 2 of the Trivy attack) can't be ridden.
- Restrict runner egress. Self-hosted runners should egress through an egress proxy that allows only known-good destinations. GitHub-hosted runners can be configured with allow-listed network rules in larger plans.
- Treat
preinstall/postinstallhooks as production code. Runnpm install --ignore-scriptsin CI by default. Re-enable per-package only when you've audited the script. - Monitor your release tags for unexpected publishes. If a tag appears in your repo's release stream that you didn't push, that's the alarm. The Trivy malicious
v0.69.4tag was the moment the attack went public-facing.
Frequently Asked Questions
Are GitHub-hosted runners or self-hosted runners more vulnerable?
Different vulnerabilities. GitHub-hosted runners are reset between runs, so persistence is harder, but you have zero egress visibility unless you build it in. Self-hosted runners give you visibility and audit logs, but they live longer — a malicious workflow that escapes the runner can persist on your network. Both classes need pull_request_target review and SHA pinning; only self-hosted gets the runner-side Falco rules.
Is SHA pinning enough by itself?
SHA pinning prevents the tj-actions class of attack (rolling a malicious commit under an existing tag). It does not prevent pull_request_target exploitation, supply-chain attacks against the actions you depend on (a SHA-pinned dependency can still be compromised at its own upstream source), or runtime credential harvest. SHA pinning is necessary, not sufficient.
Should I run Trivy at all after this?
Yes — pin to trivy v0.69.3 (the last known-clean release) or v0.69.5+ (the post-incident patched line, when verified). The Trivy team executed a textbook coordinated rollback. The longer-term lesson is structural: any vendor whose pipeline you depend on is your pipeline.
What's the realistic detection lag if I do nothing today?
The Trivy attack ran in two phases six weeks apart, and was publicly identified by Upwind's MDR team within roughly 24 hours of the malicious tag push. If you have no telemetry on your CI runners, you would have first noticed when GitHub's security team emailed you about a compromised secret — typically days to weeks after exfiltration. The detection rules above bring that lag down to hours.
Key Takeaways
- Five disclosed GitHub Actions supply-chain incidents in 2025-2026, with attackers progressing from human commit-pushers to autonomous AI agents.
- The common primitive is
pull_request_targetmisconfiguration. Audit every workflow that uses it. - SHA-pin every action. The tag-pin antipattern is the highest-impact thing to fix today.
- Runner-side Falco rules and workflow log scanning are concrete, deployable, low-cost detections.
- Cascading trust: a compromised CI pipeline at one vendor becomes a credential leak at every customer of that vendor's binaries.
Sable Offensive Research conducts authorized supply-chain assessments for engineering organizations: workflow audits, runner egress reviews, and tabletop exercises against the Trivy / tj-actions / Bitwarden playbooks. Contact us if your CI surface is large enough to warrant a focused review.
References
- Palo Alto Unit 42: tj-actions/changed-files threat assessment
- Upwind Security: Trivy supply chain incident breakdown (March 19-20, 2026)
- CISA Alert: Supply Chain Compromise of Third-Party tj-actions/changed-files (CVE-2025-30066)
- The Hacker News: TeamPCP / Checkmarx coverage
- Cybersecurity News: Bitwarden CLI compromise (April 2026)