Build1 publisher3 min readPublished
Existence checks are dead: attackers now register the packages your AI invents
A single-author npm tool measured what supply-chain guards actually verify, and found documented hallucinated names alive on the registry. Provenance beats resolution.
The Engineer · Build desk
Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction
What happened
- Three package names documented as AI hallucinations now resolve on the npm registry, which means every guard that verifies by existence passes all three.
- A 576,000 sample study put the rate at which AI coding tools suggest nonexistent package names at about 19.7 per cent.
- Of those hallucinated names, 43 per cent recur when the same prompt is run again, which is what makes pre-registration worth an attacker's time.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- constraint A gate that resolves a name and stops there can no longer separate attacker bait from a real dependency, leaving advisory and provenance lookups as the only discriminator available in the install...
- cost The price of a crude heuristic is the guard itself: flag one legitimate seven-year-old package and the developer removes the tool, which takes coverage to zero rather than to imperfect.
- decision Anyone running coding agents has to decide where the check lives, because a scanner a human must remember to invoke covers none of the installs an agent performs unattended.
- precedent Prefix completion rather than edit distance is now the rule other scanners will be judged against, since prefix-dropping is the failure mode models produce most.
The rule that catches the common case is not a distance measure at all. According to the author of vetdeps, the string `unused-imports` plus the ecosystem prefix `eslint-plugin-` resolves to a package with 7.8 million weekly downloads [5], which is exactly what a model emits when it drops the prefix. Levenshtein cannot see that: the two names sit fifteen edits apart [4]. To catch it on edit distance you would have to allow 15, seven and a half times the threshold of 2 that already misfired by calling `znv` a typo of `ajv` [13], two unrelated legitimate packages [6].
The rest of his measurement log is the useful part, because it is a record of rules that looked correct and were not. Affix rules that stripped a suffix flagged `chalk-cli` as `chalk`, a Sindre Sorhus package published in 2015, and flagged `vite-plugin-vue` as `vue` [9]. Those are not typosquats, they are how the ecosystem names things, and the fix was to allow rules that add an affix and never strip one [10].
Work the study numbers and you can see why pre-registration pays. Roughly 113,000 of the 576,000 sampled suggestions were names that did not exist [19], and if 43 per cent of those recur on identical prompts [15], then about one suggestion in twelve is a name an attacker can find by running the same prompt twice [20]. Registration costs nothing. The attacker does not need to guess what you will type; he needs to have already typed it.
Against that, the false-positive economics are brutal. `react-fetch-hook` reads like something a model made up and is seven years old with 15,000 weekly downloads [3]. Any heuristic keyed on novelty or odd-sounding names kills it, and a tool that flags a real dependency gets removed the same day. The author reports zero false positives on a real 550-package project [17]. That is one project, self-measured by the person shipping the tool, and nobody else has run it.
The distribution problem is the one most security tooling loses. Twelve packages already sit in this niche and the most successful has 202 weekly downloads [18], all of them scanners a human has to remember to invoke. So vetdeps puts itself in the path instead: a `preinstall` gate on every `npm install`, plus a Claude Code and Cursor hook that blocks the agent before the install runs [11]. That second one is the part that matters, since the thing typing the install command in 2026 is frequently not a person reading the name [16].
What such a gate can actually answer is a provenance question. `unused-imports` graduated from suspicious-sounding to confirmed malware under OSV advisory MAL-2025-48781 [7], `types-node` carries MAL-2024-12159 [c9b], and the tool's own sample finding is `@ctrl/tinycolor@4.1.1` against MAL-2025-47141 [12]. Those are lookups against advisory and reputation data, not against the registry's index. Resolution tells you an attacker did his job.
What to watch
- Whether anyone independent reproduces the zero-false-positive result on repositories other than the author's own 550 package project.
- Whether npm or the OSV feed starts flagging pre-registered hallucination names before an advisory exists, rather than after.
- Whether Claude Code and Cursor ship native install gating, which would make third-party agent hooks redundant.