Skip to content
← Blog

Technical explainer

The maintainer just changed. AI doesn’t notice.

3 min read

An npm package called event-stream was a normal utility for years. Then its original maintainer got tired and gave publish access to a stranger who'd offered to help. The stranger published a malicious version. Thousands of developers installed it.

That's 2018. The pattern hasn't changed. The maintainer change is the moment of risk, and it leaves a trail — if anyone is looking.

AI assistants don't look.

What "use the latest version" means to an LLM

When an AI coding assistant suggests npm install left-pad, the underlying model isn't looking at the npm registry in real time. It's relying on training data that's months to years old. The model knows left-pad is a popular utility. It does not know:

  • The maintainer's username changed three weeks ago
  • The npm publisher fingerprint differs between v1.3.0 and v1.3.1
  • The package was quiet for fourteen months before suddenly publishing v1.3.1 last Tuesday
  • The new maintainer's email domain is a domain registered in October

Those are the signals the recent hijacks (event-stream, ua-parser-js, colors, faker, node-ipc, getcookies) all share. They're not in the training data. The AI confidently suggests the latest version because, from its perspective, the package is still the trusted library it learned about.

The hijack pattern, end-to-end

This is the recipe:

  1. Attacker identifies a popular but under-maintained package. Bonus points if the original maintainer is responsive and friendly.
  2. Attacker offers to help — usually via a polite PR or an email saying "I have time, can I take some of this off your plate?"
  3. Original maintainer adds the attacker as a publisher.
  4. Attacker waits weeks or months. (This step matters; immediate publishing is suspicious. Patience is suspicious-resistant.)
  5. Attacker publishes a new version with malicious code (postinstall script, runtime payload, both).
  6. Every dependent project's npm install or yarn upgrade pulls the malicious version. Whatever credentials the install machine has are now the attacker's.

Step 4 is the key. It's why threat-feed-based defenses miss these — by the time a feed picks up the malicious version, half the targets have already installed it. The window from publish to detection is hours; by then attacker has what they wanted.

What catches it

The signal that fires reliably on hijacks is maintainer fingerprint drift between versions.

For each package version, you can compute a stable fingerprint from _npmUser and maintainers in the registry manifest. If the fingerprint changes between, say, v1.3.0 and v1.3.1, that's worth flagging. It doesn't mean the new version is malicious — maintainers can legitimately rotate — but combined with other signals (dormant period before the new release, no GitHub issue referencing the maintainer change, license unchanged, install scripts added that weren't there before), the score lands in "refuse."

Other signals that fire together:

  • Dormant-revival. Package was quiet for 6+ months then suddenly publishes a new version.
  • License change. Hijack toolkits often flip licenses to less restrictive options.
  • File-tree drift. The set of files in the tarball changed substantially between versions.
  • Lifecycle script delta. A postinstall was added that wasn't there before.

Each on its own is a yellow flag. Three or more is a red flag, and the gate refuses the install.

Where the AI fits

The AI keeps doing what it's good at: suggesting code, choosing libraries that solve your problem, scaffolding faster than you could type. It doesn't need to know about maintainer churn or dormant-revival. The check happens underneath the AI's recommendations.

When the AI suggests npm install left-pad and the latest version was published last week by a new maintainer after fourteen months of silence, the gate sees the pattern and refuses. Your terminal shows a clean error. You re-prompt, the AI tries a different library, or you pin to the last known-good version. Workflow unchanged; bad version never installed.

veln onboarding
veln wrapper on

The fix is invisible until it isn't. When the gate refuses, you get a clear message naming the signal that fired (maintainer changed, dormant-revival, install script added). Then you decide: pin to the previous version, switch libraries, or — rarely — override after reading the diff yourself.

The honest bit

This won't help against hijacks that are quick and don't change the maintainer (the original maintainer's account is compromised, attacker publishes immediately, original maintainer is asleep). The mitigations for that are different (cooling windows, attestation verification). But the slow-burn social-engineering hijack — the most common pattern in the recent corpus — has clear signals, and AI assistants don't watch for any of them. Something has to.