{
  "markdown": "# import-guardian 🪝\n\n**Catch AI-hallucinated (slopsquatted) npm imports in generated code — _before_ you run `npm install`.**\n\nAn LLM just wrote some code. It might `import` a package that **doesn't exist** — a name the model confidently hallucinated. Attackers register exactly those names and ship malware. This is **slopsquatting**, and in 2026 it's one of the fastest-growing supply-chain attacks: code generators have been observed referencing **205,000+ unique non-existent package names**, and real hallucinated packages (`react-codeshift`, a fake `huggingface-cli` with 30k+ downloads) have already shown up in the wild.\n\n`import-guardian` reads the generated code, extracts every npm package it imports, and tells you which ones are real — so an agent never installs a name its own model invented.\n\nIt's part of the guardian set: [**npm-guardian**](https://github.com/Baneado98/npm-guardian) audits a package you've _already chosen_ for CVEs and malicious behaviour, [**license-guardian**](https://github.com/Baneado98/license-guardian) audits the _licenses_ of your dependencies (GPL/AGPL/BUSL traps) before you ship, and [**lockfile-guardian**](https://github.com/Baneado98/lockfile-guardian) audits the resolved `package-lock.json` for integrity tampering and risky install scripts. `import-guardian` works one step earlier than all of them, at the moment the code — and its dependency names — are generated.\n\n## What it catches\n\n| | |\n|---|---|\n| 👻 **Hallucinated** | imports of packages that **do not exist on npm** — the model invented the name. Returns `BLOCK` with a \"did you mean\" to the closest real package. |\n| 🪤 **Fresh squats** | packages that _do_ exist but were published days ago, have no source repo, near-zero downloads, or are a 1–2 edit typo of a popular library. Returns `REVIEW`. |\n| 🧩 **Import-aware** | resolves `import x from \"@scope/pkg/sub\"` → `@scope/pkg`, `lodash/fp` → `lodash`; skips Node built-ins (`fs`, `node:path`) and local paths. Plain regex, no AST, **zero runtime deps beyond the MCP SDK**. |\n\nVerdicts: 🟢 `CLEAN` · 🟠 `REVIEW` · 🔴 `BLOCK`.\n\n## Use it as an MCP server (free)\n\n```json\n{\n  \"mcpServers\": {\n    \"import-guardian\": { \"command\": \"npx\", \"args\": [\"-y\", \"import-guardian-mcp\"] }\n  }\n}\n```\n\nTools:\n\n- **`scan_code_imports`** — give it a block of generated JS/TS; it extracts and checks every npm import. _Run this on code you just generated, before its install command._\n- **`check_packages`** — verify an explicit list of package names.\n- **`verify_package`** — deep-check a single package name (existence, age, versions, repo, weekly downloads, edit distance to popular packages).\n- **`audit_lockfile`** 🔒 _(premium)_ — the deep one. Give it a lockfile from **npm** (`package-lock.json`), **yarn** (`yarn.lock`), **pnpm** (`pnpm-lock.yaml`), **PyPI** (`requirements.txt` / `poetry.lock` / `Pipfile.lock`) or **Go** (`go.mod` / `go.sum`) and it resolves the **whole transitive tree** and audits every package for **dependency confusion**, transitive **typosquats / homoglyph clones**, **maintainer-takeover** fingerprints, nonexistent pins, and **install-script** risk. See below.\n\n## Deep lockfile audit (premium) 🔒\n\n`scan_code_imports` looks at the handful of imports you can *see*. `audit_lockfile` looks at the **hundreds you can't** — the fully resolved dependency graph in your lockfile — and finds the supply-chain attacks an LLM cannot reason about from source alone:\n\n| | |\n|---|---|\n| 🎯 **Dependency confusion** | a package the lockfile resolves from a **private / internal registry** whose name is **unclaimed on the public index** — an attacker can publish that exact name publicly and shadow it. The single highest-impact supply-chain bug, and impossible to detect without the resolved lockfile + a live public-registry cross-check. On **PyPI** it's sharper still: `pip install --extra-index-url` merges your private index with public PyPI and installs the **highest version from either** (the 2021 attack that hit Microsoft / Apple / Tesla). Returns **critical**. |\n| 🪞 **Transitive typosquats** | homoglyph/Unicode-confusable clones (`rеact` with a Cyrillic *e*), transpositions (`axois`→`axios`), and registry-specific squats (PyPI `python3-dateutil` vs `python-dateutil`, normalized per PEP 503) across the **entire tree** — matched against a large per-ecosystem corpus of high-traffic packages. |\n| 🕳️ **Maintainer takeover** | the fingerprint of a compromised maintainer: a **dormant package suddenly republished**, a sole-maintainer project, or a rapid republish burst — the pattern behind **event-stream**, **ua-parser-js**, **node-ipc** and PyPI's **ctx** hijack. |\n| 💉 **Install-script version-diffing** | the sharpest injection signal: a package whose **newest release ADDED** a `postinstall`/`preinstall`/`install`/`prepare` hook that the **previous release did not have**. A previously-clean dependency that suddenly gains code-execution-on-install is exactly how event-stream and ua-parser-js shipped — caught by diffing the two releases' metadata on the live registry. Returns **critical**, and tells you the last clean version to pin. |\n| 🔬 **Install-script PAYLOAD analysis** | goes past *whether* a hook exists to *what it does*: downloads the package **tarball**, gunzip+untars it **in-memory** (no deps), resolves the lifecycle command to its entry file, and statically analyzes the **actual code** for malware behavior — network egress, credential/env exfil, child-process exec, download-and-run, obfuscation. An **obfuscation-resistant AST/token analyzer** decodes `\\x`/`\\u`/octal escapes and folds split-string concatenation, so a sink hidden as `require(\"child_pro\"+\"cess\")` or `global[\"ev\"+\"al\"](…)` is still caught. The payload lives inside a binary artifact the buyer's agent never fetches — that's the access+compute moat. |\n| 🐍 **PyPI sdist `setup.py` payload analysis** | the PyPI analogue of the npm tarball pass, and the sharpest PyPI surface: a **source distribution** runs `setup.py` — arbitrary Python — at **`pip install` time, before any of your code**. We download the **sdist** (anti-DoS capped), untar it in-memory, and statically analyze `setup.py` / `setup.cfg` / `pyproject.toml` for the stealer/loader pattern — `os.environ` exfil, `os.system`/`subprocess` exec, `.pypirc`/AWS/SSH credential theft, `urllib`/`requests` egress, `exec`/`eval`/`__import__` obfuscation, download-and-run, and a **non-standard build backend**. A **Python AST de-obfuscator** decodes `\\x`/`\\u`/octal escapes and folds split + implicit-adjacent strings, so `getattr(__import__(\"o\"+\"s\"),\"sys\"+\"tem\")(…)` is still resolved. The `ctx` / `jeIlyfish` / `colourama` attack class — caught from an artifact the buyer's agent never fetches. **BLOCK is gated on the malware *combination*** (exfil/download-exec/packed-loader), so a legitimate C/Cython build that merely shells out to a compiler is reported as an informational note, **not** a false-positive block. |\n| 🦠 **Known-malware / known-vuln (live OSV cross-reference)** | every resolved package is cross-referenced — at its **exact pinned version** — against the live **[OSV.dev](https://osv.dev)** advisory database (which ingests the OpenSSF **Malicious Packages** feed as `MAL-*` plus GitHub/PyPI/Go/crates/Maven/Packagist advisories). If the version your lockfile pins is documented **malware** (e.g. `event-stream@3.3.6`, `ctx@0.1.2`), that's a **critical / BLOCK** — the strongest possible signal. The mapping is **version-specific**: `event-stream@3.3.6` is malware, `3.3.4` is clean, and only a live database query can tell them apart. This is a different *kind* of moat from the static passes above — the buyer's model doesn't hold the advisory corpus and can't resolve version↔advisory. Queries are **batched, cached (TTL), concurrency-capped**, and degrade clean if OSV is unreachable (it never turns a clean package into a false positive). |\n| 🏗️ **Local build-backend follow-hop** (PyPI) | when `pyproject.toml` declares a **non-standard build backend that ships *inside* the sdist** (`backend-path = [\".\"]`), the package runs its **own code** as the PEP 517 backend at every build/install. We **resolve that module to its file in the sdist and analyze its real source** (and follow `setup.py`'s local imports one hop), instead of merely flagging the backend name — closing the gap where a payload hides one indirection away from `setup.py`. |\n| 🔀 **Go `replace` hijack** | a `replace … => host/path` in `go.mod` pointing at an **external host**, **live-verified** against the Go module proxy. If the replacement doesn't resolve (a private / attacker host), the code your build actually compiles isn't the module you audited or anything `go.sum` can vouch for. |\n| 💣 **Install-script & provenance risk** | packages in the resolved tree that run install scripts, brand-new pins, missing repos, and **near-zero real downloads** (verified against npm / pypistats / crates.io). |\n\n**Seven ecosystems.** The same resolved-graph engine runs over **npm / yarn / pnpm**, **PyPI** (`requirements.txt`, `poetry.lock`, `Pipfile.lock`), **Go modules** (`go.mod`, `go.sum`), **Rust / Cargo** (`Cargo.lock`, `Cargo.toml`), **Java** (Maven `pom.xml`, Gradle `build.gradle` / `.kts` / `gradle.lockfile`) and **PHP / Composer** (`composer.json`, `composer.lock`) — each cross-checked against its own public index (npm registry, PyPI, the Go module proxy, crates.io, **Maven Central**, **Packagist**). On crates.io the matcher knows the `-`/`_` equivalence (`rustdecimal` vs `rust_decimal`); on Maven an internal `groupId:artifactId` unclaimed on Central is the dependency-confusion target; on Composer an internal `vendor/package` unregistered on Packagist while a private repo is merged with the public index is the confusion target, and a Packagist-**abandoned** package is flagged as a takeover risk. Large lockfiles are bounded: direct + private-source packages are always live-checked first, then transitive up to a cap, so one call never fans out to tens of thousands of requests.\n\n```\nPOST /pro/audit   { \"lockfile\": \"<contents of package-lock.json>\", \"filename\": \"package-lock.json\" }\n```\n\nPay per call via **x402** (USDC on Base) or a prepaid Stripe key. The free `POST /audit` returns the verdict and issue counts by severity, but **withholds which packages are affected and why** — that's the paid report. This deep engine runs **server-side only** and is never shipped in the npm package.\n\n### Example\n\n```\nscan_code_imports({ code: 'import shift from \"react-codeshift\";\\nimport React from \"react\";' })\n\n🟠 REVIEW — 1 referenced package(s) look risky.\n🟠 react-codeshift  (risk 57/100)\n    • No source repository linked.\n    • Only 1 published version.\n    • Only 3 downloads in the last week despite being 158 days old.\n🟢 react  (ok) — established package.\n```\n\n## Free HTTP API\n\n```\nPOST /scan        { \"code\": \"import x from 'reqeusts'\\nimport y from 'lodash'\" }\nGET  /verify?name=express\n```\n\nHosted at **https://import-guardian.vercel.app** · try [`/verify?name=express`](https://import-guardian.vercel.app/verify?name=express) vs [`/verify?name=reqeusts`](https://import-guardian.vercel.app/verify?name=reqeusts).\n\n## Pay-per-call (x402)\n\nThe `/pro/*` routes are gated by [x402](https://x402.org). Your agent pays **$0.02 USDC** per call automatically — no sign-up, no API key. Payment settles on-chain (USDC on Base). The server holds **no private key**; it only declares a public receiving address.\n\n```\nPOST /pro/scan    { \"code\": \"…\" }   # 402 → pay → result, no rate limit\n```\n\n## How it works (and its limits)\n\n- Existence + freshness come straight from the live npm registry (`registry.npmjs.org`) and the download-stats API — these are **facts**, not guesses, which is the moat: an agent can't reliably know on its own whether a name it generated is real and trusted.\n- \"Did you mean\" uses Levenshtein distance against a curated list of high-impact packages attackers impersonate.\n- It does **not** execute or install anything (read-only), and it intentionally errs toward `REVIEW` rather than silently passing a brand-new lookalike. It is a guardrail, not a guarantee — pair it with [npm-guardian](https://github.com/Baneado98/npm-guardian) for behavioural/CVE auditing of packages you decide to keep.\n\n## License\n\nMIT.\n",
  "bytes": 12303,
  "sha": "8003c6c5c43a8f47cbab5c1d34701918ab5c10a5d9e13c9cb4b6f239cafdba8d",
  "repo_slug": "baneado98/import-guardian",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_baneado98_import_guardian_eb120565/readme"
}