Skip to content

Security

Dependency and Supply Chain Security

Almost every application is built on a large number of third-party packages, and those packages depend on further packages of their own, forming a supply chain most developers never fully inspect. Supply chain security is about managing the risk that comes with that: a dependency with a known vulnerability, a package that's been compromised or hijacked, or a build process that could be tampered with before the code ever reaches production. It matters because trusting a dependency, by installing and running it, is effectively trusting everyone who can publish to it - usually a much larger and less scrutinized group than a company's own engineers.

Why it matters

Most of an application's code, by volume, is usually dependencies, not code the team wrote
A vulnerability in a widely used library can affect a huge number of applications at once, which is why dependency vulnerabilities are tracked and disclosed as their own category of risk.
A compromised package can run with the same access as the application itself
Installing a dependency generally means running its code, including any install scripts, with the same permissions as everything else in the project - there's no sandbox by default.
Old, unmaintained dependencies accumulate unpatched vulnerabilities silently
A dependency that was fine when added can develop known, disclosed vulnerabilities over time if it's never updated, with no active signal telling the team unless they check.
The build and release pipeline is itself part of the supply chain
Tampering doesn't have to happen in a dependency's source - a compromised build server or a hijacked publishing credential can insert malicious code between a trusted source and the artifact that actually ships.

Known vulnerabilities in dependencies

Disclosed vulnerabilities in public packages are tracked in shared databases, and automated scanning tools compare a project's installed versions against those records to flag ones that need attention. This creates a real tension: pinning exact versions keeps builds reproducible and predictable, while never updating leaves known, disclosed vulnerabilities unpatched, so both pinning and a deliberate process for reviewing and applying updates are needed together, not one instead of the other.

Shell
npm ci                  # installs exactly the versions in the lockfile
npm audit                # checks installed dependencies against known vulnerability databases
npm audit fix             # applies available non-breaking fixes

# a lockfile change in a pull request is worth reviewing like any other diff -
# a new indirect dependency is new code now running in the application

Trusting what you install

Package registries have had real incidents of malicious or hijacked packages, so installing a new dependency is worth a brief, deliberate look: whether it's actively maintained, how widely it's used, and whether its recent history looks normal. A lockfile makes installs reproducible so the exact set of dependencies used in testing matches what actually ships, and some ecosystems support verifying a package's checksum or signature against what its maintainer published.

Keeping the pipeline itself trustworthy

The build and release pipeline is also part of the supply chain, not just the dependencies it pulls in. A compromised build step inherits whatever credentials that pipeline has access to, so scoping those credentials narrowly, and not running arbitrary, unreviewed code with access to secrets, limits what a compromised build can actually do.

Mistakes people make here

Never updating dependencies once a project works
a dependency that was safe when added can accumulate publicly known, disclosed vulnerabilities over months or years; leaving it pinned indefinitely trades a known, patchable risk for an unpatched one that only gets worse.
Updating dependencies with no review of what changed
a major version bump, or even a patch release, can include a maintainer change, a new indirect dependency, or unexpected behavior; treating an update as routine and unreviewed misses the cases where it isn't.
Not using a lockfile, or not committing it
without a lockfile, 'the same install' can resolve to different transitive dependency versions on different machines or at different times, which makes both bugs and vulnerabilities harder to reproduce and reason about.
Giving CI pipelines broader access than the build actually needs
a build step compromised through a malicious dependency inherits whatever credentials the pipeline has available; scoping those credentials narrowly limits what a compromised build can actually do.
Assuming a popular package is automatically safe
popularity correlates with more scrutiny on average, but it isn't a guarantee - widely used packages have been compromised or hijacked too, and popularity itself makes them a more attractive target, not an automatically safer one.

Strengths and trade-offs

Where it is strong

  • Automated dependency scanning catches known, disclosed vulnerabilities with very little ongoing manual effort once it's set up.
  • Lockfiles make dependency installs reproducible, which is valuable for debugging and for security review, independent of any vulnerability scanning.
  • The tooling here has matured significantly, so a reasonable baseline no longer requires much custom work.

The trade-offs

  • Scanning only catches vulnerabilities that have already been discovered and disclosed; it says nothing about an undisclosed or not-yet-found issue in a dependency.
  • Keeping dependencies current is ongoing work, and updates occasionally break things - there's a genuine, recurring cost to staying patched, not just a one-time setup.
  • Deep transitive dependency trees make full manual review impractical past a certain size; teams end up relying on tooling and reputation rather than reading every line.

Who needs this

Any developer who installs third-party packages - which is nearly everyone building modern software - benefits from knowing the basics: keep a lockfile, run a scanner, don't ignore its findings indefinitely. Managing this at an organizational level is more of a platform or security engineering responsibility.

Questions about dependency and supply chain security

How is this different from just 'keeping dependencies up to date'?
Updating matters, but it's only one part of it. Supply chain security also covers whether a dependency should be trusted in the first place, whether installs are reproducible, whether the build pipeline that turns source into a shipped artifact is itself protected, and how quickly a team can react once a vulnerability in something they depend on is disclosed.
Are dependency scanners enough on their own?
They cover known, disclosed vulnerabilities well, which is genuinely most of the risk in practice, but they can't catch a vulnerability nobody has found yet, or a package that's maliciously behaving exactly as its undisclosed intent, rather than accidentally buggy. They're a strong baseline, not a complete answer.
Why does a lockfile matter for security specifically, not just consistency?
Without one, the exact set of transitive dependencies actually installed can vary between machines and over time, even from the same declared dependencies, which means a scan or a review done on one install doesn't necessarily reflect what another machine actually runs. A lockfile pins that down so what's reviewed and what's deployed are provably the same thing.
What's a practical first step for a project with no process around this today?
Commit a lockfile if one doesn't already exist, and turn on whatever automated dependency scanning the platform already offers, since most major registries and CI providers have a built-in or easily added option. Both are largely one-time setup that then runs with little ongoing effort, and they cover the most common, well-understood risk.

The primary source

Related concepts

← All concept guides