Skip to main content

vaporcheck

Fail-closed existence verification for model-emitted identifiers. Catch hallucinated and slop-squatted package names, dead file paths, and nonexistent API/tool names before an agent acts on them — because vaporware you pip install is somebody else's payload.

License: Apache 2.0 tests deps


Why

LLM coding agents confidently emit identifiers that don't exist: a USENIX Security 2025 study found 19.7% of LLM-recommended packages were hallucinated — an open door for slopsquatting (an attacker registers the hallucinated name). The same failure shows up as dead file paths, deprecated/removed APIs, and calls to tools that were never registered.

The root cause — model overconfidence — is not fixable by a wrapper. So vaporcheck doesn't try. It replaces the unanswerable question "is the model confident?" with a deterministic one:

"Does the referent actually exist in its authoritative source?"

That's a binary lookup (a registry, the filesystem, a symbol table) — testable, cheap, and hard to argue with.

How it works

A small resolver core answers exists / not-found / deprecated / cannot-verify for one identifier, shipped through two delivery vehicles:

Vehicle What it is Guarantee
MCP server (verify_identifier tool) Any MCP client can ask "does this exist?" Agent-callable, advisory
PreToolUse hook A Claude Code hook that denies the tool call Fail-closed — the agent can't proceed

Both reuse the same resolvers. Standalone, the MCP is advisory (the model may ignore it); paired with the hook (or any gateway that can block), it becomes a hard gate.

The honest boundary

A hook sees tool calls, not generated text. So today this gates the two classes that surface at the tool boundary:

  • package installs (pip install, npm install, …) — verified against PyPI / npm
  • file paths (Edit of a missing file) — verified against the filesystem

Tool-names and API-symbols buried in generated code are verify-on-write / verify-on-run (gate the Edit or test that introduces them), not mid-sentence interception. This is a deliberate, documented limit — see docs/SPIKE-FINDINGS.md.

Install

Requires Python 3.10+. Zero runtime dependencies (stdlib only — fitting for an anti-supply-chain-risk tool).

Option A — pip (adds the vaporcheck-mcp console command)

pip install vaporcheck

Option B — clone (if you want the hook/MCP config to point at source files)

git clone https://github.com/cdmx-in/vaporcheck.git
cd vaporcheck
python vaporcheck/test_parse.py   # offline smoke test — should print 10/10 PASS

Configure

As an MCP server (any MCP client)

// .mcp.json (project scope) — adjust the path to where you cloned
{
  "mcpServers": {
    "vaporcheck": { "command": "python", "args": ["/path/to/vaporcheck/vaporcheck/server.py"] }
  }
}

(On Windows, "command": "py", "args": ["-3", ...] also works. If you installed via pip, use "command": "vaporcheck-mcp" with no args.)

Then call the verify_identifier tool:

{ "kind": "pypi", "value": "reqeusts" }
// -> { "status": "not-found", ... }   (a slopsquat — blocked)

As a fail-closed Claude Code hook

// .claude/settings.json (or settings.local.json)
{
  "hooks": {
    "PreToolUse": [
      { "matcher": "Bash",
        "hooks": [{ "type": "command", "command": "python /path/to/vaporcheck/vaporcheck/hook.py", "timeout": 15 }] }
    ]
  }
}

Restart Claude Code to load the hook. Now pip install <hallucinated-package> is denied before it runs, with a reason the model can self-correct from. On network failure the hook degrades to ask instead of falsely blocking.

Status

MVP — working, tested, dogfooded. Resolvers: PyPI, npm, filesystem paths. Shared SQLite cache (warm lookups ~1 ms) with status-aware TTLs and a token-bucket throttle.

Roadmap

  • More package ecosystems: crates.io, Go, RubyGems, Maven
  • Tool-name resolver (against the live tool registry) — greenfield, no incumbent
  • API-symbol resolver (via LSP / compiler) — greenfield
  • Citations resolver (last; the crowded class — breadth, not headline)
  • Namespace the modules under a package; publish to PyPI

Tests

38 checks across 4 suites — all hitting real registries / filesystem, no mocks:

python vaporcheck/run_spike.py    # hook behavior (10)
python vaporcheck/test_parse.py   # shell-aware install parser (10)
python vaporcheck/test_mcp.py     # MCP client handshake (9)
python vaporcheck/test_cache.py   # cache latency / TTL / errors (9)

Provenance

This repo is the product of a structured research → validation → build effort. The full record — pain-point research, web-verified go/no-go, and the live-dogfooding spike that found and fixed two real parser bugs — is in docs/.

License

Apache-2.0 © 2026 Codemax IT Solutions Pvt. Ltd.

Metadata

Release files for vaporcheck 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for vaporcheck 0.1.0
File Size Uploaded Provenance
vaporcheck-0.1.0.tar.gz 21.4 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for vaporcheck 0.1.0
File Interpreter ABI Platform
vaporcheck-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 44.3 kB

Release files / vaporcheck-0.1.0.tar.gz

Download URL vaporcheck-0.1.0.tar.gz
Size 21.4 kB
Tags Source
SHA-256 checksum
How to use checksums
7761d94a7e3021fe69e4e60c466c43e5c26e457fb297985208fac2fdb037fc02
BLAKE2b-256 checksum
How to use checksums
d920744007c8c2f0990c442bd5aa5604a67eae0beb423d526812f8456c6e2966
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Release files / vaporcheck-0.1.0-py3-none-any.whl

Download URL vaporcheck-0.1.0-py3-none-any.whl
Size 22.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
15fbf68d6337d9586b8927195d61d8d26ea6ca2118b8eb7e86c71ca77b576bab
BLAKE2b-256 checksum
How to use checksums
3db88ec05f0d2ff87be0e09ec40bb9c53342e79f443bcf7ce8ce0fd2a3bfa87b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Security for vaporcheck 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page