how it happened
- whoattackercompromised the action
- the gappinned versionspointed at malicious code
- the result23,000+ repositoriestokens and keys exposed
what everyone assumed
Popularity reads as a safety signal. A component used by tens of thousands of repositories has had thousands of eyes on it, so the risk has been found by now and you are not the one discovering it the hard way. Pinning to a version adds a second layer: even if the upstream changes, you stay on the code you reviewed. It is a careful belief, held by careful engineers.
what happened
On 14 and 15 March 2025, the action’s repository was compromised, and the attacker rewrote the existing version tags to point at a malicious commit. This is the part that retired the assumption: rewriting the tags meant that even pinned versions resolved to the new code. The injected script read secrets out of the runner process and printed them into the workflow logs. On repositories with public logs, those secrets were now sitting in plain view, written there by the build itself.
step by step
- teams pin the action to a version tagWorkflows reference tj-actions/changed-files by a version tag, trusting it to stay on reviewed code.
- 14-15 March 2025the action's repository is compromisedThe attacker gets write access to the upstream project.
- existing version tags are rewrittenThe old tags now point at a malicious commit, so pinned workflows pull the new code without any change on their side.the assumption breaks here
- the script prints secrets into build logsIt reads secrets out of the runner process and writes them into the workflow output. Public logs put them in plain view.
- disclosed within days, CVE-2025-30066More than 23,000 repositories ran it. The fix is to upgrade past the tampered tags and rotate every secret the pipeline could have printed.
how it surfaced
The compromise was caught and disclosed within days, but by then the malicious code had moved through more than 23,000 repositories. Exposed material included access keys, GitHub personal access tokens, npm tokens, and private keys. CISA added CVE-2025-30066 to its Known Exploited Vulnerabilities catalog. The fix was to upgrade past the tampered tags and rotate every secret the pipeline could have printed.
Pinning to a version tag trusts that the tag will keep pointing where it pointed.
A tag is a label the upstream can move, so the only pin that holds through a repository takeover is one to an immutable commit hash.
picture it at a company like yours
The company here is made up. The other two paths are not: they happened, and each links to the story.
- in this storythe day it mattersThe helper is hijacked and the version names now point to malicious code.what it costs youYour build prints your secret keys into logs anyone can read.
- or it goes like thisit happenedSolarWinds / Sunburst, 2020howA genuine, signed update from a certified vendor carries a backdoor.what leavesa quiet way into your networkwho ends up with ita state intelligence service
- or it goes like thisit happenedPolyfill.io, 2024howA script your site loads from elsewhere changes owner.what leavesyour visitors' safetywho ends up with itthe new owner of the script
sources
your turn
what you can do about it
this week
Open one workflow file and check whether its actions point at version tags or at full commit hashes.
Your CI pipeline runs third-party actions you did not write, with access to secrets you would not paste anywhere by hand. The question the quiet months ask is whether those dependencies are pinned to something that cannot be moved out from under you. The tj-actions secrets reached daylight through exactly the gap between those two.
go deeper
if you're not sure where to start
Run the offboarding checklist on one leaver. It covers every account and token to close when someone leaves.
open the checklist →
