
The malicious code snippet
While experts were investigating the compromise of the widely used GitHub Action tj-actions/changed-files
, it became apparent that attackers had infiltrated the GitHub ecosystem far earlier than initially believed. The starting point of the entire chain was the leak of a personal access token (PAT) from the SpotBugs project, used within GitHub Actions. According to a recent investigation, the attack began as early as November 2024, though it remained undetected until March 2025.
Initial access was obtained through a vulnerable workflow in the SpotBugs repository—a popular static code analysis tool for Java. In one such workflow, a project developer mistakenly designated their personal access token as a secret, enabling an attacker to exfiltrate it via the pull_request_target
trigger—a hazardous feature that allows forked code to access secrets from the upstream repository.
Less than two weeks after the vulnerable workflow was introduced, on December 6, the attacker submitted a malicious pull request to spotbugs/sonar-findbugs
. The plan succeeded—GitHub Actions triggered via pull_request_target
, exposing the PAT and granting the adversary maintainer privileges in SpotBugs.
This marked the beginning of lateral movement. First, the attacker gained control over other SpotBugs repositories, then pivoted to the reviewdog project—a tool for code review automation on GitHub. Using the same stolen token, the attacker modified the v1
tag in reviewdog/action-setup
, redirecting it to a malicious commit. This poisoned code was then embedded into other GitHub Actions, including eslint-changed-files
, and eventually into the highly popular tj-actions/changed-files
.
The latter was particularly vulnerable: over 23,000 repositories rely on this Action to track file changes. The tampered version silently exfiltrated CI/CD secrets—including tokens, API keys, and passwords—into standard build logs, which were publicly accessible. Anyone with knowledge of where to look could effortlessly harvest sensitive credentials.
Interestingly, the attackers did not immediately target high-profile tools. They first tested the waters with a repository from Coinbase named agentkit
—a dry run that yielded no results. Only after this exploratory phase did they launch a full-scale assault on the open-source ecosystem.
On March 11, using the stolen token, the threat actor added a fake maintainer account named jurkaofavak
to SpotBugs and swiftly pushed a new branch containing a malicious workflow. This move entrenched their presence and enabled further propagation. It later became clear that the compromised token granted access to two critical projects: reviewdog/action-setup
and spotbugs/spotbugs
.
The attack unfolded like a well-rehearsed play. The CI workflow in tj-actions/changed-files
invoked eslint-changed-files
, which in turn ran the malicious reviewdog code. This allowed the attacker to obtain a write-access token for tj-actions/changed-files
, modify all tags, and point them to compromised commits. As a result, project secrets began leaking into log files—undetected by developers.
Although the original point of entry has now been identified, the investigation is ongoing. Researchers from Unit 42 emphasize that the attackers operated with stealth, moved deliberately, and attempted to cover their tracks. Yet a critical question remains: having attained such elevated access, why would the adversaries recklessly leak secrets into public logs—thus revealing their presence?