The Attack Surface Nobody Wants to Talk About

Most security teams have EDR on every laptop, a SIEM ingesting terabytes of logs, and a WAF in front of every public endpoint. And then they have their CI/CD pipeline, running with broad permissions, pulling dependencies from the internet, executing code from third-party actions, and nobody watching any of it.

That is where the real attacks happen now.

GitHub Actions runners spin up, build your software, push artifacts, and die, all in under ten minutes. They have access to production secrets, cloud credentials, package signing keys, and API tokens. Traditional endpoint detection tools were not built for this. By the time a signature gets updated or an alert fires, the runner is already gone.

Step Security was built specifically for this problem, and after watching what happened in 2025, the argument for using it has become very difficult to ignore.

What Step Security Actually Does

The core product is Harden-Runner, a GitHub Actions step you add to your workflows that gives you visibility and control over what runners are actually doing at runtime.

It operates in two modes. In audit mode, it logs all outbound network connections, file system activity, and process execution without blocking anything, so you can see the baseline of what a legitimate workflow actually does. In block mode, it enforces an egress allowlist, meaning any outbound connection that is not explicitly permitted gets blocked before it happens.

That second capability matters more than it sounds. The mechanism behind most CI/CD supply chain attacks is credential exfiltration: malicious code runs inside your workflow, reads environment variables containing tokens and secrets, and ships them to an attacker-controlled server. Block mode stops that exfiltration at the network layer before the data ever leaves your runner.

Beyond network control, Harden-Runner also monitors for source code and artifact tampering during the build process, correlates file and process activity with specific workflow steps so you can trace exactly what each action did, and analyzes GitHub API calls to generate minimum-permission recommendations for GITHUB_TOKEN. That last one is underutilized and underappreciated. Most workflows run with far more permission than they need, and when something goes wrong, that excess permission becomes the blast radius.

The documentation is at docs.stepsecurity.io/harden-runner and the open-source repository lives at github.com/step-security/harden-runner.

The Incident That Made This Concrete

In March 2025, a supply chain attack hit the tj-actions/changed-files GitHub Action, which at the time was used by over 23,000 repositories. The attackers compromised a GitHub Personal Access Token that gave them write access to the action's repository. They then pushed a malicious commit and retroactively moved version tags to point to it, so repositories using v35 or v44 pulled the attacker's code without knowing it.

The malicious code ran a Python script inside the workflow that dumped secrets from process memory, including GitHub tokens, npm tokens, RSA keys, and CI provider credentials, directly into the workflow logs. For any repository with public workflows, those logs were readable by anyone. This is now tracked as CVE-2025-30066.

Step Security's Harden-Runner detected the attack through anomaly detection: the action was making outbound connections to gist.githubusercontent.com in a way that did not match its documented behavior. That is exactly the kind of signal that disappears in most pipeline setups because nobody is watching outbound connections from runners.

The CISA alert covering this incident is at cisa.gov/news-events/alerts/2025/03/18/supply-chain-compromise-third-party-tj-actionschanged-files-cve-2025-30066-and-reviewdogaction.

Step Security's own write-up with detection details and mitigation steps is at stepsecurity.io/blog/harden-runner-detection-tj-actions-changed-files-action-is-compromised.

The npm Attack That Followed

September 2025 brought a different kind of attack, larger in scale and more automated than what we had seen before. A self-replicating worm, referred to in reporting as "Shai-Hulud," compromised over 500 npm packages and used them to harvest developer credentials across the ecosystem.

The worm's mechanism was straightforward in concept but devastating in practice. Compromised packages executed post-install scripts that extracted GitHub PATs, AWS keys, GCP credentials, Azure tokens, SSH private keys, and environment variables from developer machines and CI/CD environments. The stolen credentials were then used to compromise additional packages, propagating the campaign automatically.

Step Security's monitoring was referenced in CISA's alert on this campaign at cisa.gov/news-events/alerts/2025/09/23/widespread-supply-chain-compromise-impacting-npm-ecosystem.

