A developer received a LinkedIn message last week from a recruiter at a crypto startup. Normal enough. The ask: review a GitHub repository before the technical interview — “just check for deprecated Node modules.” The developer opened the repo. Nothing obvious jumped out. Then, before running npm install
, they passed the repo to a read-only AI agent for a first look. The AI found it immediately.
Buried in app/test/index.js
— disguised as 250 lines of test boilerplate — was a backdoor. The moment npm install
ran, it would execute. No test command needed. Just install. The payload assembled a URL from string fragments, phoned home to an attacker’s server, and ran whatever code came back. The developer, Roman Imankulov, published the full account today — it hit 629 points on Hacker News within hours.
The Mechanism: What prepare #
Actually Does
Most developers know to be careful about running npm install
on random packages. Almost nobody audits the root package.json
before installing an unfamiliar repo. That’s the gap attackers exploit.
npm’s prepare
lifecycle script runs automatically on every npm install
. No flags, no confirmation. The malicious package.json
in Imankulov’s case looked like this:
{
"scripts": {
"prepare": "node app/index.js",
"app:pre": "node app/test/index.js"
}
}
That’s it. npm install
executes node app/index.js
, which loads the test file, which assembles the C2 URL, which runs the second-stage payload. The attack is buried three files deep — none of which look alarming in isolation — and triggered by the most routine command in Node.js development.
A second developer described a nearly identical scenario on DEV Community, with a more sophisticated variant: the C2 endpoint was hidden inside a public Google Doc, letting the attacker rotate the destination server without touching the repository. It used new (Function.constructor)("require", code)
instead of eval()
to evade static analysis tools. The payload targeted environment variables — API keys, AWS credentials, tokens — everything a developer has loaded in their shell.
Why It’s Convincing #
The repository in Imankulov’s case had 39 commits, all attributed to a real full-stack developer who confirmed he’d never worked on the project. The recruiter profile belonged to a real arts journalist, impersonated with technically-worded messages. When Imankulov suggested reading the code before installing, the recruiter pushed back — specifically steering him toward npm install
.
This isn’t phishing. It’s social engineering with operational precision.
The Industrial Scale Behind It #
Microsoft tracks this campaign as Sapphire Sleet — a North Korean state-sponsored threat actor also known as Contagious Interview, active since December 2022. In April 2026 alone, the operation spread 1,700 malicious packages across npm, PyPI, Go, and Rust. In March 2026, Axios itself was compromised — versions 1.14.1 and 0.30.4 delivered a cross-platform RAT to developers who had nothing to do with any job interview. They just used a popular library.
The LinkedIn recruitment variant is one branch of the same tree. Individual developer targeting, package ecosystem poisoning, maintainer account compromise — all converging on the same goal: get into a developer’s machine, exfiltrate credentials, establish persistence.
What to Do Right Now #
The most effective immediate action: use npm install --ignore-scripts
when reviewing unfamiliar repositories. This prevents all lifecycle scripts — prepare
, postinstall
, preinstall
— from running. Make it permanent:
npm config set ignore-scripts true
Before installing anything from an unknown source, open package.json
and check the scripts
field. Any entry running a file from the project’s own source tree — especially under prepare
or postinstall
— is worth tracing before you touch install. For repos from unknown sources, use a throwaway environment: a Docker container, a VM, or a remote VPS. The attack only executes locally. Reading code on GitHub is safe.
The longer fix is arriving. npm v12, shipping in July 2026, flips allowScripts
to false
by default. Lifecycle scripts will require explicit approval via npm approve-scripts
. We’ve covered what npm v12 breaks and how to prepare — the recruiter backdoor is exactly the attack this change is designed to stop.
Until July, the rule is simple: any LinkedIn recruiter asking you to clone and install a repo before a technical interview should be treated as untrusted input. Read the package.json
first. Run in a sandbox. Ask why they’re specifically pushing npm install
.
The answer is usually in that prepare
script.