Skip to main content

pkg-guard

AI coding agents sometimes install packages that don't exist. Attackers register those hallucinated names, plus one-letter typos of popular packages, and wait ("slopsquatting"). pkg-guard is a hook that checks every install command your agent runs and blocks the suspicious ones before they execute.

$ pkg-guard check "pip install reqeusts"
BLOCK reqeusts: does not exist on pypi (hallucinated name?). Did you mean 'requests'?

$ pkg-guard check "npm i lodahs"
BLOCK lodahs: looks like a typosquat of popular package 'lodash'.

The agent sees the reason and fixes its own command.

  • Works with Claude Code, Codex, and Gemini CLI. One script speaks all three hook protocols.
  • Covers npm, pnpm, yarn, bun, npx, pip, uv, uvx, poetry, pipx, cargo, gem, and go (go get, go install, go run mod@v).
  • Single file, Python stdlib only. The guard adds no dependencies of its own. Read it in five minutes.

What gets blocked

Signal Rule
Doesn't exist Registry returns 404. Suggests the popular name it resembles.
Too new First published less than 30 days ago (PKG_GUARD_MIN_AGE_DAYS)
Too few users npm: fewer than 100 weekly downloads. crates: fewer than 100 in 90 days. gems: fewer than 1,000 total. Go (GitHub/GitLab/Bitbucket): fewer than 10 repo stars, since Go publish times come from git commits and can be backdated.
Typosquat One edit away from a popular package, and young or little-used
Install script (npm) Latest version runs preinstall/install/postinstall code, and the package is young (< 180 days) or little-used (< 10,000 weekly downloads)

It also catches the usual ways around a naive regex: bash -lc "...", $(...), sudo/env wrappers, npm isntall, npm i x@npm:evil, npx -p evil, uvx --with evil, and pip install -r requirements.txt (nested -r included).

It also checks file edits (Claude Code Write/Edit, Codex apply_patch, Gemini CLI write_file/replace): dependencies the agent adds to package.json, requirements*.txt, pyproject.toml, Cargo.toml, Gemfile, or go.mod are checked before the edit is written, so a later bare npm install can't sneak them in.

Install

Claude Code (plugin: hook + skill)

/plugin marketplace add somesh-ghaturle/pkg-guard
/plugin install pkg-guard@pkg-guard

Codex

git clone https://github.com/somesh-ghaturle/pkg-guard ~/.pkg-guard
mkdir -p ~/.codex/skills && cp -r ~/.pkg-guard/skills/pkg-guard ~/.codex/skills/

Then add the hook to ~/.codex/config.toml, or use examples/codex-hooks.json as ~/.codex/hooks.json:

[[hooks.PreToolUse]]
matcher = "Bash|apply_patch"

[[hooks.PreToolUse.hooks]]
type = "command"
command = "python3 ~/.pkg-guard/pkg_guard.py hook"
timeout = 20

Gemini CLI

git clone https://github.com/somesh-ghaturle/pkg-guard ~/.pkg-guard

Then merge examples/gemini-settings.json into ~/.gemini/settings.json, with the command set to python3 ~/.pkg-guard/pkg_guard.py hook. It checks run_shell_command plus write_file/replace edits to manifests.

Claude Code without the plugin

Add examples/claude-settings.json to ~/.claude/settings.json, using your own path to the script.

As a CLI

pipx install slopsquat-guard             # the PyPI name; the command is pkg-guard
pkg-guard check "npm install some-lib"   # exit 1 if blocked
pkg-guard scan                           # check every package in ./package-lock.json, uv.lock, ...

The PyPI package is slopsquat-guard. pkg-guard on PyPI is a different, unrelated project.

scan reads package-lock.json, npm-shrinkwrap.json, yarn.lock (classic and Berry), pnpm-lock.yaml, uv.lock, poetry.lock, Cargo.lock (the TOML ones need Python 3.11+), Gemfile.lock and go.sum, skips git/path/workspace entries, and applies the same rules to every registry package, transitive ones included. Use it once when you add pkg-guard to an existing repo, or in CI.

Configuration

Env var Effect
PKG_GUARD_ALLOW=a,b Always allow these packages (internal or private names)
PKG_GUARD_MIN_AGE_DAYS=30 Minimum package age
GOPRIVATE / GONOPROXY Go modules matching these globs are skipped, like go does (env vars only, not go env -w)
XDG_CACHE_HOME Packages that pass are cached for 24h in $XDG_CACHE_HOME/pkg-guard/pkg-guard-ok.json (default ~/.cache). Blocks are never cached. Delete the file to recheck everything.
PKG_GUARD_FAIL_CLOSED=1 Block when the registry can't be reached. The default is to allow and warn, so offline work keeps going.

Limits

pkg-guard catches agent mistakes: hallucinated names, typos, packages too new to trust. It is not a sandbox against an agent that is trying to get around it. A prompt-injected agent can still install through a variable (P=npm; $P i x), a script file, or python -c "import subprocess...". The command parser follows bash's quoting, comments, heredocs and $(...) rules so ordinary commands can't hide an install by accident, but no static check can see every way a shell can run code.

It is a heuristic guard, not a sandbox. It checks install commands and manifest edits made through the agent's edit tools. It does not check imports, and it does not see manifests changed by shell commands (echo evil >> requirements.txt). pyproject.toml and Cargo.toml edits are only checked on Python 3.11+ (needs tomllib). Go modules on vanity domains (go.uber.org/...) get no star signal, and their age can be backdated. The pass cache is a convenience, not a security boundary: the hook refuses agent writes to it, but a determined agent could still reach it indirectly. Packages from custom indexes are still checked against the public registry. Tarball and git URLs are not checked. Codex apply_patch patches are applied with a port of Codex's own matching rules; a patch Codex would reject is denied.

Development

python3 -m unittest -v
python3 scripts/gen_popular.py   # refresh the top-1000 npm/PyPI/crates lists in pkg_guard.py

MIT licensed.

Metadata

Release files for slopsquat-guard 0.4.1

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

Source distribution (sdist)

Source distribution for slopsquat-guard 0.4.1
File Size Uploaded Provenance Actions
slopsquat_guard-0.4.1.tar.gz 42.0 kB With provenance Details for slopsquat_guard-0.4.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for slopsquat-guard 0.4.1
File Interpreter ABI Platform Actions
slopsquat_guard-0.4.1-py3-none-any.whl Python 3 none any Details for slopsquat_guard-0.4.1-py3-none-any.whl

Total release size: 81.8 kB

Release files / slopsquat_guard-0.4.1.tar.gz

Download URL slopsquat_guard-0.4.1.tar.gz
Size 42.0 kB
Tags Source
SHA-256 checksum
How to use checksums
d62e8842d43127f3a2a70eba9a2ac294a3bc01fc4f611e52c82732f1722a8449
BLAKE2b-256 checksum
How to use checksums
65095d32ac18adc9dd20d6fb3ba237be19208c549d06bce95c6c077da5405e07
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release files / slopsquat_guard-0.4.1-py3-none-any.whl

Download URL slopsquat_guard-0.4.1-py3-none-any.whl
Size 39.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
37e6e7eb684e03022723adcbeece8cea3a7bddd289dc927ccb430e120689bc95
BLAKE2b-256 checksum
How to use checksums
f20ccb090f929c1e547c422fa148de8b096b8de072ac6f7438604fba5f70e9a7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Security for slopsquat-guard 0.4.1

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for slopsquat_guard-0.4.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

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.

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