npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

import-guardian-mcp

v0.3.0

Published

Catch AI-hallucinated (slopsquatted) npm imports in generated code BEFORE npm install. Scans a code block, flags imports of packages that don't exist on npm (the name an LLM invented and attackers register with malware) plus fresh lookalike squats. MCP se

Readme

import-guardian 🪝

Catch AI-hallucinated (slopsquatted) npm imports in generated code — before you run npm install.

An 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.

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.

It's part of the guardian set: npm-guardian audits a package you've already chosen for CVEs and malicious behaviour, license-guardian audits the licenses of your dependencies (GPL/AGPL/BUSL traps) before you ship, and 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.

What it catches

| | | |---|---| | 👻 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. | | 🪤 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. | | 🧩 Import-aware | resolves import x from "@scope/pkg/sub"@scope/pkg, lodash/fplodash; skips Node built-ins (fs, node:path) and local paths. Plain regex, no AST, zero runtime deps beyond the MCP SDK. |

Verdicts: 🟢 CLEAN · 🟠 REVIEW · 🔴 BLOCK.

Use it as an MCP server (free)

{
  "mcpServers": {
    "import-guardian": { "command": "npx", "args": ["-y", "import-guardian-mcp"] }
  }
}

Tools:

  • 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.
  • check_packages — verify an explicit list of package names.
  • verify_package — deep-check a single package name (existence, age, versions, repo, weekly downloads, edit distance to popular packages).
  • 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.

Deep lockfile audit (premium) 🔒

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:

| | | |---|---| | 🎯 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. | | 🪞 Transitive typosquats | homoglyph/Unicode-confusable clones (rеact with a Cyrillic e), transpositions (axoisaxios), 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. | | 🕳️ 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. | | 💉 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. | | 🔬 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. | | 🐍 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. | | 🦠 Known-malware / known-vuln (live OSV cross-reference) | every resolved package is cross-referenced — at its exact pinned version — against the live 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. [email protected], [email protected]), that's a critical / BLOCK — the strongest possible signal. The mapping is version-specific: [email protected] 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). | | 🏗️ 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. | | 🔀 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. | | 💣 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). |

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.

POST /pro/audit   { "lockfile": "<contents of package-lock.json>", "filename": "package-lock.json" }

Pay 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.

Example

scan_code_imports({ code: 'import shift from "react-codeshift";\nimport React from "react";' })

🟠 REVIEW — 1 referenced package(s) look risky.
🟠 react-codeshift  (risk 57/100)
    • No source repository linked.
    • Only 1 published version.
    • Only 3 downloads in the last week despite being 158 days old.
🟢 react  (ok) — established package.

Free HTTP API

POST /scan        { "code": "import x from 'reqeusts'\nimport y from 'lodash'" }
GET  /verify?name=express

Hosted at https://import-guardian.vercel.app · try /verify?name=express vs /verify?name=reqeusts.

Pay-per-call (x402)

The /pro/* routes are gated by x402. 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.

POST /pro/scan    { "code": "…" }   # 402 → pay → result, no rate limit

How it works (and its limits)

  • 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.
  • "Did you mean" uses Levenshtein distance against a curated list of high-impact packages attackers impersonate.
  • 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 for behavioural/CVE auditing of packages you decide to keep.

License

MIT.