Sign In

slopgate-cli

Package Overview
Dependencies
Maintainers
1
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

slopgate-cli

Install-gate hook that blocks slopsquatted / hallucinated npm packages before an AI coding agent installs them.

latest
Source
npmnpm
Version
0.4.2
Version published
Weekly downloads
12
-92.45%
Maintainers
1
Weekly downloads
 
Created
Source

slopgate

A hook that sits in front of your AI coding agent's npm installs and stops slopsquatted or hallucinated packages before they're ever fetched.

CI Go 1.21+ License: MIT

slopgate blocking a hallucinated npm package before it installs

A supply-chain scanner tells you a package was bad after it's in your node_modules. slopgate refuses to fetch a package that shouldn't exist in the first place — at the moment your agent types npm install.

LLM coding agents confidently invent package names that don't exist (gpt-commit-assistant, ai-code-reviewer-toolkit). Attackers register those hallucinated names and wait — slopsquatting. slopgate does exactly one thing that a supply-chain scanner like pkgxray doesn't: npm registry existence + freshness checking — is this package name real, and if so, is it suspiciously new / low-download in the shape of a name that was just squatted. For everything else (OSV, static heuristics, prompt-injection detection) it shells out to pkgxray and folds its verdict in. It never reimplements that analysis.

It reuses hookshot (imported as a Go library) so a single binary hooks Claude Code, Codex, Cursor, Windsurf Cascade, and Factory Droid from one unified handler.

Install

slopgate is a single pure-Go binary.

# npm — ships prebuilt binaries for darwin/linux on arm64/amd64, no toolchain
npm install -g slopgate-cli

# from source (needs Go 1.21+ and the hookshot sibling repo checked out beside it)
git clone https://github.com/adamsjack711-ux/slopgate
git clone https://github.com/adamsjack711-ux/hookshot          # replace target
cd slopgate && make install                                  # builds → /usr/local/bin

go install …@latest isn't available yet: the build depends on a fork of hookshot via a local replace directive, so hookshot must sit beside slopgate. That goes away once hookshot is a tagged module.

Wire it into your agent

slopgate init wiring the hook, then blocking a hallucinated npm install

One command. slopgate init finds its own absolute path and merges the hook into the agent's settings file — no hand-pasted path to go stale, every other key in the file preserved, the original backed up to *.slopgate.bak:

slopgate init                 # Claude Code  (~/.claude/settings.json)
slopgate init --agent codex   # OpenAI Codex (~/.codex/hooks.json)
slopgate init --agent all     # every supported agent at once
slopgate init --print         # print the snippet + target path, write nothing

Re-running is safe: it updates the path in place instead of duplicating, and says unchanged when nothing needs to move. Supported: claude, codex, cursor, droid, cascade.

Prefer to wire it by hand?

Claude Code, ~/.claude/settings.json (the same binary serves the other agents via their own hook command names — see the Agents table):

"PreToolUse": [{ "matcher": "Bash",
  "hooks": [{ "type": "command",
    "command": "/abs/path/to/slopgate claude-pre-tool-use # slopgate-managed" }] }]

The trailing # slopgate-managed is a shell comment (inert when the hook runs). It's how a later slopgate init recognizes and updates this entry in place — regardless of where the binary ends up — instead of adding a duplicate. Wiring by hand is optional; slopgate init adds it for you.

How a command is judged

For each npm package a command would fetch, the checks run cheapest-first, short-circuiting where they can:

  • ExistenceGET registry.npmjs.org/<name>. A 404 is a hard stop: the package doesn't exist. This is its own outcome (nonexistent), distinct from a verdict, and it always denies.
  • Freshness — package first-published < freshnessAgeDays and weekly downloads < minWeeklyDownloads. Eligible on its own → at least review.
  • Typosquat — the (existing) name is a near-miss of a popular package (lodahslodash, reacttreact), by edit distance against an embedded corpus of the top npm packages. Established/popular packages are exempt. Eligible on its own → at least review. (Offline; no extra network.)
  • pkgxray guardpkgxray guard npm:<name>@<version> --format json, parsed for its decision and cited report.findings.

The results fold into one verdict using pkgxray's scheme — allow / review / block — worst wins. The policy then maps that to a hook action.

verdictstrictbalanced (default)permissive
allowallowallowallow
reviewdenyaskallow (warn)
blockdenydenydeny
nonexistentdenydenydeny

(npx / npm exec runners execute package code immediately, so a review that would be allowed under permissive is hardened to ask.)

If pkgxray isn't on PATH, slopgate fails loudly and closed with install instructions — never a cryptic subprocess error mid-install.

What counts as an install

Before judging anything, slopgate has to find the packages a command would fetch. It recognizes npm install|i|add, npm ci, npm exec, and npx, and:

  • Strips leading env-var assignments and command wrappers, so NODE_ENV=production npm i x, sudo npm i x, and env FOO=bar npm i x are still seen as npm installs rather than waved through as unrecognized shapes.
  • Expands a bare npm install / npm ci — which names no packages — by reading the direct dependencies from package.json in the agent's working directory and gating each by name. This catches the common path where an agent writes a hallucinated dependency into the manifest and then runs a plain npm install. When the manifest can't be read, the install is held for review instead of allowed. Only direct deps are read, and non-registry deps (git:/file:/workspace:/url/alias) are skipped so a legitimate non-registry dependency isn't false-denied.

Packages are evaluated concurrently (bounded), so a manifest that fans out to many dependencies doesn't stall the install serially.

CLI (out-of-band, for testing / CI)

