twimco.Let’s talk ↗︎
Menu
Field notes / Product & engineering

When a package goes bad, the obvious first move can hurt.

This week a poisoned JavaScript package came with a trap. Revoke the key it stole, and it wipes the computer it's on. If your site or store runs on JavaScript, here's what to sort out now and what to do first next time.

In a bright, skylit loft office with white brick walls, a grey-haired woman in a mustard cardigan pulls the cable from a laptop on a standing desk set apart from the rest, a bearded colleague reads a single printed page, and a young woman at a separate desk works on another laptop beside an opened cardboard box and a cup of tea.

Don't start by revoking keys. Stop new installs, find every machine that ran the bad version, take those machines off the network and have your developer remove whatever the malware left running, then rotate every key they could reach from a different, clean computer. Before the next one, know which machines install packages and what they hold, and make brand-new versions wait a few days before your project takes them.

The reflex is to pull the keys

Picture a Thursday morning message from your developer: "One of the packages in our project got hacked. It may have stolen some of our keys."

If you own the business, your gut says one thing. Revoke the keys. Now. Every one of them.

This week that reflex would have made things worse. A worm hidden in a JavaScript package called tensorlake installed a small background job on computers where it found a GitHub key, with one purpose: check, every 60 seconds for up to a day, whether the key it had stolen still worked. The moment someone revoked that key, the job deleted everything in the user's home folder. The researchers at StepSecurity put it plainly: the obvious first move is what sets it off.

You probably don't write JavaScript, and you may never have heard of Tensorlake. But if your web app, online store or customer portal was built in the last few years, it very likely runs on a long list of small open-source packages pulled from npm, the public registry where JavaScript code is shared. Your developers install them, update them and ship them for you. This note is about the three things worth knowing before the next poisoned one: which machines install packages and what they can reach, how new versions get into your project, and the order of the first hour, because the order is the part this attack was built to punish.

What happened, without the jargon

Tensorlake makes sandboxes for running AI-written code safely, and its software kit for JavaScript developers gets roughly 12,000 downloads a week, by Socket's count. That figure is overall use, not how many people got the bad version. Nobody knows that yet. The Register's write-up on Thursday said the impact is unknown.

Here is the sequence, from StepSecurity's and Socket's reports. Someone with access to a maintainer's account pushed eight changes straight into the project's main code over a few hours, about a day before the release, none of them reviewed. One of them added a line telling the package to run a setup script the moment anyone installs it. On the evening of October 7, US Eastern time, the project's own release process published version 0.5.144 with that script inside. Socket's scanner flagged it about 11 minutes later, and it was pulled within hours; 0.5.145 is the clean replacement, and 0.5.143 is the last clean version before it.

Three details matter for anyone who pays for software rather than writes it.

It ran on install. Nobody had to use the package. Installing it was enough, and the script ran with whatever access the installing computer had. Socket's phrase is that install code "inherits the permissions of the installing process."

It went for laptops. StepSecurity found the script skips itself on automated build servers, so developer machines were the real target. That's where the good stuff lives: GitHub and npm keys, cloud keys, SSH keys, saved browser passwords, and the settings files of AI coding tools.

It looked legitimate. Because it was built from the project's real code by the project's real release process, it carried a valid provenance stamp. That stamp says where a package was built. It doesn't say the code is safe.

Then it spread. With a stolen npm key it republished the victim's own packages with itself inside. With a stolen GitHub key it slipped files into the victim's projects under the message "chore: update dependencies," so the worm runs again when someone opens that project in VS Code or Claude Code.

Decision guide

The reflex and the order that works

ConsiderationThe reflexWhat to do instead
First moveRevoke every key right away.Stop installs and find affected machines; revoke only after the trap is removed.
The infected laptopKeep working on it and clean it later.Take it off the network, back up what matters, treat it as compromised.
Where you rotate fromThe same laptop, since it's already logged in.A different, clean computer.
Removing the packageUninstall it and assume you're done.Remove what it left running, check for spread, rebuild if unsure.
Decision framework drawn from the Tensorlake advisories; check your own setup with your developer. Illustrative, not client data.
In a breach, the right steps in the wrong order can do as much damage as the attacker.

The order that works

GitHub's advisory is blunt: treat any computer that installed the bad version as fully compromised, and rotate every secret it held from a different computer. Socket and StepSecurity add the part that saves the laptop: get rid of the background job before you revoke anything. Put together, the first hour looks like this.

Stop new installs. Pin the package to a known clean version so nobody, human or build server, pulls the bad one again while you work.

