AI coding agents hallucinate package names, and attackers register them. GhostDep checks every package before your agent installs it — inside IBM Bob, in the terminal, and in CI.
The live catch
On 25 Sep 2026, GhostDep returned BLOCKED for reqeusts — a package
first published three days earlier with five downloads, closely resembling
requests. By the following day it was no longer on PyPI.
reqeusts
BLOCKED
requests
reqeusts no longer available on PyPI
REMOVED
Radar
reqeusts was spotted by chance. The Radar makes it systematic: every
30 minutes, GhostDep sweeps PyPI's feed of newly created packages and flags names
that imitate one of the 1,000 most-downloaded packages.
Lookalikes are possible impersonations, not proof of malice; each one needs review. Sweeps are scheduled by GitHub Actions and can run a few minutes late. Raw data: radar/
How it works
GhostDep runs five deterministic checks. The most severe result becomes the verdict; a BLOCKED package is never installed.
pip install command — could be a
hallucinated name, a typo, or a real package.
Benchmark
Run on 2026-09-26 against 75 packages across four categories using the live PyPI registry. All names and results are fully reproducible (seed 42).
Historical attacks (colourama, python3-dateutil, jeIlyfish) are simulated replays — those packages no longer exist on PyPI and are tested via the typosquat check directly. All 30 generated typosquats and hallucinated names were caught by the existence check; the typosquat check itself is evaluated in the historical replay. Full methodology and per-package results: benchmark/RESULTS.md
Works everywhere
GhostDep plugs in at the exact moment a package name is chosen — whether that's inside an AI agent, a CI pipeline, or the command line.
check_package() before installing it.
// Bob MCP config (project scope) { "mcpServers": { "ghostdep": { "command": "path/to/.venv/Scripts/python.exe", "args": ["-m", "ghostdep.server"], "cwd": "path/to/ghostdep" } } }
# Check, then install only if safe (drop-in for pip install) ghostdep install requests python-dateutil # Check one package ghostdep check reqeusts # Scan requirements.txt — exits 2 if any BLOCKED ghostdep scan requirements.txt # Generate a self-contained HTML report ghostdep report requirements.txt --output report.html # Watch the live PyPI feed for new lookalikes ghostdep scan-new # Allow internal packages, deny risky ones: .ghostdep.toml [policy] allow = ["acme-internal-auth"] deny = ["pycrypto"]
requirements.txt or
pyproject.toml, uploads SARIF to the GitHub Security tab,
and fails the job on BLOCKED packages.
# abridged # .github/workflows/ghostdep.yml on: pull_request: paths: ["requirements.txt", "pyproject.toml"] jobs: ghostdep-scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: ghostdep scan requirements.txt --format sarif > ghostdep.sarif - uses: github/codeql-action/upload-sarif@v3 with: sarif_file: ghostdep.sarif