Verify a package is real before you install it — built for the AI-coding era of typosquatting and slopsquatting.
Project description
wary
Verify a package is real — and isn't a typosquat — before you install it.
$ wary check requsts --ecosystem pypi
💀 requsts [pypi] — nonexistent
→ 'requsts' does not exist on pypi. If an AI assistant suggested this
package, it's a hallucinated name — do not install it.
1 packages checked — 0 ok, 0 suspicious, 0 likely squats, 1 nonexistent
RESULT: FAIL
Why this exists
AI coding assistants are extremely good at writing code and occasionally hallucinate a package name that sounds exactly right and doesn't exist. Security researchers call this slopsquatting: attackers watch for these hallucinated names — Copilot, ChatGPT, and Claude all tend to hallucinate the same plausible-sounding names repeatedly — and register them first, so the next developer who blindly installs what their AI suggested gets malware instead.
This isn't hypothetical. 2026 alone has seen a steady drumbeat of real supply-chain compromises: the Axios npm hijack (100M+ weekly downloads, North Korea-attributed), the LiteLLM PyPI poisoning, a compromised Red Hat npm namespace, a fake @bitwarden/cli impersonating the real Bitwarden CLI, and typosquatted OpenSearch/DevOps packages harvesting AWS and CI/CD credentials. Every one of these relied on the same thing: a developer (or a CI pipeline) installing a name that looked right.
Excellent general supply-chain-security tools exist (Socket.dev, Snyk) — but they're built and priced for teams of 5+ developers. wary is narrower and free at the individual level: is this specific name I'm about to install real, and does it look like it's impersonating something popular.
Install
pip install wary
Use it the moment it matters
Right after your AI assistant suggests a package, before you run the install:
wary check some-package-suggested-by-copilot --ecosystem npm
Scan a whole project
cd your-project
wary scan
Check only what's new (the CI use case)
wary diff /path/to/base-branch-checkout /path/to/pr-branch-checkout
This is what the GitHub Action runs on every PR — see action/README.md. It only flags dependencies that are new in the diff, so it's quiet on unrelated PRs and loud exactly when something new shows up.
What it checks
For every package name:
- Does it exist at all? If not, and this came from an AI suggestion, it's a hallucination — the single most actionable finding
waryproduces. - Is the name a squat pattern against one of ~650 popular npm/PyPI packages — character swaps, keyboard-adjacent typos, combosquats (
lodash-utils), delimiter swaps (react_dom), pluralization, vowel swaps? - Is it young enough to be an active impersonation rather than a coincidentally similar independent package?
- Does its publish history look automated — a burst of many versions in a very short window?
See docs/DETECTION.md for the full pattern taxonomy and the verdict logic.
Limitations (read this)
- This is a fast, automated first pass, not a guarantee. A package that passes every check here can still be malicious in ways that don't show up in name similarity or publish timing (a legitimately-named package whose maintainer's account gets compromised, for instance — exactly what happened to Axios).
- The protected-name list (
src/wary/popular_names.py) is a curated, versioned snapshot of ~650 packages, not a live top-N-by-downloads query — see the file for why. - Currently covers npm and PyPI. See
docs/ROADMAP.mdfor what's next.
Development
pip install -e ".[dev]"
pytest # unit tests (fast, offline)
pytest -m integration # + live tests against real npm/PyPI registries
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file wary_sh-0.1.0.tar.gz.
File metadata
- Download URL: wary_sh-0.1.0.tar.gz
- Upload date:
- Size: 37.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2835a2b7904174a8f117e66630de5448f3ec482b0558eb2ea1f20da7c340221e
|
|
| MD5 |
b4658da8335a5691b54b4498ea7795d0
|
|
| BLAKE2b-256 |
73d469cfdbc7f5af9afc6f47e073c1d10392d4d084335f10c724d47359e82ccf
|
File details
Details for the file wary_sh-0.1.0-py3-none-any.whl.
File metadata
- Download URL: wary_sh-0.1.0-py3-none-any.whl
- Upload date:
- Size: 18.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3969d26ace43797be0da5ae71e91f604f2f1396ea8c064b158c6ba09d3ee5b0c
|
|
| MD5 |
22616a78e910a03d6140d7f2b308b2f7
|
|
| BLAKE2b-256 |
7a0bfa5ad8294302cf2d6bf09c0914389f478cd8cdb99999e12d439e21306f3c
|