slopsquat-radar
Live registry of AI-hallucinated package names that have been registered as malicious slopsquats on npm and PyPI.
Roughly 20% of LLM-recommended package names don't exist on the target registry. When the same prompt is rerun ten times, ~43% of those hallucinated names appear on every run — meaning attackers can predict and pre-register them. This pattern is called slopsquatting, and confirmed cases (e.g. huggingface-cli on PyPI in 2024, react-codeshift on npm in January 2026) have already netted tens of thousands of downloads from real developers whose AI coding agents recommended the squatted name.
slopsquat-radar surfaces this attack surface as a public, searchable, live-refreshed dashboard, an RSS alert feed, and a JSON API any CI pipeline can call.
How it works
Two engines run on independent cron schedules:
- Probe engine (every hour at
:07): picks 5 realistic dev prompts × all configured cheap models, calls OpenRouter, extracts every package name suggested viarequire(…),import …,pip install …,package.jsonblocks, etc. Stores raw response and extracted (ecosystem,name) tuples. - Registry watch (every 2 hours at
:31, plus a daily long-tail rescan): for every observed package name, hits the registry to check whether it exists yet. Records first-publish timestamp, maintainer count, weekly downloads, readme length.
A simple classifier in lib/score.js then assigns each tracked package to one of: hallucinated (LLM suggested, registry 404s), live-safe (exists, was published before our first sighting), squat-suspect (published after first sighting + 2 signals), squat-confirmed (4+ signals), or unknown (not yet checked).
Data sources
All fetched live at runtime. No mock data, no seed packages.
| Source | URL pattern | Frequency |
| --- | --- | --- |
| OpenRouter chat | POST https://openrouter.ai/api/v1/chat/completions | hourly probe |
| npm registry | GET https://registry.npmjs.org/<name> | every 2h per package |
| npm downloads | GET https://api.npmjs.org/downloads/point/last-week/<name> | every 2h per package |
| PyPI JSON | GET https://pypi.org/pypi/<normalized-name>/json | every 2h per package |
| PyPI stats | GET https://pypistats.org/api/packages/<normalized-name>/recent?period=week | every 2h per package |
Bundled configuration (not data):
- config/prompts.json — 30 realistic developer prompts, ecosystem-tagged. This is what we ask; the answers (and therefore all tracked package names) are fetched live.
- config/models.json — five cheap OpenRouter model slugs (Llama 3.3 70B, Qwen 2.5 Coder 32B, Gemini Flash 1.5 8B, Mistral Nemo, DeepSeek V3.1).
- config/stdlib.json — Node.js + Python standard-library names used to filter false-positive extractions.
Endpoints (all under /slopsquat-radar, all unauthenticated)
GET /health— liveness + counters, what the orchestrator polls.GET /api/stats— top-line stats + last run.GET /api/leaderboard?ecosystem=&status=&limit=— most-hallucinated names.GET /api/squats?ecosystem=&since=&limit=— currentlysquat-suspectorsquat-confirmed.GET /api/models— per-model hallucination rate.GET /api/prompts— list seeded prompts with last-run times.GET /api/recent— last 100 probes.GET /api/package/:ecosystem/:name— full record + probe history.GET /api/check?ecosystem=npm&name=foo— public CI lookup. Add this to yourpre-installhook.GET /rss.xml— last 30 squat alerts as RSS.
Running locally
cp .env.example .env
# add OPENROUTER_API_KEY for the probe engine; without it, registry watch still
# runs on whatever data is in the DB.
set -a; source .env; set +a
npm install
node server.js
# → http://localhost:4765/slopsquat-radar/
better-sqlite3 requires a native build; it's prebuilt for macOS arm64 and most Linux distros via npm.
Engineering notes
- Stack: Node 18+, Express, better-sqlite3 (WAL), node-cron, helmet, compression, morgan. Nothing else.
- No auth anywhere — every endpoint, including
/healthand/api/check, is public. - No
Math.random()in data paths. The only randomness is OpenRouter'stemperature: 0.7so the same prompt produces varied completions across cron ticks. - Native fetch (Node 18+). No
node-fetchdep. - Graceful degradation: when
OPENROUTER_API_KEYis missing or__INJECT_FROM_VAULT__, the probe engine is paused and a yellow banner shows on the dashboard. Registry watch still operates on whatever names are already in the DB.
Related public research
- Aikido Security —
react-codeshiftsquat case study, January 2026. - Cloudsmith — typosquatting + slopsquatting detection patterns.
- Trend Micro — Slopsquatting: When AI Agents Hallucinate Malicious Packages.
- Mend.io — The Hallucinated Package Attack: Slopsquatting Explained.