AI Security

Your AI just imported a package that doesn't exist

LLM assistants keep inventing plausible package names. Unclaimed, those names are an attacker's opportunity — slopsquatting is typosquatting's AI-era cousin, and most teams have no check for it.

Webcuris Research

Security Engineering

·4 min read

An AI-generated import of a package that does not exist, flagged in red

The pull request looked fine. Clean diff, sensible tests, and at the top of the new file, three imports:

ts
import { queue } from "async";
import { withRetry } from "smart-retry-utils";
import { backoff } from "exponential-backoff";

Two of those packages exist. One of them — smart-retry-utils — does not. It never has. An AI coding assistant wrote it, a developer skimmed it, and it read so plausibly that it nearly shipped.

That near-miss is worth understanding, because the failure mode behind it is quietly becoming one of the more interesting supply-chain risks of the AI era.

Why language models invent packages

A language model completing code is doing exactly what it does with prose: producing the most plausible next tokens. Package names have strong internal grammar — exponential-backoff is real, so smart-retry-utils feels real. The model has seen thousands of names shaped like it. It is not lying; it is rhyming.

Researchers who studied this measured that a meaningful share of packages suggested by code assistants simply do not exist in any registry — and, more interestingly, that the same invented names recur across sessions and users. The models hallucinate consistently. That consistency is what turns a nuisance into an attack surface.

Slopsquatting: the attack hiding in the autocomplete

Typosquatting is old news: register lodahs and wait for someone to fat-finger lodash. Slopsquatting is its AI-era cousin, and it is better in every way that matters to an attacker:

  • The victim doesn't make a mistake. Nobody mistypes anything. The name arrives in a diff, syntactically perfect, from a tool the team trusts.
  • The names are predictable. Because models hallucinate the same names repeatedly, an attacker can harvest them — prompt an assistant across common tasks, collect the invented imports, and register the ones that recur.
  • The payload runs at install time. A malicious package doesn't need anyone to call it. npm install scripts execute on the developer's machine, in CI, with CI's credentials.

Walk the timeline: an assistant suggests smart-retry-utils today and the build fails — annoying, visible, harmless. An attacker registers the name tomorrow with a working shim around a real retry library, plus an install script. Next month, the same assistant suggests the same name to someone else. This time the install succeeds. Nothing is visibly broken, which is the worst possible outcome.

The quieter sibling: phantom dependencies

There is a second, less dramatic version of the same disease. An import that works — but only by accident. The package exists, resolves today through hoisting or a transitive install, and is declared by nobody. Its version is pinned by nothing you control. It breaks when an unrelated dependency updates, and until then it sits invisible in every audit that reads package.json instead of reading the code.

AI assistants generate these constantly, because their training data is full of projects where the import happened to work. The model has no concept of your dependency tree.

What a real check looks like

The defense is unglamorous and completely mechanical, which is exactly what makes it automatable:

  1. Extract every import your code actually makes — from the source, not from the manifest. The manifest is the claim; the code is the truth.
  2. Diff against what is declared. Everything imported-but-undeclared is a finding, before any registry is consulted.
  3. Check undeclared names against the public registry. A name that is not there is either an invention or an internal package — and an unclaimed internal name is a dependency-confusion setup, which is its own emergency.
  4. Treat unclaimed names in generated code as blocking. Not a warning. The cost of being wrong is an attacker inside your install step.

Alongside that: install from lockfiles in CI (npm ci, never a bare install), consider --ignore-scripts where your stack tolerates it, and re-run the check continuously rather than once — the registry changes under you, which is the entire point of the attack.

The uncomfortable summary

AI assistants are writing a growing share of the world's code, and they introduce a category of dependency risk that no human workflow ever produced: imports that are wrong in a way that looks right, recurring predictably enough to be farmed. Code review was designed to catch human mistakes. This is not a human mistake.

Someone on your team should be reading your imports the way an attacker reads them: as a list of names, some of which are still unclaimed.


This check is built into [Webcuris](/) — every import in a connected repository, diffed against declarations and checked against the registry, on every scan. The [free tier](/pricing) runs the same engine as paid, and you can [scan one page free, no signup](/signup) to see the wider surface reading it does. This article is the write-up of why we built it.

Get the next one

One email when a new article goes out. No newsletter, no drip sequence, no sales follow-up.

Unsubscribe in one click. We never sell or share the address.

Keep reading

Everything this article describes is what Webcuris checks continuouslyscan one page free, no signup.

Contact

Talk to us.

Questions about what the engine checks, whether it fits your estate, or what it deliberately refuses to do. A person reads every message.

  1. 01You writePlain form, no qualifying call, no obligation. The marketing checkbox is optional and unticked.
  2. 02A person reads itMessages land with the team, not a queue-bot. Nothing is auto-replied.
  3. 03You get an answerTo the address you gave — including “this product is not the right fit”, when that is the honest answer.
Reporting a vulnerability?
Read the disclosure policy first — it tells you what is in scope and what to expect.
New messagereplies go to your email

We reply to this address, so a disposable one will not reach you.

+91

0 / 4000