Skip to content
Braincap
← All articles

Anatomy of a compromised dependency chain: what a supply-chain audit turns up

by Claudiu Hulea · IT Management Consultant

Illustration of a software dependency chain with one compromised link

During a software supply-chain audit we identified a compromised dependency chain: popular packages that, through a typosquat trick, pulled in malicious libraries with names almost identical to the legitimate ones. The most interesting part wasn’t the attack itself, but the trap in the remediation: the automated “fix” recommended by the tooling led right back into the infected versions. Here’s the anatomy and what to do.

What is a software supply-chain attack?

It’s the attack where you aren’t the direct target — the code you depend on is. Modern applications assemble hundreds of open-source packages, and each one pulls in others (transitive dependencies). A single compromised package anywhere in that tree is enough for the payload to reach your development machines and build pipeline — without anyone touching “your” application.

What did the audit turn up?

A very widely used npm package had been altered to depend on two typosquat libraries — names almost identical to well-known utilities:

  • obug instead of debug
  • piccolore instead of picocolors

The substitution was quiet: on install, the dependency tree pulled them in automatically, and their code executed on every build. The case is documented publicly in a security advisory (see Sources). It’s not a theory — it’s the classic dependency-chain dropper pattern.

Why is a dependency typosquat dangerous?

  • It runs at build time, on developer workstations and CI runners — a layer that endpoint antivirus and EDR rarely monitor.
  • Caret ranges (^) in manifests mean a new, malicious version can be pulled automatically on the next install, with no action from you.
  • It’s transitive: you didn’t ask for the bad package — a dependency of a dependency did — so it isn’t on your “direct” list and is easy to miss.

The trap: when the automated “fix” sends you back into the problem

This was the instructive part. Running the package manager’s automated security audit, the suggested “fix” (fix --force) pointed to newer versions that still contained the typosquats — a fix that actually reintroduced the problem.

Worse, the attacker hadn’t only changed the dependency list, but also the package’s source code, so it used the malicious library’s API. The consequence: you can’t trick the build with a simple redirect (override) to the real library — it breaks. The only clean path is pinning to the last genuinely-clean upstream versions, verified by hand.

That is the full vulnerability chain: substituted dependency → code executed at build under the radar → automated remediation that entrenches the problem → modified source that blocks the shortcuts.

How do you defend?

  1. Treat the lockfile as code. Pin exact versions and review any change to the dependency tree in the PR, not just your own code.
  2. Don’t run audit fix --force blindly. Check what version it proposes and whether it’s genuinely clean (npm view <package> dependencies) before accepting it.
  3. Software bill of materials (SBOM). You need to know which packages — including transitive ones — enter the build.
  4. Verify provenance (signatures/OIDC) and watch for unusual version jumps or dependencies with “almost right” names.
  5. Isolate CI. Runners that build execute third-party code; limit their access and rotate the credentials reachable from the build environment regularly.
  6. Prefer fewer dependencies and remove unmaintained components — every extra package is another link that can fail.

The takeaway

The software supply chain is the real attack surface of modern applications, and automated scans — however useful — don’t see everything, and sometimes send you back into the problem. A dedicated security audit examines the dependency tree, the lockfile and the pipeline with a critical eye, while a penetration test confirms what is genuinely exploitable. The difference between “I ran a scan” and “I understood the chain” is exactly the difference between fixing it and believing you fixed it.

Sources

Frequently asked questions

What is a dependency typosquat attack?

An attack in which a popular package is altered to pull in malicious libraries with names almost identical to the legitimate ones (e.g. `obug` instead of `debug`, `piccolore` instead of `picocolors`). On install, the dependency tree pulls them in automatically, and their code runs on every build — on developer workstations and CI runners.

Why is a dependency typosquat dangerous?

It runs at build time, a layer that antivirus and EDR rarely monitor; it is transitive (you didn't ask for the bad package, a dependency of a dependency did), so it escapes your "direct" list; and caret ranges (`^`) can automatically pull a new malicious version on the next install.

Why doesn't the automated "fix" solve it?

Because, in the documented case, `audit fix --force` proposed newer versions that still contained the typosquats — it reintroduced the problem. The attacker had also changed the package's source code, so a simple override to the real library breaks the build. The only clean path is pinning to the last genuinely-clean upstream versions, verified by hand.

How do I defend?

Treat the lockfile as code (pin exact versions, review dependency-tree changes in the PR), don't run `audit fix --force` blindly, keep an SBOM, verify provenance (signatures/OIDC), isolate CI and rotate build-reachable credentials, and prefer fewer dependencies.

Related articles