For teams running these packages through CI/CD pipelines, egress control would have contained the damage by preventing the credential exfiltration step even when the malicious post-install script ran. That is the practical value of network-layer enforcement. The code executes, the exfiltration attempt fires, and it goes nowhere.

The Endpoint Problem in CI/CD Is Not Like Traditional Endpoints

When I was getting into security, the mental model for endpoint protection was simple: deploy an agent on every machine, the agent watches for known-bad signatures and behavior, you get alerts when something fires. That model works reasonably well for persistent systems.

CI/CD runners are not persistent systems. A GitHub Actions runner spins up, runs for ten minutes, and is gone. There is no persistent agent state. No behavioral baseline to compare against. No memory of what happened on the last run. Each build starts clean.

Traditional EDR is not designed for this. The agents assume persistence. The baselines require time to build. The detection logic is often tied to process behavior over hours, not minutes. You end up with either massive noise, because everything looks anomalous on a fresh system, or blind spots, because nothing runs long enough to trigger behavioral detection.

Harden-Runner approaches this differently. It instruments the runner at the workflow level, not the OS level. It knows what step is running at what moment. It knows which action should be making which network calls. When something falls outside that context, it can flag it immediately without needing to compare it against historical baseline data from the same machine. The ephemeral nature of the runner is expected behavior, not a gap in coverage.

Supply Chain Security Is an Operational Problem

The instinct in a lot of organizations is to treat supply chain security as a compliance checkbox. Pin your action versions, scan your dependencies, run SAST. Done. Those controls matter, but they address the problem at the wrong layer.

Pinning an action to a commit SHA prevents the tag-moving attack that hit tj-actions. It does not stop a compromised action that was already pinned before it was compromised. It does not stop a malicious npm package that was already in your lockfile. It does not stop a build step that behaves differently in your specific environment than it did in anyone else's.

Runtime monitoring addresses the residual risk after static controls have run. It is the difference between knowing what code you are running and knowing what that code is actually doing.

The military analogy is direct here. You can do all the pre-mission planning, intelligence gathering, and equipment checks you want. You still need someone watching what is happening during the operation because reality does not always match the plan.

What Deployment Looks Like

Adding Harden-Runner to an existing workflow is a single step added at the start of the job:

- uses: step-security/harden-runner@v2
  with:
    egress-policy: audit

Start in audit mode. Let it run for a week or two and collect the legitimate outbound connections your workflows actually make. Step Security's dashboard gives you that data in a format where you can generate an allowlist directly from the audit output. Then switch to block mode with the generated allowlist and you have network egress enforcement with minimal friction.

The Community tier covers public repositories at no cost. Private repositories and centralized policy management across multiple repositories require the Enterprise tier.

For teams running self-hosted runners or GitHub-hosted runners in restricted network environments, the egress policy can be managed centrally through Step Security's service rather than individually in each workflow file, which matters for organizations trying to enforce consistent controls across large numbers of repositories.

The Current State of the Threat

Supply chain attacks against CI/CD pipelines are not a theoretical risk category. They are the primary mechanism for large-scale credential theft in development environments right now. The tj-actions incident in March 2025, the npm ecosystem campaign in September 2025, and the Nx build system compromise in August 2025 are not isolated incidents. They are a pattern.

The pattern works because the attack surface is largely undefended. Developers trust their CI/CD environments implicitly. Security teams often lack visibility into what those environments are doing at runtime. The combination creates a gap that attackers have learned to exploit reliably.

Step Security is one of the more direct responses to that specific gap that actually exists in production at scale. It was built for this environment, by people who understood why traditional endpoint tooling does not fit the problem. The recent incident detections are not theoretical capabilities, they happened with real organizations running real workflows.

If your team is running GitHub Actions and you have not looked at what those actions are doing at the network layer, that is worth changing.

The Step Security newsroom with their latest announcements is at stepsecurity.io/newsroom.

Referenced CISA advisories: CVE-2025-30066 at cisa.gov, September 2025 npm ecosystem compromise at cisa.gov. Step Security documentation at docs.stepsecurity.io.