Supply Chain Attacks 2026 — Patterns, SLSA, and Defences

Published May 3, 2026 · 18 min read · APT

The shape of a software supply chain attack changed with SolarWinds (2020) and has been sharpening every year since: dependency confusion (2021), the Codecov bash uploader (2021), the Lapsus$ raids on identity providers (2022), the 3CX cascading compromise (2023), the xz-utils backdoor (2024). The attacker gets one foothold — a hijacked maintainer account, a typosquat dependency, a malicious GitHub Action, a signed update artefact — and inherits the trust of every downstream organisation that pulls the result. This post covers the live attack patterns 2024-2026, maps them to the SLSA framework, and lists the practical controls that move you up its levels.

The 2024-2026 attack patterns

PatternReference incidentInitial access
Vendor build-system compromiseSolarWinds Orion (2020)Implant in build, signed update channel
Dependency confusionBirsan disclosure (2021)Public package shadows internal name
TyposquattingRecurring on npm / PyPI / RubyGemsMisspelled package name
Maintainer account takeovernode-ipc, ua-parser-js, eslint-scopePhished or session-stolen maintainer
Long-game social engineeringxz-utils CVE-2024-3094Trusted contributor over 2+ years
Malicious GitHub Actiontj-actions/changed-files (2025)Compromised popular Action runs everywhere
CI/CD secret theftCodecov bash uploader (2021)Modified script, exfil env vars
Cascading vendor compromise3CX (2023)Trojanised upstream dependency

Dependency confusion in practice

Internal package names that match a public registry namespace are the primitive. Default resolution in npm, pip and gem prefers the highest version — if an attacker publishes a higher version of internal-tooling on the public registry, internal builds that do not pin the registry origin will pull the malicious one.

# Probe -- given a leaked internal package.json
{
  "dependencies": {
    "internal-billing-utils": "^1.4.0"
  }
}

# The attacker publishes:
npm publish --tag latest internal-billing-utils@99.0.0
# whose preinstall script exfils env vars to attacker.example.

# Defence -- scope to a private registry
npm config set @target:registry https://npm.target.example/
# package.json
{ "dependencies": { "@target/internal-billing-utils": "1.4.0" } }

Typosquatting

Common variants: misspellings (requets vs requests), confusable characters, alternate scopes (@types/foo when the real package is at @stencil/foo), and namespace squatting on language-mirror registries (PyPI mirrors of npm package names, etc.). The CISA advisories catalogue continues to publish recurring incidents.

Maintainer takeover and long-game compromise

xz-utils(CVE-2024-3094) is the textbook case of long-game social engineering: a contributor cultivated trust over more than two years before introducing a backdoor in liblzma that, when bundled into OpenSSH's sshd via patches in some Linux distributions, allowed remote pre-auth code execution. Reference: the NVD entry and the CISA alert.

Malicious GitHub Actions

GitHub Actions referenced by tag (uses: org/action@v1) silently re-resolve when the tag is moved. The 2025 tj-actions/changed-filescompromise demonstrated the blast radius — the action's tag was retargeted to a malicious commit, and every workflow that ran it during the window leaked CI secrets. The fix is to pin to the immutable commit SHA, not a tag:

# Vulnerable -- tag is mutable
- uses: tj-actions/changed-files@v44

# Hardened -- commit SHA is immutable
- uses: tj-actions/changed-files@90a06d6ba9543371ab4df8eeca0be07ca6054959

# Add Dependabot to update SHAs in PRs you can review.
# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule: { interval: "weekly" }

SLSA — the framework levels

SLSA (Supply-chain Levels for Software Artifacts) is the OpenSSF framework that defines what "trusted" means at each maturity step. v1.0 simplified the levels to focus on the build track:

LevelBuild track requirement
Build L1Provenance exists, generated by the build platform
Build L2Provenance is signed by a hosted build platform
Build L3Build platform is hardened — non-falsifiable provenance, isolated builds

Provenance is a signed attestation that records: the source repo + commit, the build instructions, the build environment, and the resulting artefact digest. GitHub's artifact attestations land you at SLSA L2 with minimal config; reaching L3 means a hardened, isolated builder (e.g. the slsa-github-generator reusable workflow).

SBOM tooling — Syft and Grype

An SBOM (Software Bill of Materials) is the dependency manifest you ship alongside your artefact. It is the prerequisite for vulnerability triage at scale — when the next xz-utils-grade CVE drops, an SBOM tells you in seconds which builds shipped the affected version. The de-facto open-source tooling is Anchore's Syft (generate SBOM) and Grype (scan SBOM for known CVEs).

# Generate a SPDX SBOM from a built container
syft target.example/api:1.4.0 -o spdx-json > sbom.spdx.json

# Generate CycloneDX from a project directory
syft dir:./repo -o cyclonedx-json > sbom.cdx.json

# Scan SBOM for known vulnerabilities
grype sbom:sbom.spdx.json --fail-on high

# Sign the SBOM with cosign and attach it to the OCI image
cosign attest --predicate sbom.spdx.json --type spdxjson \
  target.example/api:1.4.0

Reference standards: NIST's SSDF SP 800-218 for the secure-software development framework; CISA's SBOM page; OpenSSF's Scorecard for project hygiene metrics.

Defences — the practical controls

Source — signed commits, branch protection

  • Required signed commits on protected branches — sigstore / GPG / SSH signing.
  • Required PR review with at least one independent reviewer; no self-merge.
  • CODEOWNERS for sensitive paths (CI config, build scripts, dependency manifests).
  • Branch protection that blocks force-push and tag deletion on release branches.
  • Dependabot or Renovate with auto-PR + human merge for dependency bumps.

Dependencies — pin and verify

  • Pin to commit SHA for GitHub Actions (not tags).
  • Pin direct + transitive dependencies via lockfiles checked in (package-lock.json, poetry.lock, go.sum, Cargo.lock).
  • Per-language scope: npm config set @org:registry ... to defeat dependency confusion.
  • Allow-list registries at the firewall — builders should not be able to fetch from arbitrary public sources.
  • Use npm install --ignore-scripts in CI; opt in per package where install scripts are needed.
  • Verify package signatures where the registry supports it (npm provenance, sigstore-signed PyPI packages).

Build — hardened CI/CD

  • Ephemeral, isolated runners per build — no shared workspace, no carry-over secrets.
  • OIDC federation for cloud credentials (no long-lived deploy keys in CI).
  • Two-person review on changes to .github/workflows/, Jenkinsfile, .gitlab-ci.yml.
  • Generate SLSA build provenance and publish it alongside the artefact.
  • Generate SBOM in the build stage; sign it; attach via cosign attestations.
  • Network egress allow-list on builders — outbound only to known package mirrors.
# Minimal hardened GitHub Actions snippet
permissions:
  contents: read
  id-token: write       # for OIDC + provenance
  attestations: write   # for artifact attestations

jobs:
  build:
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11  # v4
      - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a  # v4
        with: { node-version: 20 }
      - run: npm ci --ignore-scripts
      - run: npm run build
      - uses: actions/attest-build-provenance@v1
        with: { subject-path: 'dist/**' }

Distribution — signed artefacts

  • Sign release artefacts with cosign / sigstore; verify on pull (cosign verify).
  • Image registry policy that blocks unsigned images at the cluster admission controller.
  • Maintain a published cosign public key or use keyless OIDC-bound signatures.
  • Publish provenance and SBOM as OCI referrers next to the image.

Runtime — constrain blast radius

  • Run with non-root, read-only root filesystem, dropped Linux capabilities.
  • Network egress restricted from production workloads — outbound to listed third parties only. A SolarWinds-style implant cannot beacon home if there is no path home.
  • eBPF / runtime detection on unexpected exec, unexpected DNS, unexpected outbound TLS SNI.
  • Workload identity bound to the specific service account — do not share credentials across services.

