DEV Community

Eldor Zufarov
Eldor Zufarov

Posted on

Inside the Chain: the tj-actions compromise wasn't one incident — it was three

Every scanner involved in the March 2025 tj-actions/changed-files attack did exactly what it was built to do. That's the part that should worry you more than a missed CVE.

One fact isn't a chain

A finding is a single fact: a leaked token, a mutable tag, a transitive dependency. Security orgs are built around single facts — SAST findings go to AppSec, SCA findings go to the dependency team, CI/CD findings go to DevOps. Each team closes its own list correctly. Nobody owns the line between the lists. That gap is where this attack lived.

The three hops

Hop 1. A personal access token sits in the GitHub Actions secrets of SpotBugs, a Java static analysis tool. Unremarkable — thousands of projects are set up exactly this way.

Hop 2. That token has write access to a second project: reviewdog, a code review automation tool.

Hop 3. Reviewdog is a dependency, a few steps removed, of tj-actions/changed-files — a GitHub Action used in 23,000+ repositories.

Token → tool that trusted it → tool that depended on that tool. Each hop, viewed in isolation, is completely fine. Together, they're a path.

The payload

With write access to tj-actions, the attacker didn't add malicious code anywhere visible. They moved the v45 tag — and others — to point at a different commit. Every workflow referencing that tag by name (the default, recommended pattern) started executing a script that printed CI/CD secrets, API keys, and cloud credentials straight into the build log, in plain text.

Detection to confirmed CVE: under 24 hours. Appearance on CISA's Known Exploited Vulnerabilities catalog: within days. The response was fast — the exposure window still mattered for anyone whose logs were public.

The 10-second check

Open a workflow file. Find a line like uses: some-action, then look at what follows the @. If it isn't a 40-character commit hash, it's a tag — and a tag can move without a single line in your workflow file ever changing.

The actual point

This incident wasn't defined by a vulnerability. It was defined by a trusted connection that quietly changed. Different stacks will produce different versions of this same shape — the hard part is never spotting a connection, it's knowing which of the hundreds of unremarkable ones deserves a second look.

Episode 3 of They Read The Manual, "Inside the Chain," walks through the full three-hop compromise and shows what real detection code for this pattern looks like.

🎥 Watch: https://youtu.be/y-QEHIk9WvQ

Top comments (0)