{"slug": "that-linkedin-job-offer-hid-a-backdoor-in-npm-install", "title": "That LinkedIn Job Offer Hid a Backdoor in npm install", "summary": "A developer received a LinkedIn message from a recruiter at a crypto startup asking to review a GitHub repository before a technical interview. The repository contained a backdoor in npm's prepare script that executed on npm install, part of a North Korean state-sponsored campaign tracked as Sapphire Sleet. The attack has spread 1,700 malicious packages across multiple ecosystems since December 2022.", "body_md": "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`\n\n, they passed the repo to a read-only AI agent for a first look. The AI found it immediately.\n\nBuried in `app/test/index.js`\n\n— disguised as 250 lines of test boilerplate — was a backdoor. The moment `npm install`\n\nran, 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](https://roman.pt/posts/linkedin-backdoor/) — it hit 629 points on Hacker News within hours.\n\n## The Mechanism: What `prepare`\n\nActually Does\n\nMost developers know to be careful about running `npm install`\n\non random packages. Almost nobody audits the root `package.json`\n\nbefore installing an unfamiliar repo. That’s the gap attackers exploit.\n\nnpm’s `prepare`\n\nlifecycle script runs automatically on every `npm install`\n\n. No flags, no confirmation. The malicious `package.json`\n\nin Imankulov’s case looked like this:\n\n```\n{\n  \"scripts\": {\n    \"prepare\": \"node app/index.js\",\n    \"app:pre\": \"node app/test/index.js\"\n  }\n}\n```\n\nThat’s it. `npm install`\n\nexecutes `node app/index.js`\n\n, 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.\n\nA second developer [described a nearly identical scenario on DEV Community](https://dev.to/vladimirnovick/a-linkedin-recruiter-sent-me-malware-disguised-as-a-pre-interview-code-review-2k3j), 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)`\n\ninstead of `eval()`\n\nto evade static analysis tools. The payload targeted environment variables — API keys, AWS credentials, tokens — everything a developer has loaded in their shell.\n\n## Why It’s Convincing\n\nThe 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`\n\n.\n\nThis isn’t phishing. It’s social engineering with operational precision.\n\n## The Industrial Scale Behind It\n\nMicrosoft tracks this campaign as Sapphire Sleet — a [North Korean state-sponsored threat actor](https://www.microsoft.com/en-us/security/blog/2026/04/16/dissecting-sapphire-sleets-macos-intrusion-from-lure-to-compromise/) 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.\n\nThe 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.\n\n## What to Do Right Now\n\nThe most effective immediate action: use `npm install --ignore-scripts`\n\nwhen reviewing unfamiliar repositories. This prevents all lifecycle scripts — `prepare`\n\n, `postinstall`\n\n, `preinstall`\n\n— from running. Make it permanent:\n\n```\nnpm config set ignore-scripts true\n```\n\nBefore installing anything from an unknown source, open `package.json`\n\nand check the `scripts`\n\nfield. Any entry running a file from the project’s own source tree — especially under `prepare`\n\nor `postinstall`\n\n— 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.\n\nThe longer fix is arriving. [npm v12, shipping in July 2026](https://github.blog/changelog/2026-06-09-upcoming-breaking-changes-for-npm-v12/), flips `allowScripts`\n\nto `false`\n\nby default. Lifecycle scripts will require explicit approval via `npm approve-scripts`\n\n. We’ve covered [what npm v12 breaks and how to prepare](https://byteiota.com/npm-v12-breaking-changes-what-breaks-in-july-2026/) — the recruiter backdoor is exactly the attack this change is designed to stop.\n\nUntil 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`\n\nfirst. Run in a sandbox. Ask why they’re specifically pushing `npm install`\n\n.\n\nThe answer is usually in that `prepare`\n\nscript.", "url": "https://wpnews.pro/news/that-linkedin-job-offer-hid-a-backdoor-in-npm-install", "canonical_source": "https://byteiota.com/linkedin-npm-backdoor-job-offer/", "published_at": "2026-06-16 01:09:37+00:00", "updated_at": "2026-06-16 07:24:12.721796+00:00", "lang": "en", "topics": ["ai-safety", "ai-agents", "developer-tools"], "entities": ["Roman Imankulov", "Microsoft", "Sapphire Sleet", "LinkedIn", "npm", "GitHub", "Axios", "Hacker News"], "alternates": {"html": "https://wpnews.pro/news/that-linkedin-job-offer-hid-a-backdoor-in-npm-install", "markdown": "https://wpnews.pro/news/that-linkedin-job-offer-hid-a-backdoor-in-npm-install.md", "text": "https://wpnews.pro/news/that-linkedin-job-offer-hid-a-backdoor-in-npm-install.txt", "jsonld": "https://wpnews.pro/news/that-linkedin-job-offer-hid-a-backdoor-in-npm-install.jsonld"}}