← Compendium

Why “we scan after the git commit” is not enough for supply chain security

A compromised npm package does not become dangerous when it lands in the repository, but the moment a developer installs it locally. Anyone who takes the supply chain seriously should therefore not ask “Did we scan?” but “Can we determine our blast radius within minutes?”, and that is an organisational question, not merely a tooling one.

Where the attack really begins

Most security processes start where code leaves the team: at the commit, at the merge, in the pipeline. The focus is on what moves into the repository and on to production. With supply chain attacks, exactly this order is misleading, because a manipulated package takes effect one station earlier.

The critical moment is the local `npm install` on a developer’s machine, long before a line of code is committed. Some packages bring an install script that runs automatically while they are being unpacked, so foreign code runs before anyone even uses the library. A scanner that only kicks in after the commit sees none of this.

The install script is only the earliest trigger, not the only danger. The actual package code can also be rigged as a credential stealer and strikes as soon as the library is imported and executed. Such a package runs in the same process as your own program and may in principle do everything the developer or the running service may do, because a language runtime like Node does not, by itself, separate trusted code from code you pulled in.

What a malicious package takes with it

Once this code runs, it has access to everything the developer has. It reads local tokens, SSH keys, cloud credentials, active browser sessions, `.env` files and GitHub access and sends them to the attacker, while the actual build or program run looks completely normal to the developer. This affects not only the pipeline: the workstation itself becomes part of the attack path.

This trust decision is currently getting thinner rather than stricter. With vibe coding, for many developers it is no longer made by a human today, but by a language model that decides, based on a package description, to install and integrate a dependency. Where someone used to at least take a quick look at the name, origin and adoption, the assistant is often satisfied with a plausible description, so a convincingly disguised package finds its way onto the machine without a human ever consciously approving the installation.

A clean separation between development and production remains important, but it is not enough, because the loot on the developer machine is valuable even without production credentials. Even if production access is cleanly separated, source code can leak, internal repositories can be read, and the dev credentials are enough to manipulate your own packages, CI configurations or repositories.

The central question is therefore no longer whether a library is currently running in production, but which library in which version was installed on which developer machine, and when.

s1ngularity, step by step

How this plays out in practice was demonstrated by the s1ngularity attack in August 2025, and it is worth walking through it once. It did not start with a spectacular break-in, but with a vulnerability in a GitHub Actions workflow of the Nx project, through which attackers obtained the npm publishing token.

With this token they published manipulated versions of several Nx packages on npm for around four hours. Anyone who installed during this window ran a script that searched the local machine for credentials and uploaded the finds to publicly visible GitHub repositories under the victim’s account.

The extent only became visible afterwards: according to the analysis of several security researchers, around 2,349 credentials were exposed this way, including GitHub and npm tokens, SSH keys and cloud and AI credentials. Some of them were still valid after the affected repositories had already been cleaned up, because a clean-up without rotation does not invalidate the stolen keys.

When the package spreads itself

A few weeks later the pattern escalated. In September 2025, a self-replicating worm called “Shai-Hulud” appeared, which did not manipulate a single package but used the stolen credentials of a compromised maintainer straight away to infect their other packages. The US agency CISA reported more than 500 affected npm packages, and in a second wave in November 2025 it was again hundreds of packages along with tens of thousands of GitHub repositories within a few hours.

The difference from a classic attack is decisive: a worm does not wait for manual distribution, but jumps automatically from one package to the next as long as it finds valid credentials. That shortens the time a manipulated package is in circulation and increases the number of those affected, while the window for a response keeps shrinking.

The real question: the blast radius

If it becomes known tomorrow that a package version was manipulated for just a few hours, it is not the scanner that determines how well you are placed, but the ability to determine your own blast radius. That means the question of how far the damage reaches: did anyone on the team install this version, which secrets were on the affected machine, and were pushes, releases or CI runs triggered with them afterwards?

Only the answer to that decides whether a patch is enough or whether you have to rotate, revoke, investigate forensically and treat a repository as potentially compromised. This question is organisational in nature, because it requires that somewhere it is reliably recorded who pulled which dependency when, even if no commit ever came of it.

Why scanning is only one layer

This also shifts the role of the CVE. A CVE is not the moment a vulnerability comes into being, but the moment an already existing flaw is publicly documented. From then on, considerably more people know about it and ready-made exploits become reproducible faster, which is why response speed depends less on the tool than on knowing what you use and where.

NIS2 and ISO 27001 only help here if they do not stop at paper, but make risks visible, clarify responsibilities, build asset and dependency inventories and rehearse incident processes. Code review and scanning in the CI/CD pipeline remain useful, but only cover one layer, because they do not answer the local install or the question of the blast radius.

What this means in practice

Anyone who takes the supply chain seriously therefore combines several levels instead of relying on the scanner. On the technical side, that includes hardened developer machines, separate credentials and short-lived tokens, control over lockfile and registry, software inventories down to the dependencies, and detection that notices when a machine shows unusual access. On the organisational side, you need clear runbooks that allow access to be rotated and revoked quickly in an emergency.

The core of all this is visibility into what was actually pulled in, and that is exactly what we are working on with our product Driftguard, which is currently in a closed beta. On the roadmap is an npm proxy that lets you determine the blast radius, that is trace who loaded which package in which project, even without a git commit, and at the same time control which packages and versions are approved at all.

If you want to take an honest look at your current security situation, we will look at it together, pragmatically, hands-on and without buzzword bingo. The next step usually does not start with a big project, but with making your own blast radius visible in the first place.

Frequently asked questions

Isn’t a scan in CI/CD enough?

No. A compromised package already takes effect at the local `npm install` through its install script, or at the latest when the package code is imported and executed, that is before code ever reaches the repository. Scanning is a useful layer, but it does not answer who installed which version when.

What does “determining the blast radius” mean?

Being able to answer quickly who on the team had installed an affected package version, which secrets were on that machine and whether pushes, releases or CI runs happened with them. Only that decides whether you just patch or have to rotate, revoke and investigate forensically.

What was the difference between s1ngularity and Shai-Hulud?

With s1ngularity (August 2025), attackers used a captured publishing token to publish manipulated Nx packages for a few hours and harvested around 2,349 credentials in the process. Shai-Hulud (from September 2025) was a self-replicating worm that used stolen credentials to infect further packages automatically, hitting hundreds of packages.

What does vibe coding change?

The trust decision about a dependency is increasingly made not by a human, but by a language model based on the package description. The quick human check of name, origin and adoption often disappears, and a convincingly disguised package lands on the machine without anyone having consciously approved the installation. That makes approval rules for packages and a determinable blast radius more important, not less.

Do NIS2 and ISO 27001 help with this?

Only if they do not end up as paper. Used properly, they create asset and dependency inventories, clear responsibilities and rehearsed incident processes, which is exactly what determines response speed in an emergency.

Related topics