An npm supply chain attack does not arrive through the library your developers chose. It arrives through the fourteenth package that library installed on its own. On August 4, 2026, an attacker took the GitHub account of the maintainer behind keyv, a caching library carrying roughly 127 million weekly downloads, and pushed credential stealing code across that maintainer’s whole portfolio. The worm reached 444 packages and 2,236 package versions within hours. Most firms affected had never heard of keyv and never typed its name anywhere. We have spent the weeks since walking client dependency trees, and the three risks below are the ones that keep turning up in businesses that do not think of themselves as software companies at all.
The Five Things to Settle About Your Dependency Tree
If you commission custom software, run a customer portal, or have a developer maintaining an internal tool, these five points decide how exposed you were in August and how exposed you are next time.
- You install far more packages than you selected. A modest web application pulls several hundred transitive dependencies. Your team picked perhaps a dozen of them.
- The malicious code ran before your application did. The keyv payload sat in a preinstall hook, so it executed during installation, not at runtime.
- Your build server was the target, not your website. The payload harvested cloud and continuous integration credentials, which is where the real damage compounds.
- A stolen publishing token turns one compromise into hundreds. The worm republished trojanized versions of every package the stolen token could reach.
- Lockfiles only help if you actually honour them. A build that resolves fresh versions on every run discards the protection the lockfile was written to give.
For the wider pattern behind incidents like this, our guide to supply chain attacks for small businesses covers how the category works before you get to the mechanics below.
Risk One: The npm Supply Chain Attack Runs at Install Time, Not Run Time
The keyv payload executed inside a preinstall lifecycle hook, which means it ran the moment a dependency was installed and never needed your application to start. This is the detail that made August different, and it is the one most of the coverage skipped. A preinstall hook is a script npm runs automatically before a package is unpacked into place. It is a legitimate feature used for compiling native code and fetching platform binaries. In this case the hook, a file named setup.mjs, downloaded a standalone Bun runtime, executed an obfuscated second stage, and began collecting credentials. Our earlier write up on what the npm compromise meant for wallet users covered the consumer angle. The business angle is narrower and worse.
Why Runtime Controls Never Saw It
Controls that watch running applications missed this attack because the code had already finished by the time anything was running. Your web application firewall inspects requests to a live service. Your endpoint tool watches process behaviour on workstations. Your vulnerability scanner compares installed versions against a list of known bad ones, and for several hours there was no list. The install happened on a build agent, ran for a few seconds, and exited. There is a fair objection to raising the alarm about this. Install time execution has existed for as long as the package manager has, most of it is benign, and a business cannot reasonably audit every lifecycle script in a tree of four hundred packages. We agree with the premise and not the conclusion. You cannot read every script, but you can stop running them, which is a single configuration change covered below.
The Fix Is a Flag, Not a Project
Disabling lifecycle scripts during installation removes this entire attack path, and for most business applications nothing breaks. Run your installs with scripts ignored, then allow them back individually for the handful of packages that genuinely compile something. Modern package managers support this directly, and the allowlist for a typical business application runs to two or three entries. The counterargument deserves space: teams that build native extensions or image processing tooling will find the allowlist longer, and some frameworks assume hooks run. That is a real cost for a minority of projects. For the internal tool, the portal, and the integration script that describe most of our clients’ software, the flag costs nothing and closes the door the August payload walked through.
Risk Two: Your Build Pipeline Holds More Credentials Than Your Laptop
Continuous integration systems hold the credentials that matter most, which is why an npm supply chain attack aims there rather than at a developer’s machine. Think about what sits in the environment of your build server. Cloud deployment keys. Database connection strings for a staging environment that was cloned from production. A container registry password. Possibly a publishing token for your own packages. The keyv payload went looking for exactly this set, and the reason the incident spread to 444 packages is that it found npm tokens and used them to republish. Our note on supply chain ransomware and vendor risk covers the same dynamic from the vendor side, and the cost breakdown of supply chain attacks puts numbers on what recovery actually runs to.
Separate the Job That Installs From the Job That Deploys
Splitting installation and deployment into separate pipeline stages with separate credentials means a compromise during install cannot reach your deployment keys. This is standard practice in mature engineering organisations and almost absent in the businesses we assess. The install stage needs network access to a package registry and nothing else. It does not need a cloud key, a database password, or a registry credential, yet it usually inherits all of them because the pipeline was written as one job with one environment block. Separating them is an afternoon of work. The argument against is honest enough: two stages are harder to debug, artifacts have to move between them, and a small team feels the friction daily while the benefit is invisible until the day it matters. We still recommend it, because the alternative is that every package your build installs runs with the same authority as your deployment.
Scope and Expire Every Token You Issue
A publishing token with write access to every package you own is the difference between one bad install and a worm, so scope narrowly and set expiry dates you actually honour. Granular tokens limited to a single package, read only tokens where publishing is not needed, and short lifetimes all reduce what a theft is worth. Rotate after any incident anywhere in your dependency tree, not just after one involving you. The opposing view is that token hygiene generates busywork and broken builds at 2am when something expires unnoticed, and that complaint is legitimate in a team without alerting on expiry. Our position is that the failure mode of a short token is an outage you fix in ten minutes, and the failure mode of a long one is a stranger publishing code under your name.
Risk Three: Nobody Can Tell You What You Are Actually Running
Most businesses cannot answer the question that mattered most on August 4, which was simply whether keyv appeared anywhere in their software. That question should take a minute and usually takes a week of asking developers and waiting. A software bill of materials, which is a machine readable list of every component in a build, is what turns that week into a query. Generate one on every build, store it with the build artifact, and you can answer any future version of the question immediately. Firms in defence supply chains are already being asked for these, as our note on defence supply chain security and the common mistakes we see both cover.
An Inventory You Cannot Query Is Not an Inventory
A bill of materials filed as a document nobody can search gives you the paperwork of an inventory without the benefit, and that is the state we find most often. The value is in being able to ask which builds, across which applications, across which dates, contain a given package at a given version range. That requires the files to land somewhere queryable and to be generated automatically rather than at audit time. There is a counterpoint worth stating plainly. Generating and storing these adds pipeline time and storage cost for something that pays off only during an incident, and plenty of firms have never had one. That is the same argument made about backups, and it resolves the same way. The cost is small, continuous, and boring. The benefit is concentrated in the worst week you will have.
Pin Versions, and Make Your Build Prove It
Installing from a lockfile with a command that fails rather than silently updating is what makes pinning real, because a build that quietly resolves newer versions has thrown the lockfile away. Use the strict install command your package manager provides, the one that refuses to proceed when the lockfile and the manifest disagree. Then a compromised version published after your lockfile was written cannot enter your build without a visible change to a file in your repository. The fair objection is that strict pinning slows security patching, since every update becomes a deliberate act rather than something that happens on its own. That is true, and it is the trade we recommend making. Automatic updates are how a malicious version reaches you in the first place, and the attack chain behind those incidents is the same one our piece on how ransomware attacks start traces from initial access onward.
What the First Hour Should Look Like Next Time
The first hour of the next compromise is spent either answering questions or discovering you cannot, and which one depends entirely on work done beforehand. We run clients through the same short sequence. Search every lockfile in every repository for the named packages and version ranges, and record the result even when it is negative, because a documented negative is what stops the question being asked again on Thursday. Pull the build logs for the window the advisory names and look for installs that ran during it, since an install that never happened cannot have executed anything. Rotate any credential that was present in the environment of a build during that window, on the assumption it was read rather than on evidence that it was. Check whether any package you publish was republished in that period. Then write down what you found and when, because the same questions arrive from a customer or an insurer weeks later in a form that needs dates.
None of that is sophisticated, and that is the point. The firms that handled August badly were not missing tooling, they were missing the ability to search their own lockfiles and to say which builds ran when. The counterargument we hear is that rotating credentials on suspicion rather than evidence causes needless outages, and there is something to it: a rotation during business hours on a system nobody documented can break a deployment path that had been working for a year. Our answer is that the outage is recoverable within the hour and a leaked cloud key is not, and that a rotation which breaks something has told you about a dependency you did not know you had. Treat the breakage as a finding rather than a cost.
Frequently Asked Questions
Were we affected if we never installed keyv directly?
Possibly. The compromised packages included flat-cache and file-entry-cache, which arrive as transitive dependencies of common developer tooling. Check your lockfile for the package names rather than your manifest, because the manifest lists only what you chose.
Does an npm supply chain attack affect our website visitors?
Usually not directly. This payload targeted build and continuous integration credentials rather than site visitors. The risk to customers comes later, through whatever the attacker does with the cloud access it collected.
Is disabling lifecycle scripts safe for our application?
For most business applications, yes. Disable them by default, then allow them back for the small number of packages that genuinely compile native code. Test in a branch first, since a package that needs a hook will fail loudly rather than quietly.
How often should we rotate our package publishing tokens?
Set a short expiry and rotate on schedule, then rotate again after any compromise in your dependency tree even if your own packages were untouched. A stolen token is what turned this incident from ten packages into 444.
Do we need a software bill of materials if we do not sell software?
If you commission or run custom software, yes. It is not a compliance artifact so much as the only fast way to answer whether a newly disclosed compromise touches you.
Who You Are Taking This Advice From
Mindcore has spent years securing the software estates of businesses that do not consider themselves software firms, which is where dependency risk sits unexamined for the longest. We audit build pipelines, split credentials out of monolithic jobs, and put inventory in place that answers a question in a minute rather than a week, and the guidance above comes from pipelines we have actually opened rather than from a vendor briefing. Matt Rosenthal, who leads Mindcore, keeps the firm pointed at controls a small team can run without a dedicated security engineer, which for supply chain work means configuration changes and inventory rather than another monitoring product to watch. Your developers know your software. Our job is to make the parts they did not choose visible to them.
Your Next Move on Dependency Risk
Start with the question you could not answer in August. Pull your lockfiles, search them for the package names from that incident, and note how long it took you to get an answer. If it took more than a few minutes, that is the finding, and it will be the same finding during the next compromise. Then make the three changes in order of how little they cost: turn off lifecycle scripts by default, split your install stage away from your deployment credentials, and switch your build to the strict install command that honours the lockfile. None of those is a project. All of them would have narrowed what August reached. The firms that came through this quietly were not the ones with the most tooling, they were the ones who could see their own dependency tree. If you want someone to walk that tree with you and tell you where your build is trusting code nobody chose, book a free strategy call and we will start with your lockfiles.