Find every place it ran. Socket's list is the right one: the project's dependency files and lockfiles, build logs, and what's actually deployed. Then ask about people. Who ran an install on their own laptop this week, including the contractor?

Take those machines off the network, and leave the keys alone for now. Back up anything on them you can't lose.

Remove the trap. Your developer stops and deletes the token monitor and anything else the malware set to start on its own. Both research reports publish the exact steps.

Now rotate, from a clean computer. The stolen GitHub key first, since it's safe at this point. Then npm keys, cloud keys, SSH keys, database and payment secrets in settings files, AI tool keys, and browser-saved passwords on that machine.

Look for spread. New repositories in your GitHub account, commits you don't recognise, new files in .claude or .vscode folders, new versions of any package you publish.

Rebuild if you're not sure. If nobody can say the machine is clean, wipe it and start again.

None of this needs the owner at the keyboard. It needs someone who already knows the answers to two questions you can settle this month.

How the work moves

The first hour, in order

  1. Stop new installs

    Pin to a known clean version on every machine and build.

  2. Find where it ran

    Lockfiles, build logs, deployed code, and every laptop that ran an install.

  3. Isolate, don't revoke

    Take those machines off the network; back up what you can't lose.

  4. Remove the trap

    Delete the token monitor and anything else set to start on its own.

  5. Rotate from a clean computer

    GitHub key first, then npm, cloud, SSH, payment, AI tool keys and saved passwords.

  6. Check for spread, then rebuild

    Unknown repos, commits and package versions; wipe any machine nobody can vouch for.

Illustrative sequence based on GitHub, Socket and StepSecurity guidance for [email protected]; not a measured result.

Know which machines install code and what they hold

Ask your developer to list every place that runs an install for your project. It's usually a short list: each developer's laptop, a contractor's laptop, the build service that tests and deploys your code, and sometimes a production server that installs packages when it starts. Next to each, write down which keys live there.

What you're looking for is the laptop that can do everything. In a lot of small teams one person's computer holds the GitHub key with admin rights, the key that deploys to production, the cloud account login and the payment provider's secret key, all because that was the easy way to set it up years ago. That's the machine this worm wanted, and it's the one you least want to wipe in a hurry.

You don't need to fix it all at once. Move deploy keys to the build service so laptops don't need them. Give laptops keys that can read and push code but not publish packages or touch production. Turn on the setting that stops package install scripts from running by default (npm calls it ignore-scripts), and let your developer allow the few that genuinely need them.

And practise rotation before you need it. If nobody has ever rotated your payment or cloud keys on purpose, the first time shouldn't be in the middle of an incident. We've written up how to rotate a credential like a planned cutover, with both keys valid for a short window and a named person who can roll back.

Decide how new versions get in

The bad Tensorlake version was flagged within minutes and pulled within hours. A project that waited even a day before taking brand-new versions would never have installed it. That's the cheapest protection on this list.

Three habits do most of the work.

Lock the versions. Your project should have a lockfile, a file that records the exact version of every package, so every machine installs the same thing and nothing changes without someone noticing. Ask whether yours is checked in and whether the build uses it strictly.

Wait before adopting. npm, pnpm and Yarn now each have a setting that refuses versions younger than a set age. A few days is a sensible default. Your developer can make an exception for an urgent security fix, on purpose and in writing.

Review updates like code. Updates should arrive as a reviewed change, not as something that happens quietly when someone runs an install. That's also where you'd notice a surprise commit called "chore: update dependencies."

None of this makes you immune. A compromised package that sits quietly for a week would get past a three-day wait. But it moves you from taking whatever was published five minutes ago to taking what the rest of the world has already had a look at.

Three questions for your developer on Monday

You don't need to understand npm to run this conversation. You need three answers, written down.

  1. Which machines install packages for our project, and which keys does each one hold? If the answer is "my laptop has everything," that's the first fix.
  2. How does a new version get into our project? Listen for a lockfile, a waiting period for brand-new releases, install scripts off by default, and updates arriving as reviewed changes.
  3. If a package we use is compromised tomorrow, what do we do in the first hour, and in what order? The answer should start with stopping installs and finding affected machines, not with revoking keys. If it starts with revoking, send them this note.

If those answers come back thin, that's normal. Most small teams have never been asked. It's the kind of groundwork we do as part of building and looking after web applications: mapping where code is installed and what it can reach, tightening how updates come in, and writing the first-hour order down before anyone needs it. Tell us what you run and we'll start with the three questions.

Related work: Sutton|Place, where deciding who can see and do what came before automating any of it.

twimco.

We build alongside the people doing the work.
Bring us the problem you’re thinking about.

Related work

Sutton Place ↗︎