# 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

Source: https://socket.dev/glossary/software-supply-chain

Last updated: 2026-10-26

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](https://slsa.dev/spec/v1.0/terminology) 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](https://socket.dev/glossary/software-dependencies), each [transitive dependency](https://socket.dev/glossary/transitive-dependency) beneath them, and the compiler that built all of them. In practice the chain has seven kinds of links:

| Link | Examples | What you trust without checking |
| --- | --- | --- |
| Source | Git repositories, maintainers, code review | Commits come from who they claim, and released artifacts match the repository |
| Dependencies | Packages from npm, PyPI, crates.io, Maven Central | Every version is benign, including future versions your ranges will accept |
| Build | Compilers, bundlers, build.rs, Gradle plugins | The build ran only the inputs it declared |
| CI/CD | GitHub Actions, runners, caches, stored secrets | Third-party actions and caches are the same code they were yesterday |
| Publishing and distribution | Registry accounts, tokens, OIDC trusted publishing, mirrors | The publisher is the legitimate one, and caches serve what upstream served |
| Install and runtime | Package manager, lifecycle scripts, npx, base images, language runtime | Install-time code is safe to run with your credentials |
| Developer environment | IDE extensions, CLIs, AI coding agents, MCP servers | Local tools are what they claim, and agents choose real packages |

The software supply chain is the system itself. [Software supply chain security](https://socket.dev/glossary/supply-chain-security) is the practice of defending it. A [supply chain attack](https://socket.dev/glossary/supply-chain-attack) enters through one of its links. A [software bill of materials](https://socket.dev/glossary/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](https://socket.dev/blog/owasp-2025-top-10-adds-software-supply-chain-failures) 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
$ 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:

> From 9 declared packages to 27 installed, plus the links outside every file

| Measure | Count |
| --- | --- |
| Packages declared in package.json | 9 (2 dependencies, 7 devDependencies) |
| Entries in package-lock.json | 70 |
| Lockfile entries that are platform-specific native binaries | 46 |
| Packages installed on Linux x64 (glibc) | 27 |
| Packages that install on macOS arm64, per the lockfile's os and cpu fields | 28 |
| Installed packages that end up in the production bundle | 3 (react, react-dom, scheduler) |
| npm accounts listed as maintainers of the 27 installed packages | 30 |
| Installed packages published by GitHub Actions through trusted publishing, with SLSA provenance | 16 |
| Installed packages published from a personal or bot account, without provenance | 11 |
| Package copies bundled inside npm 12.2.1 itself, outside the lockfile | 147 |

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](https://socket.dev/glossary/postinstall-scripts) (`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](https://socket.dev/glossary/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:

> The links of a software supply chain and an incident that entered through each

- **Source and maintainers.** The [xz-utils backdoor](https://socket.dev/blog/how-to-use-socket-to-find-out-if-you-were-affected-by-the-backdoored-xz-package) 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](https://socket.dev/blog/axios-npm-package-compromised) (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](https://socket.dev/blog/popular-rust-crates-compromised) (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](https://socket.dev/supply-chain-attacks/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](https://socket.dev/blog/tanstack-npm-packages-compromised-mini-shai-hulud-supply-chain-attack) (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](https://socket.dev/blog/malicious-package-exploits-go-module-proxy-caching-for-persistence) 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](https://socket.dev/supply-chain-attacks/glassworm-v2) 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](https://socket.dev/blog/slopsquatting-targets-across-frontier-llms) (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](https://socket.dev/blog/npm-12) (July 8, 2026) and [pnpm 10](https://socket.dev/blog/pnpm-10-0-0-blocks-lifecycle-scripts-by-default) (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](https://socket.dev/blog/tanstack-npm-packages-compromised-mini-shai-hulud-supply-chain-attack) 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](https://socket.dev/blog/axios-npm-package-compromised) 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*](https://dl.acm.org/doi/10.1145/358198.358210), 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](https://socket.dev/blog/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](https://socket.dev/glossary/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:

| Record | What it captures | What it misses | The question it answers |
| --- | --- | --- | --- |
| Lockfile | Exact versions and integrity hashes for one project's resolved packages | npx and global tools, the package manager's own code, CI actions, new resolutions | What will a clean npm ci install? |
| SBOM (SPDX or CycloneDX) | Components inside a shipped artifact | Build tools whose output shipped, CI, developer machines; often generated from manifests alone | What did we ship? |
| Provenance attestation | Repository, commit, workflow, and builder that produced a package | Whether that commit or workflow was malicious; every package published without one | Was this tarball built where it claims? |
| Ingest log at a registry proxy | Every package any tool actually fetched through the proxy, with timestamps | Machines and agents that bypass the proxy | What 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](https://docs.socket.dev/docs/socket-firewall-overview) 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](https://docs.github.com/en/actions/reference/security/secure-use) 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](https://socket.dev/glossary/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](https://socket.dev/blog/federal-government-rescinds-software-supply-chain-mandates-makes-sboms-optional) 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.

## The newest link has no manifest

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*](https://socket.dev/blog/the-code-you-didnt-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.
