On March 14, 2025, every version tag of the tj-actions/changed-files GitHub Action — a workflow step used in more than 23,000 repositories — was silently rewritten to run code that dumped CI runner memory into public build logs, exposing npm tokens, RSA keys, and other secrets in plain text (CVE-2025-30066). The root cause traced back three days further, to a compromised personal access token in a separate dependency, reviewdog/action-setup@v1 (CVE-2025-30154). Neither incident involved a bug in application code. Both were compromises of the machinery that builds and ships code — the exact pattern security researchers watched play out a year earlier when a backdoor sat inside XZ Utils' liblzma library (CVE-2024-3094, CVSS 10.0) after a multi-year social-engineering campaign, and was only caught because a Microsoft engineer, Andres Freund, noticed SSH logins running 500 milliseconds slower than expected. Software supply chain attacks have moved decisively upstream: away from exploiting the code you wrote, and toward compromising the build systems, package registries, and CI/CD credentials that produce it. This post walks through the three dominant attack patterns of the last few years and the defensive priorities that actually address them.
What made the XZ Utils backdoor different from a typical open-source vulnerability?
The XZ Utils backdoor was different because it was a deliberate, patient infiltration rather than an accidental bug. An account using the name "Jia Tan" spent roughly two years building trust as a co-maintainer of the widely used compression library before shipping a backdoor in versions 5.6.0 and 5.6.1, released in February 2024. The backdoor modified the build process itself — hidden in test files and build scripts, not in reviewable source — to intercept SSH authentication via sshd on systemd-based Linux distributions, potentially granting a remote attacker code execution. It was never caught by static analysis, package scanning, or code review; Andres Freund found it on March 28, 2024, purely by noticing anomalous CPU usage and login latency while benchmarking Postgres. Security researchers and outlets including Datadog Security Labs have since described it as the most sophisticated open-source supply chain attack discovered to date, precisely because it targeted the build pipeline rather than a single vulnerable function.
How does dependency confusion actually trick a build system?
Dependency confusion works by exploiting how package managers resolve names when both a private and a public package share one. In 2021, researcher Alex Birsan demonstrated this against internal package names he'd found leaked in JavaScript files, build logs, and support tickets at companies including Apple, Microsoft, PayPal, and Tesla. He published public packages on npm, PyPI, and RubyGems using those exact internal names but with higher version numbers, and because many build systems default to pulling the highest available version across all configured registries, his code executed inside corporate build environments automatically — earning him over $130,000 in bug bounties. The fix isn't exotic: scoped package namespaces, registry allowlists, and pinning internal dependencies to a specific registry rather than "highest version wins." But four years later, typosquatting and confusion-style attacks remain a persistent, high-volume category on both npm and PyPI, tracked continuously in state-of-the-software-supply-chain research from firms like Sonatype and Socket.
Why has CI/CD token theft become the fastest-growing attack path?
CI/CD token theft has grown fastest because a single stolen credential now offers far more reach than a single stolen vulnerability. The tj-actions compromise is the clearest recent case: a maintainer's personal access token, taken via the earlier reviewdog/action-setup@v1 compromise, gave the attacker write access to rewrite every git tag on the Action — meaning any workflow pinned to a tag like v45 (rather than a commit SHA) silently pulled the malicious version on its next run. CISA issued an advisory the same week, and GitHub published advisory GHSA-mrrh-fwg8-r2c3 confirming the scope. This followed a similar pattern in the January 2023 CircleCI breach, where malware on a single engineer's laptop stole session tokens broad enough that CircleCI had to force-rotate all customer secrets and API tokens as a precaution — one endpoint compromise cascading into a mass credential rotation across their entire customer base.
Is this fundamentally different from the SolarWinds attack, or the same pattern repeating?
It's the same pattern at a different scale. In the 2020 SolarWinds compromise, attackers attributed to APT29 inserted the Sunburst backdoor directly into the build pipeline for SolarWinds' Orion platform, so that roughly 18,000 customers downloaded a trojanized, digitally-signed update through the vendor's own legitimate update mechanism. XZ Utils and tj-actions replicate that same insight — that compromising the thing that builds or distributes software is more efficient than attacking each downstream user individually — just executed against open-source maintainer trust and CI Actions marketplaces instead of a single commercial vendor's release pipeline. The earlier Codecov Bash Uploader compromise (discovered April 2021, active since January 2021) fits the same lineage: attackers altered a CI script to quietly exfiltrate environment secrets from every customer pipeline that ran it, undetected for roughly two months.
What do security teams actually need to do differently in response?
Security teams need to treat the build and CI environment as a first-class asset to secure, not just the application it produces. The consensus set of controls, reinforced by OpenSSF's guidance issued after the tj-actions incident, includes: pinning GitHub Actions and other CI dependencies to immutable commit SHAs instead of mutable version tags, since a tag can be rewritten but a SHA cannot; replacing long-lived personal access tokens with short-lived, scoped OIDC tokens issued per-workflow-run; generating build provenance under a framework like SLSA so a consumer can verify what commit, builder, and inputs actually produced an artifact; and maintaining a current SBOM in CycloneDX or SPDX format so that when the next CVE — or the next Jia Tan — surfaces, a team can answer "are we affected, and where" in minutes instead of days of manual grepping across repositories.
How Safeguard helps
Safeguard treats the build and CI layer as part of the attack surface it monitors, not an afterthought bolted onto application scanning. It generates CycloneDX-format SBOMs automatically on every build and ingests SBOMs from vendors and open-source dependencies alike, so when an incident like tj-actions or XZ Utils breaks, a team can query one inventory to find every affected repository instead of manually auditing workflow files one by one. Safeguard's reachability analysis extends the same discipline to dependency risk: a vulnerable transitive package only ranks as urgent when your code can actually execute the affected path, cutting through the noise that made attacks like the Birsan dependency-confusion research possible in the first place — internal teams that don't know what's actually reachable in their own dependency tree are the same teams that don't notice a shadowed package name. Griffin AI reads that reachability and provenance context together to explain findings in plain language and propose fixes, so supply chain risk gets triaged against real exploitability rather than raw CVE counts.