Back to glossary

Software Supply Chain

A software supply chain is every source, dependency, tool, build system, registry, and account that code passes through between its authors and production.

Also known as software supply chains, open source supply chain, open source software supply chain, OSS supply chain

A software supply chain, also called the open source supply chain, is everything code passes through between its authors and the machine that runs it: source repositories, dependencies, package registries, build systems, CI/CD pipelines, developer tools, and the accounts that publish through them. Every link is trusted implicitly, so compromising one link reaches everyone downstream.

The mechanism is transitive trust. An install resolves version ranges against the registry's current state, then downloads, and often executes, code chosen by people you have never met. We measured one fresh project on October 26, 2026: a Vite and React app that declares 9 packages installed 27 on Linux, maintained by 30 npm accounts. Only 16 of the 27 carried build provenance.

Our position: a software supply chain is defined by what executes, not by what a manifest declares. The tool that scaffolded that app, the 147 package copies bundled inside npm itself, the CI actions that build it, and the extensions in the developer's editor all run with real credentials. None of them appear in package.json or a typical SBOM. Inventories built from manifests describe a fraction of the chain, and attackers now work in the rest.

What is a software supply chain?

#

A software supply chain is the graph of sources, builds, dependencies, and published packages that produces a piece of software, plus the infrastructure and people that operate each step. The SLSA specification states the recursive version: "One artifact's supply chain is a combination of its dependencies' supply chains plus its own sources and builds."

The recursion is the important part. Your application has a supply chain. So does each of your software dependencies, each transitive dependency beneath them, and the compiler that built all of them. In practice the chain has seven kinds of links:

LinkExamplesWhat you trust without checking
SourceGit repositories, maintainers, code reviewCommits come from who they claim, and released artifacts match the repository
DependenciesPackages from npm, PyPI, crates.io, Maven CentralEvery version is benign, including future versions your ranges will accept
BuildCompilers, bundlers, build.rs, Gradle pluginsThe build ran only the inputs it declared
CI/CDGitHub Actions, runners, caches, stored secretsThird-party actions and caches are the same code they were yesterday
Publishing and distributionRegistry accounts, tokens, OIDC trusted publishing, mirrorsThe publisher is the legitimate one, and caches serve what upstream served
Install and runtimePackage manager, lifecycle scripts, npx, base images, language runtimeInstall-time code is safe to run with your credentials
Developer environmentIDE extensions, CLIs, AI coding agents, MCP serversLocal tools are what they claim, and agents choose real packages

The software supply chain is the system itself. Software supply chain security is the practice of defending it. A supply chain attack enters through one of its links. A software bill of materials is one inventory of it, usually a partial one.

Commercial software has the same graph. A vendor's build server is a link in every customer's chain. The OWASP Top 10 2025 groups vendor compromises such as SolarWinds with malicious npm packages under a new category, Software Supply Chain Failures, ranked #3. Half of OWASP's survey respondents ranked it their top concern.

Anatomy of one npm install

#

We scaffolded Vite's default React and TypeScript template with npm 12.2.1 and Node.js 22.23.4 on Linux x64, on October 26, 2026 (UTC), and installed it:

Shell Session
$ npm create vite@9.3.1 app -- --template react-ts --no-interactive
npm warn exec The following package was not found and will be installed: create-vite@9.3.1
npm notice run npx
...
$ cd app && npm install
added 27 packages, and audited 28 packages in 8s
found 0 vulnerabilities

The first command is already a supply chain event. npm create runs npx underneath: it fetched create-vite from the registry and executed it before the project, its package.json, or its lockfile existed. In a non-interactive shell, npm printed a warning and ran it.

found 0 vulnerabilities is accurate and beside the point. npm audit matches installed versions against known-vulnerability advisories. It says nothing about who can publish the next version, or about anything outside the lockfile.

We then joined the lockfile against the registry record for every resolved version:

One npm install, measured