slopgate init --print           # show the wiring snippet without touching any file
slopgate preflight              # is pkgxray resolvable?
slopgate check express          # evaluate a name or command, print JSON evidence
slopgate check 'npm i left-pad@1.3.0'

check prints the full evidence report to stdout and exits with pkgxray's convention: 0 allow · 2 block/nonexistent · 3 review.

Configuration

A single JSON file (~/.config/slopgate/config.json, or point at one with SLOPGATE_CONFIG). Omitted fields keep their defaults; unknown keys are rejected so a typo is caught. See config.example.json.

fielddefaultmeaning
freshnessAgeDays30flag packages first-published younger than this (0 disables)
minWeeklyDownloads100flag packages below this weekly-download count (0 disables)
policybalancedstrict | balanced | permissive (how review maps to an action)
pkgxrayBinpkgxraypath/name of the pkgxray executable
skipPkgxrayfalserun existence + freshness only; skip the pkgxray subprocess (and its preflight)
typosquatChecktrueflag names that are near-misses of popular packages; set false to disable

skipPkgxray: true turns slopgate into a pure existence/freshness pre-check — use it when a separate pkgxray gate (e.g. the pkgxray-guard hook) already runs, so npm installs aren't guarded twice.

Environment:

  • SLOPGATE_DISABLE=1 — bypass slopgate entirely (operator kill switch)
  • SLOPGATE_LOG — evidence log sink: stderr, or a file path (default: off). Hooks are quiet by default; the agent sees only the decision reason.

Agents

One binary, one OnBeforeExecution handler, five agents — each with its own hook command name. All verified against a hallucinated install:

AgentHook commandblock / deny surfaces asreview (ask) surfaces as
Claude Codeclaude-pre-tool-usepermissionDecision: deny (stdout JSON)approval prompt
OpenAI Codexcodex-pre-tool-usepermissionDecision: denyblocked (Codex doesn't enforce ask)
Cursorcursor-before-shellpermission: deny (stdout JSON)approval prompt
Factory Droiddroid-pre-tool-useexit 2 + stderr reasonblocked (no ask path)
Windsurf Cascadecascade-pre-run-commandexit 2 + stderr reasonblocked (ask not wired)

An ask never fails open. On agents without an approval prompt (Codex/Droid/Cascade), hookshot enforces a review-tier ask as a hard block — so under balanced policy those agents behave like strict for the review tier (safe, just no prompt). slopgate's deny/ask/allow → hook-decision mapping is pinned by a test so a review can never silently become an allow.

Design notes

The decisions worth knowing, and why they went the way they did.

Non-existence is its own outcome

A hallucinated name isn't a risky package — it's no package. slopgate gives a registry 404 its own verdict (nonexistent) rather than folding it into "block", and short-circuits there: nothing to be fresh about, nothing for pkgxray to fetch. It always denies, under every policy. This is the single most common thing an LLM gets wrong, and it's the cheapest check, so it runs first.

Freshness needs both young and low-download

A brand-new package isn't suspicious (every good package was new once); a low-download package isn't either (plenty of legitimate niche tools). It's the conjunction — just published and almost nobody's using it — that matches a name squatted moments after an LLM invented it. Requiring both keeps an established package from ever being flagged this way. "Too new to have download stats yet" reads as low, because that's part of the just-squatted signal.

Manifest installs are expanded, not waved through

The obvious attack on a command-line gate is to not name the package on the command line: write it into package.json, then run a bare npm install. slopgate reads the manifest's direct dependencies and gates each by name. It reads only direct deps — a hallucinated package is one the agent wrote, not a transitive it doesn't control — and skips non-registry sources so a git: or file: dependency is never false-denied.

Typosquatting is name-distance, guarded against false positives

A typosquat is the inverse of a hallucination: the package does exist, but its name is a near-miss of a popular one (lodahs for lodash) registered to catch a fat-fingered or mis-recalled install. slopgate measures optimal-string- alignment (edit) distance — including adjacent transpositions, a classic slip — against an embedded, rank-ordered corpus of the top npm packages, so it works offline. Three guards keep it precise: a package that is itself popular is never flagged; one with real download traffic is treated as established, not a squat; and short names are skipped (too many distance-1 collisions). It's review-tier, not an auto-block, because edit distance is a heuristic — the human gets the final call.

It shells out to pkgxray — it never reimplements it

OSV lookups, static heuristics, and prompt-injection detection live in pkgxray and stay there. slopgate calls pkgxray guard <ref> --format json as a subprocess and folds the verdict in. Duplicating that logic would mean two things to keep correct and in sync; one source of truth is the whole point.

Every fail-mode is fail-closed

A missing pkgxray denies with install instructions (not a cryptic mid-install error). A pkgxray that can't produce a verdict maps to review, not a false allow. In skipPkgxray mode — where there's no pkgxray backstop — a registry lookup error resolves to review, not allow. And an ask is enforced as a block on agents that can't prompt. There is no path where uncertainty opens the gate.

Development

make build      # build ./slopgate (needs the hookshot sibling repo)
make test       # go test -race ./...
make vet        # go vet
make dist       # cross-compile release binaries for all platforms
make help       # list targets

Tests are hermetic: the registry client is exercised against an httptest server and the pkgxray subprocess against a pure-Go fake Runner, so the suite needs neither the network nor pkgxray installed. Re-record the demo GIF with vhs:

bash demo/record.sh

Scope

The gate covers npm/npx only. Deliberately out of scope (clean extension seams left where natural): PyPI/pip, pnpm/yarn/bun clients, cross-registry confusion checks, and any review of agent-written code.

License

MIT — see LICENSE. Built to guard the installs you didn't write.

Keywords

npm

FAQs

Package last updated on 26 Jul 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts