Supply Chain Attacks

tj-actions Supply Chain Attack March 2025: A Postmortem

The tj-actions/changed-files compromise exposed CI secrets across thousands of public repositories. A postmortem on the attack chain and the GitHub Actions trust model.

Hritik Sharma
Staff Engineer
5 min read

The tj-actions/changed-files compromise in March 2025 was the most consequential GitHub Actions supply chain attack to date. It cascaded through reviewdog actions, exposed CI secrets from thousands of public repositories, and forced a broad reset on how organizations think about third-party Actions in their workflows. A year later, the postmortem still informs CI hardening conversations.

This post walks through the attack chain, the IOC details, and the structural problems with the Actions trust model that the incident exposed.

How did the attack chain unfold?

The attack chain began with the compromise of the reviewdog/action-setup repository, where an attacker obtained the ability to push to a tag. On March 11, 2025, the attacker modified the action to exfiltrate the GITHUB_TOKEN and other CI secrets to a Gist. Because reviewdog/action-setup was a dependency of tj-actions/eslint-changed-files, which shared a maintainer with tj-actions/changed-files, the attacker pivoted to compromise the tj-actions/changed-files repository between March 14 and March 15, 2025. They rewrote git history across many version tags so that the v1 through v45 tag references all pointed to a single malicious commit containing payload code that dumped runner memory to the workflow log. The malicious commit hash was 0e58ed8671d6b60d0890c21b07f8835ace038e67. CVE-2025-30066 was issued for the incident.

Because GitHub Actions logs from public repositories are world-readable, any public-repository CI run that invoked the compromised action during the window published its secrets to a publicly accessible log file. Researchers at StepSecurity confirmed the active exploitation pattern on March 14, 2025 and GitHub revoked the compromised tags and removed the malicious commits by March 15.

How wide was the blast radius?

The tj-actions/changed-files action was used in an estimated 23,000 public repositories at the time of the compromise, including high-profile projects maintained by Microsoft, AWS, Google, and a long tail of medium-sized open source projects. Confirmed exposure of secrets occurred in at least 218 public repositories where the affected workflow ran during the window and the workflow logs captured the dumped memory contents. The captured secrets included GitHub Personal Access Tokens, npm publish tokens, AWS access keys, GCP service account credentials, and Docker Hub credentials.

Private repository exposure was harder to assess because private workflow logs were not publicly readable, but security teams at affected organizations identified credential theft signals in subsequent weeks. Several major projects performed full credential rotation as a precaution, and at least three downstream package compromises in March and April 2025 were traced to credentials originally exfiltrated through the tj-actions chain.

Why did unpinned action references make this so much worse?

The structural amplifier was the GitHub Actions convention of referencing actions by tag name rather than by commit SHA. A workflow line reading uses: tj-actions/changed-files@v35 resolves to whatever commit the v35 tag currently points to, which is mutable by anyone with push access to the repository. When the attacker rewrote the tags to point at the malicious commit, every workflow referencing the action by tag pulled the malicious code on its next run. Workflows that pinned to the immutable commit SHA were not affected because their hash-pinned reference continued to resolve to the legitimate code.

GitHub published guidance recommending commit-SHA pinning in 2022, but adoption was modest. Post-tj-actions, several major open source projects mandated SHA pinning for all third-party actions, and a wave of tooling emerged to auto-convert tag references to SHA references during pull request review.

What did GitHub and the ecosystem change?

GitHub's response included rapid revocation of the compromised tags, removal of malicious commits from cache, and a tightening of the security advisory workflow for the Actions Marketplace. In the following months, GitHub introduced default workflow permission restrictions, requiring explicit opt-in for write access to GITHUB_TOKEN, and rolled out an artifact attestations feature that allows action authors to publish signed provenance for their releases. Adoption of these features was modest in the first six months but accelerated through late 2025 as the institutional memory of the incident remained fresh.

The wider community also produced several tools, including pinact and ratchet, that automatically convert action references to commit-SHA pins and verify the conversion in CI. These tools moved from niche to standard in security-mature engineering organizations during 2025.

What is the structural lesson about CI trust?

The structural lesson is that GitHub Actions are arbitrary code execution with full access to your CI secrets, and the trust model for installing them is closer to npm install than to a vetted enterprise integration. Every action your workflow references is a transitive trust relationship with that action's maintainer, who in many cases is an individual contributor with no security review process and no published incident response capability. The defenses are version-pin to commit SHAs, restrict GITHUB_TOKEN to the minimum scope needed, segregate sensitive workflows onto self-hosted runners with restricted egress, and continuously inventory third-party action usage across your repository fleet.

A program that does all four of these things would have been unaffected by the tj-actions incident. Most programs in March 2025 did one or two, which is why the incident produced the blast radius it did.

How Safeguard Helps

Safeguard inventories every GitHub Actions reference across your repository fleet and flags any reference that uses a mutable tag rather than a commit SHA, with one-click conversion suggestions and policy gates that block new tag-pinned references from being merged. Griffin AI tracks the behavioral baseline of every action your workflows invoke and alerts on suspicious changes, like a sudden modification across many version tags consistent with the tj-actions pattern. Reachability analysis identifies which of your repositories actually expose privileged secrets to third-party actions, so prioritization reflects exposure rather than total Action count. TPRM scoring assesses action maintainers on their security posture and incident history, surfacing the high-risk transitive trust relationships before they become next year's postmortem.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.