If you’re running GitHub Actions in your CI/CD pipeline — and let’s be honest, most of us are at this point — you need to pay attention to what happened this week. The popular tj-actions/changed-files action was compromised, and attackers used it to exfiltrate secrets from CI runners across thousands of repositories. This is the kind of supply chain attack that keeps me up at night.
What Happened#
The tj-actions/changed-files action is widely used — it helps workflows determine which files changed in a pull request or push, so you can conditionally run tests, lints, or deployments. It has millions of downloads and is referenced in countless workflow files.
Attackers managed to compromise the action by modifying the tagged releases. When a workflow references an action by tag (e.g., tj-actions/changed-files@v45), GitHub resolves that to a specific commit. The attackers altered what the tags pointed to, inserting malicious code that dumped CI runner environment variables — including secrets — to workflow logs.
The attack was initially spotted by researchers at StepSecurity, who detected unusual behaviour in their monitoring systems. The compromised versions were live for a window during which any repository using the action would have its secrets exposed in build logs.
The attack vector likely started through a compromise of the related reviewdog/action-setup action, which tj-actions had as a dependency. This cascading dependency compromise is exactly what makes supply chain attacks so devastating — you don’t just need to trust the action you’re using, you need to trust everything it depends on, and everything that depends on, all the way down.
The Fundamental Problem with GitHub Actions Security#
Here’s the uncomfortable truth: the way most teams use GitHub Actions is inherently risky. The common pattern of referencing actions by mutable tag names means you’re running code that can change underneath you without any notification or approval process.
Consider this typical workflow snippet:
- uses: tj-actions/changed-files@v45That @v45 tag can be re-pointed to a completely different commit at any time. Unlike a pinned dependency in package-lock.json or go.sum, there’s no integrity verification happening here by default. You’re essentially giving the action maintainer — or anyone who compromises their account — the ability to run arbitrary code in your CI environment with access to all your repository secrets.
The secure alternative is to pin actions to specific commit SHAs:
- uses: tj-actions/changed-files@abc123def456789...But almost nobody does this because it’s inconvenient. You lose automatic patch updates, and the workflow files become harder to read. This is a classic security-versus-usability tension, and usability has been winning.
Broader Supply Chain Lessons#
This incident is part of a pattern we’ve been seeing across the software ecosystem. The SolarWinds attack in 2020, the Codecov breach in 2021, the ua-parser-js and colors.js npm incidents — each one demonstrates that build and deployment infrastructure is a high-value target.
What makes CI/CD supply chain attacks particularly dangerous is the blast radius. A single compromised action or dependency can affect thousands of organisations simultaneously. And CI environments typically have elevated privileges: deployment credentials, cloud provider tokens, package registry keys, database passwords. It’s a treasure trove.
The industry has been making progress on supply chain security — SLSA frameworks, Sigstore for signing, SBOM requirements in government contracts — but the CI/CD pipeline remains a weak link. The tooling for verifying the integrity of CI components lags far behind what we have for application dependencies.
What You Should Do Right Now#
First, audit your GitHub Actions workflows. Search your repositories for any reference to tj-actions/changed-files and update or remove it. Check the GitHub advisory for the specific affected versions.
Second, pin your actions to commit SHAs, at least for actions that run with access to secrets. Yes, it’s more maintenance. Use Dependabot or Renovate to automate SHA updates — they can propose PRs when new versions are released, giving you a review step.
Third, restrict secret access. Use GitHub’s environment protection rules and required reviewers to limit which workflows can access production secrets. Not every PR build needs your deployment credentials.
Fourth, monitor your CI output. Tools like StepSecurity’s Harden-Runner can detect anomalous network activity from your CI runners. If a build step is making unexpected outbound connections, you want to know about it.
Finally, consider using only first-party or verified actions where possible. GitHub’s own actions (actions/checkout, actions/setup-node, etc.) have a higher security bar. For everything else, evaluate whether the convenience is worth the risk — sometimes a few lines of shell script in your workflow file is safer than importing a third-party action.
My Take#
I’ve been doing DevOps before it had a name, and the speed at which CI/CD pipelines have become the new “soft underbelly” of software organisations still catches me off guard. We spent years hardening our production environments, implementing zero-trust networking, and encrypting everything in transit and at rest. Meanwhile, our build systems are pulling in unverified code from the internet and running it with our most sensitive credentials.
The tj-actions incident should be a wake-up call, but I’ve seen enough “wake-up calls” in this industry to know that most teams won’t change their practices until it directly affects them. If you’re reading this, don’t wait for that to happen. Spend an afternoon auditing your CI pipelines. It’s the highest-impact security work you can do this week.
This is part of my Security in Practice series, covering real-world security incidents and their implications for development teams.