Vite react-ts template, npm 12.2.1, Linux x64, Oct 26, 2026 (UTC). Bars share one scale.

Packages declared in package.json
9
Entries in package-lock.json
70
light segment: 46 platform-specific native binaries
Packages installed on this machine
27
24 devDependencies, 3 in the production bundle
npm accounts that maintain them
30
Installed packages with SLSA provenance
16
the other 11 were published from personal or bot accounts

Also in the chain, but in no project file

create-vite, via npxnpm CLI: 147 bundled package copiesNode.js runtimeCI actionseditor extensions
MeasureCount
Packages declared in package.json9 (2 dependencies, 7 devDependencies)
Entries in package-lock.json70
Lockfile entries that are platform-specific native binaries46
Packages installed on Linux x64 (glibc)27
Packages that install on macOS arm64, per the lockfile's os and cpu fields28
Installed packages that end up in the production bundle3 (react, react-dom, scheduler)
npm accounts listed as maintainers of the 27 installed packages30
Installed packages published by GitHub Actions through trusted publishing, with SLSA provenance16
Installed packages published from a personal or bot account, without provenance11
Package copies bundled inside npm 12.2.1 itself, outside the lockfile147

Four things in that table matter more than the totals.

The supply chain is per machine. 46 of the 70 lockfile entries are prebuilt binaries for one operating system and CPU: 19 for oxlint, 15 for Rolldown, 11 for Lightning CSS, and fsevents. A Linux CI runner and a macOS laptop installing from the same lockfile execute different native code. The macOS set also adds fsevents, the only package in the graph with an install script (node-gyp rebuild), which npm 12 does not run unless you allow it. One lockfile describes several supply chains.

devDependencies are not out of scope. 24 of the 27 installed packages are development dependencies. Vite and Rolldown write every byte of the production bundle, and they run on developer laptops and CI runners with whatever credentials those machines hold. A build tool does not ship as a file. Its output ships.

Provenance tells you where a package was built, not whether it is safe. Here is the registry's record for react@19.3.1, trimmed:

JSON
{
  "name": "react",
  "version": "19.3.1",
  "_npmUser": {
    "name": "GitHub Actions",
    "email": "npm-oidc-no-reply@github.com",
    "trustedPublisher": { "id": "github" }
  },
  "dist": {
    "integrity": "sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw==",
    "attestations": {
      "url": "https://registry.npmjs.org/-/npm/v1/attestations/react@19.3.1",
      "provenance": { "predicateType": "https://slsa.dev/provenance/v1" }
    }
  }
}

Decoding the provenance attestation from that URL gives the repository github.com/react/react, the ref refs/heads/releases/19.3.x, commit 61aa00df822d0eb76d0362eb8b6fdd3527693efe, the workflow .github/workflows/runtime_release_from_ci.yml, and a GitHub-hosted runner. That is a real guarantee: this tarball came from that workflow at that commit. Package provenance says nothing about whether the commit or the workflow was benign. The 11 installed packages without provenance, including the TypeScript compiler and Lightning CSS's native binary, carry no verifiable link from tarball to commit at all.

The tool that installs the chain is part of the chain. npm 12.2.1 bundles 147 package copies of its own. They never appear in your lockfile or in npm ls. The registry entry for npm 12.2.1 carries a registry signature but no provenance attestation. create-vite, run once through npx, is in no file in the project. Neither is Node.js, nor the GitHub Actions that will build this app in CI.

Where can a software supply chain be attacked?

#

A software supply chain can be attacked at every link, and recent incidents have hit each one. Every incident below entered through a different link:

Every link, one incident each

Code flows top to bottom. Developer tools sit beside the chain and touch every stage.

  1. Source and maintainers
    trust: release artifacts match the repository
    xz-utils 5.6.0, 5.6.1 · Mar 2024
  2. Maintainer account
    trust: the publisher is the real maintainer
    axios 1.14.1, 0.30.4 · Mar 31, 2026
  3. Build-time code
    trust: the build runs only declared inputs
    arrayref 0.3.10 via proc-macro1 build.rs · Aug 20, 2026
  4. CI/CD
    trust: an action's tag points at yesterday's code
    Trivy action, 75 of 76 tags · Mar 2026
  5. Publishing and distribution
    trust: the pipeline and the mirror are honest
    TanStack via trusted publishing · May 2026boltdb-go/bolt in Go mirror · 2021 to 2025
  6. Install and runtime
    trust: install-time code is safe to run
    axios dropper ran as postinstall
  7. Developer environment
    trust: local tools and agents pick real code
    GlassWorm extensions · since Oct 202553 slopsquatting targets · Jul 2026
  • Source and maintainers. The xz-utils backdoor in versions 5.6.0 and 5.6.1 (March 2024) shipped in release tarballs, through build files that did not exist in the Git repository.
  • Maintainer account. In the axios compromise (March 31, 2026), a hijacked publish of versions 1.14.1 and 0.30.4 added plain-crypto-js@4.2.1, whose postinstall dropper ran on install. axios has about 100 million weekly downloads.
  • Build-time code. In the arrayref, internment, and append-only-vec compromise (August 20, 2026), a new dependency, the typosquat proc-macro1, ran a loader from build.rs during cargo build. RustSec counted roughly 152 million downloads of the last clean arrayref release.
  • CI/CD. In the Trivy GitHub Actions compromise (March 2026), 75 of 76 version tags were force-pushed to a payload that stole runner secrets. More than 10,000 workflow files referenced the action.
  • Trusted publishing. In the TanStack compromise (May 2026), cache poisoning let attacker code extract an OIDC token from a CI runner and publish through the project's own trusted-publisher binding.
  • Mirrors and caches. The boltdb-go/bolt typosquat was served from the Go Module Mirror for over three years after its GitHub tag was rewritten to clean code. It was cached in 2021 and reported in 2025.
  • Developer tools. GlassWorm has shipped malicious and hijacked editor extensions on Open VSX and the VS Code Marketplace since October 2025, touching 237 unique extensions as of October 2026.
  • AI agents. A slopsquatting study (July 2026) found 53 package names that all 5 frontier models tested hallucinated and that were still available to register as of April 2026.

Four patterns run through these incidents.

First, several of these attacks never touched the code a reviewer reads. The xz backdoor lived in tarballs, not Git. The malicious axios versions did not appear in the project's GitHub tags. The boltdb-go tag on GitHub was rewritten to clean code while the mirror kept serving malware. Reviewing the repository, or trusting package metadata, would have missed all three. Analysis has to run on the artifact you actually install.

Second, many of them executed before any application code ran. The axios dropper was a postinstall script, the Rust loader ran at compile time, and the Trivy payload ran inside CI. Install time is where most supply chain attacks execute. Default-on dependency lifecycle scripts were a design mistake, which npm v12 (July 8, 2026) and pnpm 10 (January 2025) have now corrected. That fix covers package manager lifecycle hooks only. Cargo still runs build.rs, and CI still runs whatever an action's tag points to.

Third, trusted publishing did not stop the TanStack compromise. As Socket's report on the TanStack compromise put it, "an attacker who can execute in GitHub Actions can generate valid Sigstore attestations for malicious packages." Provenance and trusted publishing are necessary, because they remove long-lived tokens and pin a package to a pipeline. They are not sufficient, because the pipeline itself is a link.

Fourth, none of these is a bug in honest code. Each is malicious code, a hijacked publish, or a name waiting to be claimed. A scanner that matches dependency versions against CVEs is looking for the wrong thing.

How is a software supply chain different from a physical supply chain?

#

A software supply chain differs from a physical supply chain in four ways, and each one removes a safeguard that manufacturing takes for granted.

Software parts are copied, not shipped. A malicious version is available to every consumer the moment it is published. There is no transit time and no inventory to recall. Socket's automated detection flagged the axios dropper six minutes after it was published, at 00:05:41 UTC on March 31, 2026. In a software supply chain, six minutes counts as early.

