Software Supply Chain Security: Your Dependencies Are an Attack Surface

Fractional CTO · Founder of Injuria.ai
Modern applications aren't written from scratch. They are assembled. Your team writes the unique business logic, but that code sits atop a vast pyramid of open-source libraries, frameworks, and tools. This entire ecosystem, from a developer's keyboard to your production servers, forms your software supply chain. Each link in that chain represents a surface for attack. High-profile incidents like the Log4Shell vulnerability in a ubiquitous logging library and the sophisticated SolarWinds breach, where the build process itself was compromised, demonstrate that attackers have shifted their focus from targeting individual applications to poisoning the components they are built from.[18]
Understanding and securing this supply chain is no longer an optional exercise for elite teams. It is a fundamental responsibility of modern software engineering and leadership. The core challenge is that you are implicitly trusting the code of hundreds or thousands of unknown developers. Trust is not a security strategy. You need verification, transparency, and a deliberate, layered defense to manage the inherent risks of dependency-based development.[19]
What Is a Software Supply Chain?
A software supply chain encompasses every component, process, and person that touches your software on its path from source code to production deployment.[20] Think of it like a physical manufacturing supply chain. A car manufacturer relies on third parties for tires, electronics, and glass. If a supplier provides a faulty component, the safety and integrity of the entire car is at risk. The same principle applies to software.
Your software supply chain includes:
- Source Code: Your proprietary code and any open-source software you vendor or modify.[21]
- Dependencies: The libraries and packages your code imports, managed via tools like npm[1], Maven[2], or PyPI[3]. This includes both direct dependencies (what you explicitly add) and transitive dependencies (what your dependencies depend on).
- Build Tools: Compilers, transpilers, bundlers (like webpack), and containerization tools (like Docker) that transform source code into executable artifacts.
- CI/CD Pipelines: The automation servers (like Jenkins or GitHub Actions) and scripts that build, test, and deploy your code.[22]
- Artifacts: The outputs of the build process, such as binaries, JAR files, or container images.
- Registries: The repositories where artifacts are stored and from which they are pulled, like Docker Hub or a private artifact registry.
Each of these stages is a potential point of failure or malicious injection. A compromise at any step can undermine the security of the final product, often in ways that are difficult to detect.
The Anatomy of a Supply Chain Attack
Attackers target the supply chain because it offers immense use. Compromising a single popular library can grant them access to thousands of downstream applications. These attacks generally fall into three categories: compromising a dependency, the build process, or the distribution channel.
Compromising a Dependency
This is the most common vector, where an attacker tricks a developer into including malicious code.
- Typosquatting: An attacker publishes a package with a name very similar to a popular one, for example
python-requestsinstead ofrequests. A developer making a typo can inadvertently install the malicious version. Attackers have used this technique to publish thousands of malicious packages. - Dependency Confusion: This attack, famously demonstrated by researcher Alex Birsan in 2021[4], targets hybrid build environments. If a company uses an internal package named
internal-utils, an attacker can publish a package with the same name to a public registry like npm. If the build tool is configured to check public registries first, it may pull the malicious public package instead of the intended internal one. - Account Takeover: An attacker gains control of a legitimate maintainer's account through phishing or credential stuffing. They then publish a new, malicious version of the trusted package. The package's reputation remains intact, making the attack difficult to spot.
- Malicious Contribution: A bad actor becomes a contributor to an open-source project with the long-term goal of introducing a subtle vulnerability or backdoor. The recent backdoor in xz Utils[5] is a chilling example. A seemingly trusted maintainer, who had worked on the project for years, introduced highly obfuscated malicious code that targeted specific builds to compromise SSH servers.
Compromising the Build Process
Instead of attacking the source code, an attacker can target the infrastructure that builds it. The SolarWinds attack was a masterclass in this technique. Attackers gained access to SolarWinds' CI/CD environment and injected malware into the Orion software after the code was written but before it was signed and shipped. The final signed artifact, which customers trusted, contained a backdoor. This highlights the need to secure not just the code, but the entire build pipeline.[22]
Compromising Distribution
Even if the source code and build process are secure, an attacker can target the distribution mechanism. This could involve hacking a package registry to replace a legitimate package with a malicious one, or performing a man-in-the-middle attack to intercept a developer's download request and serve a tainted artifact. While transport-layer security (TLS) mitigates some of this risk, it's not a complete solution.
Defending the Supply Chain: A Multi-Layered Approach
There is no single tool that can secure your supply chain. A robust defense requires layers of security controls that provide transparency, verification, and enforcement throughout the software development lifecycle.[18] These practices align with the OWASP Top 10's[6] category of A06:2021 - Vulnerable and Outdated Components.
Securing Dependencies
This is the foundational layer. You must know what's in your software and whether it's vulnerable.
Software Composition Analysis (SCA) tools are designed for this. They scan your project's manifest files (package.json, pom.xml, go.mod) to create a complete list of all direct and transitive dependencies. They then cross-reference this list against databases of known vulnerabilities, like the National Vulnerability Database (NVD)[7], to report any risks.
Lockfiles are a critical, non-negotiable practice. Files like package-lock.json (npm), yarn.lock (Yarn), or Pipfile.lock (Pipenv) record the exact version of every dependency and sub-dependency used in a project. This ensures that every developer on a team, and the CI server, installs the identical dependency tree. Without a lockfile, running npm install could pull a newer, potentially malicious, minor or patch version of a dependency without warning. A lockfile makes builds deterministic and prevents unexpected code from entering your system.
Vulnerability Scanning should be automated. Tools like npm audit[8] or open-source scanners like Trivy[9] can be integrated directly into your CI pipeline. You can configure the pipeline to fail if a new dependency introduces a critical or high-severity vulnerability.
Let's see a concrete example. A developer adds an old version of lodash to a Node.js project:
// package.json
{
"name": "vulnerable-app",
"version": "1.0.0",
"dependencies": {
"lodash": "4.17.4"
}
}
Running npm install generates a package-lock.json pinning this version. Running npm audit reveals the problem:
# npm audit report
lodash < 4.17.21
Severity: high
Command Injection in lodash
fix available via `npm audit fix`
Will install lodash@4.17.21, which is a breaking change
node_modules/lodash
The audit clearly identifies the vulnerable package, describes the vulnerability, and suggests a path to remediation. This automated check prevents the vulnerability from silently making its way to production.
Securing the Build and Distribution Process
Knowing your dependencies is step one. Ensuring the artifacts you build and ship are secure and untampered is step two. This is where concepts like SBOMs, artifact signing, and SLSA come in.
Software Bill of Materials (SBOM)
An SBOM is a formal, machine-readable inventory of software components and dependencies.[19] Think of it as a detailed nutrition label for your application. It lists not just your direct dependencies, but their dependencies, and so on, creating a complete graph of your software's composition. Key standards for SBOM formats are SPDX (Software Package Data Exchange)[10] and CycloneDX[11].
Artifact Signing and Verification
How can a consumer of your software be sure it actually came from you and wasn't modified in transit? The answer is cryptographic signing. The build process can generate a digital signature for each artifact (like a container image or binary). Anyone who downloads the artifact can then verify its signature to confirm its authenticity and integrity.
Sigstore[12] is an open-source project that has become a leading standard for signing and verifying software artifacts. It consists of several components, including Cosign[13] for signing, Fulcio[14] for certificate issuance, and Rekor[15] for a transparency log. Using Cosign to sign and verify a container image is straightforward:
# Assume you have a container image: my-registry/my-app:v1.0
# Sign the image. This command will prompt you to authenticate with an OIDC provider (like Google or GitHub).
# It generates a short-lived key, signs the image, and records the signature in Rekor's transparency log.
$ cosign sign my-registry/my-app:v1.0
# A consumer can now verify the image's signature before running it.
# This checks the signature against the public key and the entry in the transparency log.
$ cosign verify my-registry/my-app:v1.0
Verification for my-registry/my-app:v1.0 --
The following checks were performed on each of these signatures:
- The cosign claims were validated
- The signatures were verified against the specified public key
- Any certificates were verified against the Fulcio roots.
This process provides strong guarantees that the image is legitimate and has not been tampered with since it was signed.
SLSA (Supply-chain Levels for Software Artifacts)
SLSA (pronounced "salsa") is a security framework, not a single tool. It provides a checklist of standards and controls to achieve increasing levels of supply chain security. SLSA's v1.0 specification defines three build levels[16] of assurance, focused on the provenance (origin and history) of software artifacts.
- SLSA 1: Requires the build process to be scripted and generate provenance, not an SBOM. This is the easiest level to achieve.
- SLSA 2: SLSA v1.0 Build Level 2 requires a hosted build service and authenticated provenance, but does not require version control (source requirements were removed from the Build track in v1.0). This prevents tampering with artifacts after the build.
- SLSA 3: The build platform must provide stronger guarantees of isolation and auditability. The provenance it generates is more trustworthy.
SLSA provides a maturity model. Teams can start at Level 1 and progressively harden their build and release pipelines over time.
Real-World Tradeoffs and Decision Making
Implementing these controls involves real costs and tradeoffs. A technical leader must weigh the risk against the investment.
Cost vs. Risk: Achieving SLSA 3 is extremely difficult and expensive. It may require re-architecting your entire build system. This level of assurance is necessary for critical infrastructure like operating systems or foundational cloud services. For a typical business application or a marketing website, the risk profile is much lower, and achieving SLSA 1 or 2 combined with robust SCA scanning is a more pragmatic and cost-effective goal.
Developer Friction: Overly aggressive security gates can grind productivity to a halt. If every build fails because of a low-severity vulnerability in a non-production dependency, developers will quickly look for ways to bypass the checks. A sensible policy might be to block builds on new 'critical' and 'high' severity CVEs, alert on 'medium' ones, and simply log 'low' ones. The goal is to reduce risk, not to achieve a perfect but impractical state of zero vulnerabilities.
The SBOM Challenge: Generating an SBOM is becoming a standard feature of many build tools. The real challenge is operationalizing it. An SBOM is just data. You need processes and tools to store, query, and monitor your SBOMs to get value from them. Without this, an SBOM is just a compliance checkbox that provides little actual security benefit.
Tooling Strategy: The security market is crowded. Instead of adopting a dozen different tools at once, focus on a phased approach. Start with the fundamentals: enforce lockfiles and integrate an SCA scanner into your CI pipeline. This addresses the most common attack vector. From there, move on to generating SBOMs and signing key artifacts with a tool like Sigstore. Finally, evaluate your build process against the SLSA framework to identify the next most important improvements.
Common Misconceptions
- "My lockfile makes me secure." A lockfile is essential for deterministic builds, but it only ensures you use the same version every time. It does not ensure that the version is secure. You still need SCA scanning to check that pinned version for known vulnerabilities.
- "An SBOM makes my software secure." An SBOM is an ingredient list, not a safety inspection. It provides the transparency needed to find vulnerabilities, but it does not fix them. It is a foundational tool for security analysis, not a security control in itself.
- "We only use dependencies from trusted sources." The xz Utils and SolarWinds incidents prove that trust is not a reliable defense. Even well-regarded projects and companies can be compromised. Security must be based on verification (signing, scanning, secure builds), not reputation.
The State of Software Supply Chain Security Today
Adoption of supply chain security practices is accelerating rapidly.[23] This push is driven by major incidents that have made the risks undeniable and by government action like the foundational US Executive Order 14028 on Cybersecurity, even though related OMB memos M-22-18 and M-23-16 were rescinded in January 2026.[23] The ecosystem is maturing to meet this demand.
Standards like SPDX, CycloneDX, SLSA, and Sigstore are providing a common language and toolset for the industry. Package managers have integrated auditing tools, and cloud providers like Google Cloud[17] and AWS offer managed services for artifact scanning, storage, and signing. These tools are making it easier than ever to implement foundational controls.
The frontier of supply chain security is now moving toward more advanced challenges. These include making hermetic and reproducible builds more accessible, developing better tools for consuming and analyzing SBOMs at scale, and finding ways to assess the security posture of open-source projects beyond just looking for known CVEs. Securing the software supply chain is a complex, ongoing effort, but it's one that is essential to building reliable and secure software.
Sources (23)
- npm (Node Package Manager)
- Apache Maven Project
- PyPI (Python Package Index)
- Dependency Confusion by Alex Birsan
- CISA: xz Utils Backdoor
- OWASP Top 10
- National Vulnerability Database (NVD)
- npm audit documentation
- Trivy GitHub Repository
- SPDX (Software Package Data Exchange)
- CycloneDX
- Sigstore
- Cosign (a Sigstore project)
- Fulcio (a Sigstore project)
- Rekor (a Sigstore project)
- SLSA (Supply-chain Levels for Software Artifacts)
- Google Cloud Artifact Analysis
- Muthu Ramachandran, Guide to AI for Cybersecurity (Springer Nature, 2026)
- Chris Hughes, Tony Turner, Software Transparency (John Wiley & Sons, 2023)
- John Walsh, Uzi Ailon, Matt Barker, Identity Security for Software Development ("O'Reilly Media, Inc.", 2025)
- David Okeyode, Joylynn Kirui, DevSecOps for Azure (Packt Publishing Ltd, 2024)
- Avdhesh Bhardwaj, Mastering DevSecOps (Apress, 2026)
- Cassie Crossley, Software Supply Chain Security ("O'Reilly Media, Inc.", 2024)