Detection content for the SOC

  • Alert on any git push --force to a release branch.
  • Alert on changes to .github/workflows/*.yml outside of an approved PR.
  • Alert on first-seen SNI from a production workload — Codecov-style implants beacon to a new domain.
  • Alert on package-manager network connections from production runtime (apps should not npm install in production).
  • Alert on signature-verification failure at registry pull or admission control.

Pentest scenarios for supply-chain readiness

A red-team exercise scoped against the supply chain answers the question the SBOM cannot: given the controls that are in place, how far can an attacker who compromises a build actually reach? Common scenarios:

  • Phished maintainer — the engagement adds a benign marker to a release artefact via a stolen-token push; the customer's pipeline is graded on whether the marker reached production unblocked.
  • Malicious GitHub Action — a fake third-party action is added in a feature branch; the test measures whether the workflow approval policy and the network egress allow-list catch the secret-exfiltration attempt.
  • Dependency-confusion publish — with the customer's permission, a same-name package is published to a public registry; the test measures whether the build pulls it.
  • Compromised builder — assume the runner is hostile (shell access during build); evaluate what credentials are reachable, whether OIDC limits the blast radius, and whether the produced provenance is verifiable.
  • Update-channel hijack — with permission, a signed-but-rolled artefact is staged in the release path; admission-control / signature verification are the gating controls.

Each scenario is reported with: the exact control that fired (or did not), the time-to-detect, the time-to-block, and the SLSA level the result is consistent with.

A 90-day hardening roadmap

  1. Days 0-30 — lockfiles in every repo; SHA-pin all GitHub Actions; signed commits required on release branches; Dependabot enabled.
  2. Days 30-60 — SBOM generation + Grype scan in every build; cosign sign + verify on container images; admission control blocks unsigned.
  3. Days 60-90 — SLSA build provenance attestations; egress allow-list on production workloads; runtime detection content shipped to SIEM.

References: slsa.dev, CISA ICT Supply Chain, NIST SP 800-218 SSDF, CISA SBOM, Syft, Grype, sigstore.

For a scoped supply-chain assessment aligned with this methodology — covering CI/CD posture, SLSA gap analysis, dependency hygiene, signed-artefact validation, and runtime constraints — see the AxVeil red team service.

FAQ

What is a software supply chain attack?

It is an attack where an adversary compromises a trusted upstream component, such as a build system, a dependency, a CI/CD pipeline, or a signed update, and inherits the trust every downstream organisation places in that component. SolarWinds (2020), dependency confusion (2021), Codecov (2021), the 3CX cascading compromise (2023), and the xz-utils backdoor (2024) are reference incidents that each gained one foothold and reached many victims.

What is SLSA and how does it help?

SLSA (Supply-chain Levels for Software Artifacts) is a framework that defines progressive levels of build integrity, from documented provenance up to hermetic, reproducible, two-party-reviewed builds. Mapping your pipeline against SLSA gives a concrete ladder of controls: generate signed provenance, harden the build platform, isolate the build, and verify provenance at deploy time.

What is an SBOM and do we need one?

A Software Bill of Materials is a machine-readable inventory of every component in a build, typically in CycloneDX or SPDX format. It lets you answer 'are we affected?' within minutes when the next Log4Shell or xz-utils class incident lands, instead of days of manual auditing. US Executive Order 14028 and downstream procurement increasingly require SBOMs, so most vendors now generate one in CI.

How do we defend against a malicious dependency or typosquat?

Pin dependencies to hashes (not floating version ranges), use a private registry or proxy that blocks public packages from shadowing internal names (the dependency-confusion defence), require lockfile review in code review, scan new dependencies for known-bad indicators, and gate on signed provenance where available. Treat a new transitive dependency as a change that needs the same scrutiny as your own code.

How do we secure our CI/CD pipeline against attacks like the GitHub Actions class?

Pin third-party Actions to a full commit SHA rather than a tag, scope workflow tokens to least privilege, isolate build secrets from pull-request workflows, require review for workflow file changes, and monitor for unexpected outbound connections from build runners. The build system is production infrastructure; an attacker who controls it controls every artifact you ship.

Harden your supply chain with AxVeil.

CI/CD compromise simulation, SLSA gap analysis, SBOM tooling, dependency hygiene, signed-artefact validation, runtime constraints, retest included.

Talk to a senior operator about supply-chain scope →
Share