A social engineering attack targeting a Python developer through LinkedIn reveals how npm lifecycle hooks remain a practical code-execution risk in developer workflows.
Summary
- A fake recruiter on LinkedIn directed a developer to clone a repository containing a backdoor hidden inside a test file, designed to execute on npm install.
- The malicious payload was concealed using string fragmentation to defeat static analysis tools and triggered via a package.json ‘prepare’ lifecycle hook.
- The developer avoided compromise by running an AI coding agent in a read-only environment on an isolated VPS before touching the code locally.
- npm 12, expected next month, will default the allowScripts setting to off, removing automatic lifecycle script execution for dependencies.
- LinkedIn data shows accounts flagged after user reports grew from 86,000 in the first half of 2021 to 386,000 in the first half of 2025, reflecting a sustained increase in fake profile activity.
The approach
A person presenting as a recruiter for a small crypto startup contacted Python developer Roman Imankulov on LinkedIn, asking him to review proof-of-concept code for a lead engineer role. The request involved troubleshooting a deprecated Node module. Imankulov had a nagging sense something was not right. Having heard of similar attacks, he chose not to clone the repository to his own machine. Instead, he spun up a VPS on Hetzner and used a Pi coding agent running Codex to conduct a read-only analysis of the code before doing anything else.
What the AI found
Imankulov expected the agent to return a clean bill of health. It did not. Almost immediately, the agent flagged app/test/index.js and advised against running the code. Imankulov had already skimmed that file himself and found it unremarkable — sloppy, but nothing more. The AI identified what he had missed: a backdoor constructed from a fragmented server URL designed to look like test configuration, paired with a network request that would fetch and execute whatever the remote server returned. The deliberate fragmentation of the URL into small string constants was a specific countermeasure against static analysis tools that scan for hardcoded indicators of compromise.
The trigger mechanism
The attack did not require the target to run any explicit script. The repository’s package.json contained a ‘prepare’ lifecycle hook, meaning the malicious payload would execute automatically during npm install as part of routine dependency resolution. As independent security architect Devashri Datta explained to The Register, the adversary relied on the target running a command developers treat as second nature. “Developers run npm install on autopilot,” she noted. The technique is not new, but it continues to work precisely because it hijacks standard, trusted workflows rather than asking the target to do something obviously suspicious.
Manufactured credibility
The repository’s commit history appeared to belong to a real developer with an established online presence. Imankulov reached out to that developer, who confirmed he had been impersonated on GitHub more than once and had no connection to the code. The recruiter’s LinkedIn profile referenced a real person — an arts journalist — though Imankulov concluded the profile was fabricated, noting that the recruiter’s technical knowledge during their conversation was inconsistent with her stated work history. The sources do not identify who was behind the campaign, though The Register notes that North Korean-linked groups have been running similar fake interview and job offer operations targeting developers.
The LinkedIn context
LinkedIn has reported removing tens of millions of fake accounts before they interact with users, but a meaningful volume still reaches real people. In the first half of 2025, the platform restricted 386,000 accounts following user reports. That compares with 266,000 in the prior six-month period and 86,000 in the first half of 2021. The trend line is consistent with a growing volume of social engineering activity using the platform as an initial access vector.
A near-term fix in npm
GitHub, which maintains npm, is preparing to release npm 12 with a material change to default behaviour. The allowScripts setting will default to off, meaning npm install will no longer automatically execute preinstall, install, or postinstall lifecycle scripts from dependencies unless explicitly permitted. GitHub product manager Leo Balter described install-time lifecycle scripts as “the single largest code-execution surface in the npm ecosystem,” noting that a single compromised package anywhere in a dependency tree can currently execute arbitrary code on a developer machine or CI runner. Imankulov has already moved to pnpm, which does not execute those scripts by default.
Defensive AI at the endpoint
Datta observed that Imankulov’s approach represents a shift in how security-conscious developers are handling code review. Rather than manually inspecting untrusted code or relying solely on sandboxing guidance, he used an AI agent in a constrained, read-only environment before executing anything. “An AI agent isn’t susceptible to fatigue or social pressure,” she said, noting it surfaces anomalous behaviour — such as a test suite initiating an outbound network connection to retrieve unverified code — in seconds. The same quality that makes AI-assisted development productive, speed and pattern recognition at scale, applies when directed defensively at untrusted codebases.
Why it matters
This incident illustrates that the software supply chain attack surface now extends to individual developer endpoints, not just CI/CD pipelines or package registries. A developer’s workstation typically holds active SSH keys, cloud provider tokens, and live access to internal repositories. When a developer is compromised during what appears to be a routine job interview interaction, that access travels with the compromise. Security programs that focus supply chain controls at the enterprise perimeter or SBOM inventory level are missing the point of introduction. The threat has moved left to the moment a developer first touches untrusted code, and the controls need to follow.
What to do now
- Establish and communicate a policy requiring developers to evaluate untrusted or third-party code in isolated environments — dedicated VMs, containers, or secure cloud workstations — before any local execution.
- Track npm 12’s release and plan to adopt the default allowScripts=off behaviour across your development environment; in the interim, assess whether pnpm or equivalent tooling that disables lifecycle script execution by default is suitable.
- Include npm package.json lifecycle hooks (prepare, preinstall, postinstall) in code review checklists and automated scanning for any externally sourced repository.
- Brief engineering teams on LinkedIn-based recruiter social engineering patterns, specifically the fake job offer vector, and establish a clear internal reporting path when developers encounter suspicious outreach.
- Evaluate AI-assisted, read-only static analysis as a defensive layer for code review workflows involving external or unverified repositories, noting its effectiveness against obfuscation techniques that defeat simple indicator-of-compromise scanning.