Most suppliers have no contract with you. Open source licenses disclaim warranty, and most maintainers do not know who depends on them. Ken Thompson made the point in his Turing Award lecture, Reflections on Trusting Trust, published in August 1984: "You can't trust code that you did not totally create yourself." Four decades later, almost nobody creates more than a sliver of the code they ship.

The receiving dock runs the goods. A warehouse does not power on a pallet to see what it does. Package managers did exactly that for years: npm lifecycle scripts, Python's setup.py in source distributions, and Cargo's build.rs all execute supplier code during install or build, before anyone has inspected it.

The supplier decides when you get new parts. Version ranges such as ^1.13.5 hand that decision to whoever publishes next. The hidden blast radius of the axios compromise summed it up: "Nothing in your code changed. The only thing that changed is the registry." A lockfile freezes existing installs, but the same post adds: "Lockfiles make existing installs deterministic. They do not make new installs safe." Code fetched by npx or a global install never appears in a project lockfile, so a clean lockfile checked after the fact does not prove you were never exposed.

The analogy fails where it would help most. A factory inspects parts when they arrive. A software supply chain's default is to accept whatever the registry serves and run it. The fix is to restore receiving inspection: analyze each artifact before it is installed, not after it is deployed.

How do you map your software supply chain?

#

You map a software supply chain by combining four records, because each one sees a different slice of it:

RecordWhat it capturesWhat it missesThe question it answers
LockfileExact versions and integrity hashes for one project's resolved packagesnpx and global tools, the package manager's own code, CI actions, new resolutionsWhat will a clean npm ci install?
SBOM (SPDX or CycloneDX)Components inside a shipped artifactBuild tools whose output shipped, CI, developer machines; often generated from manifests aloneWhat did we ship?
Provenance attestationRepository, commit, workflow, and builder that produced a packageWhether that commit or workflow was malicious; every package published without oneWas this tarball built where it claims?
Ingest log at a registry proxyEvery package any tool actually fetched through the proxy, with timestampsMachines and agents that bypass the proxyWhat entered our environment, and when?

Our verdict: use lockfiles for reproducible builds, SBOMs for what you ship to customers and regulators, and provenance to reject packages built outside their expected pipeline. Only the ingest point sees the links that live outside every file, so the ingest point is where enforcement belongs. Socket Firewall works at that point: it is an HTTP/HTTPS proxy between package managers and registries that checks each requested package and blocks known-malicious ones at any dependency depth. A proxy has the same limit as any checkpoint. It sees only the traffic routed through it.

CI needs its own lockfile. As of October 2026, GitHub's Actions security guidance recommends workflow dependency lockfiles that record "the exact commit for every action your workflows depend on, including transitive dependencies." Repositories that cannot adopt one should pin actions to full commit SHAs, a baseline rule of GitHub Actions security, because a tag can be moved, as the Trivy compromise showed.

Regulation will not do the mapping for you. In January 2026 the US Office of Management and Budget rescission of memos M-22-18 and M-23-16 made SBOMs optional for federal agencies, replacing mandates with risk-based requirements. As of October 2026, the incentive to know your chain is the risk, not the paperwork.

A coding agent that installs a package to finish a task is a link in your software supply chain, and it leaves no lockfile behind. Brad Arkin, a Socket advisor who previously ran security at a global company, described asking an AI assistant to chart a spreadsheet and watching it fetch and run open source packages in a sandbox: "That was a software supply chain event." His post, The Code You Didn't Write Is Still Yours to Defend, names the three places code now enters: the build pipeline, the developer laptop, and the agent environment, "the gap almost nobody has mapped."

Every record in the previous section assumes code enters through files someone can read later. More of it now enters through events: an npx run, an extension auto-update, an agent resolving a name that a model invented. The open problem is not a better inventory format. It is putting a control at every place code is fetched, including the ones nobody has listed yet.