Featured image of post Dependency Updates: latest Is Not a Security Strategy

Dependency Updates: latest Is Not a Security Strategy

Past and present supply chain compromises prove that automatic dependency updates are attack amplifiers. Here is what controlled trust looks like in practice.

On March 31, 2026, North Korean state actors compromised Axios, an npm package with over 70 million weekly downloads. Any project declaring axios@^1.14.0 connected to attacker C2 infrastructure the next time npm update ran. Six weeks later, a self-propagating worm hit 42 TanStack packages. It reached Mistral AI and UiPath within hours. It also carried valid SLSA provenance attestations, signed by the legitimate build pipeline.

The same infrastructure that helps us stay current also amplified these attacks. Automated dependency updates pushed the compromised packages into production builds faster than any human could react.

We learned that outdated dependencies are dangerous. That is true. But many teams have flipped the lesson around. Newer is treated as safer. It is not.

The Trust Problem

Modern package managers are not static library linkers. They are code execution pipelines. A single npm install can run arbitrary install scripts, read credentials, and run with full CI permissions. It does this across hundreds of transitive packages that nobody on the team has reviewed. Every dependency expands your trusted computing base.

It’s not every dependency you add that’s a problem; it’s every dependency you update.

โ€• Ben Hoyt, Every dependency you add is a supply chain attack waiting to happen

Two problems arise here:

  1. Version ranges like ^1.14.0 do not mean “I trust version 1.14.0”. They mean “I trust every future release that matches this constraint”. That is a large, open-ended trust delegation. The Axios attackers used exactly that. Version 1.14.1 was a manifest-only change that added a malicious dependency while leaving the Axios source code untouched. Semantic versioning signals maintainer intent, not trustworthiness. The same applies to floating Docker tags, unpinned GitHub Actions, and latest tags everywhere.

  2. Tools like Dependabot and Renovate normalized constant dependency updates. At scale, they improve visibility and shorten the window for known CVEs. But they also changed how we consume trust. Security automation quietly became trust automation. Dependency update PRs receive less review than internal code, even though they run with the same or higher privilege. The volume is overwhelming and eventually the workflow shrinks to: CI passes โ†’ vulnerability score drops โ†’ merge. That creates a dangerous illusion. “No known CVEs” does not mean “safe”. Supply-chain attacks are newly published, initially undetected, semantically valid, and technically “up to date”.

False positive alerts are not only a waste of time, they also reduce security by causing alert fatigue and making proper triage impractical.

โ€• Filippo Valsorda, Turn Dependabot Off

The XZ Utils backdoor in 2024 showed how long-running social engineering can compromise critical infrastructure. The Mini Shai-Hulud incidents in 2025 and the ongoing supply chain attacks show that the attack surface extends well beyond maintainer compromise. It reaches into version ranges, build pipelines, provenance systems, and the update automation that ties them together. These are not theoretical risks. They are documented, attributed attacks against the most popular packages in the ecosystem.

What Controlled Trust Looks Like

There is no perfect fix for supply-chain risk. There are practical ways to replace blind trust with controlled trust:

Pin Versions

Remove ^ and ~ from package.json or whatever dependency manifest you use. Use exact versions. For transitive dependencies, force the version with overrides:

1
2
3
4
{
  "dependencies": { "axios": "1.14.0" },
  "overrides": { "axios": "1.14.0" }
}

Lockfiles like package-lock.json, pnpm-lock.yaml, Cargo.lock, and poetry.lock do not make dependencies safe. They make dependency state reproducible. You cannot investigate what you cannot reproduce.

Enforce Release-Age Cooldowns

Supply-chain attacks have a short half-life. The compromised TanStack versions lasted about three hours before someone caught them. A release-age cooldown rejects packages younger than a threshold. That gives the community time to spot compromises before they reach your builds:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# ~/.npmrc
min-release-age=7

# pnpm-workspace.yaml
minimumReleaseAge: 1440

# ~/.bunfig.toml
[install]
minimumReleaseAge = 604800

# ~/.config/uv/uv.toml
exclude-newer = "7 days"

A seven-day cooldown would have prevented both the Axios and TanStack compromises.

Control Upgrade Cadence

Filippo Valsorda recommends a better model than auto-updating: Run your test suite against the latest dependency versions daily in a sandboxed CI job. You get early warning of breakage without pulling untrusted code into production. You still update. You do it on your schedule, after review, when you are ready.

Verify Provenance, But Do Not Trust It Alone

The TanStack attack is the first documented case of malicious npm packages carrying valid SLSA Build Level 3 attestations. Provenance verification is necessary but not sufficient. Pair it for instance with deep package inspection to catch suspicious network calls, filesystem access, and hidden scripts before they run.

Deploy Repository Proxies

Place package managers behind a properly configured repository proxy like Sonatype Nexus, JFrog Artifactory, or an internal mirror. These proxies should be configured not only to mirror (ideally approved) dependencies, but also quarantine suspicious releases and scan for known malware. But keep in mind that most scanners only detect known threats. Sophisticated attacks are novel by design. Proxy scanning is one layer, not a replacement for version pinning and controlled upgrades.

Use Reachability-Aware Scanning

Not every CVE in your dependency tree matters. A vulnerability in a function you never call does not deserve the same urgency as one that is on your hot path. Go’s govulncheck traces your actual call graph and filters out findings in code your application never reaches. Other ecosystems are catching up. At a minimum, make sure your scanner can tell whether a vulnerable package is actually used. The best scanners go further and trace the call graph down to the specific vulnerable function.

Reduce Dependency Surface Area

A little copying is better than a little dependency.

โ€• Go Proverbs

Every package adds code, maintainers, credentials, infrastructure, and trust assumptions. Modern ecosystems normalize absurd dependency depth. Before adding a package, ask: can we write this in 30 lines? If yes, write it. A coding agent can do most of the work for you.

Controlled Trust, Not Blind Trust

The lesson from 2025 and 2026 supply-chain attacks is not “never update”. Stale dependencies become liabilities too. The lesson is that blind trust does not become safer just because it is automated.

Secure software supply chains are built on controlled trust: Pinned versions. Enforced cooldowns. Verified provenance. Proxy repository managers. Reachability-aware scanning. A small dependency surface. The fastest update is not always the safest. Sometimes the safest thing to do is wait.

Made and hosted in the EU ๐Ÿ‡ช๐Ÿ‡บ ยท Powered by Hugo & Stack
Imprint ยท Privacy