A CI gate against slopsquatting and hallucinated dependencies (Python / npm / Go).
Project description
slopgate
A CI gate against slopsquatting and hallucinated dependencies for Python, npm, and Go.
LLMs hallucinate plausible package names. Attackers pre-register them. An AI coding agent then adds the dependency with no human at the install checkpoint. slopgate is the checkpoint: it runs in CI, looks at the dependencies a change introduces, and fails the build on the ones that carry a slopsquat signature.
It has zero third-party runtime dependencies. A tool that guards your dependency surface shouldn't widen it.
✗ BLOCK pypi:reqeusts name does not exist on the registry and closely
resembles 'requests' — typo/slopsquat signature
✗ BLOCK pypi:hallucinated-pkg name does not exist on the registry —
likely a hallucinated dependency
! WARN npm:lodahs resembles popular package 'lodash'
✓ ok pypi:requests (silent unless --verbose)
Why this exists, and what makes it different
Most "AI supply chain" material tells you to write a policy and inventory your shadow AI. This is the control that actually runs in the pipeline. The hard part of a tool like this isn't the plumbing — it's the signal-to-noise discipline. A gate that flags every new package gets disabled on the first false positive, and then you have no gate. So slopgate is built around one rule: block only on the intersection of signals that actually describes the attack; warn on the rest; pass everything else silently.
The threat model (read this — the thresholds are yours to own)
A brand-new package is not inherently malicious. A name near a popular one is not inherently malicious. The slopsquat signature is the intersection:
| Verdict | Signature |
|---|---|
| BLOCK | Name doesn't exist on the registry (hallucination or typo) |
| BLOCK | Freshly registered and resembles a popular package (the bullseye) |
| BLOCK | Freshly registered and no source repo and a single version (throwaway publish) |
| WARN | Resembles a popular package but is established (could be a known squat — needs eyes, not an auto-fail) |
| WARN | Recently published, on its own |
| WARN | Registry lookup failed — never block on a transport error |
| OK | Everything else, silently |
The default thresholds live in slopgate/score.py and are deliberately
conservative:
fresh_days = 14— the window slopsquat registrations live in.young_days = 90— worth a warning, not a block.immature_versions = 2— below this, a package is "immature".
These are starting points, not gospel. They encode a risk tolerance; yours may differ. The similarity corpus in slopgate/corpus.py is the other knob: it's a curated set of the most-impersonated packages per ecosystem, kept small to hold the false-positive rate down. Swap in a real top-N-by-downloads list for production — bigger corpus catches more and warns more, and that trade-off is your call.
Install
pip install slopgate
# or, from source:
pip install git+https://github.com/nullracodes/slopgate
Use
slopgate scan . # scan every manifest under cwd
slopgate scan requirements.txt # one file
slopgate scan . --strict # warnings fail the build too
slopgate scan . --offline # name-similarity only, no registry calls
slopgate scan . --ignore my-internal-pkg another-internal-pkg
slopgate scan . --format sarif --output slopgate.sarif
In a PR, evaluate only the dependencies the PR adds — the signal-to-noise contract:
slopgate scan . --added-only --base-ref origin/main
Exit codes: 0 clean, 1 at least one BLOCK (or any WARN under --strict),
2 usage error.
Supported manifests
- PyPI:
requirements*.txt(follows-r/-cincludes, detects--hash=pins),pyproject.toml(PEP 621 and Poetry) - npm:
package.json(+ lockfile presence for hash-pinning detection) - Go:
go.mod(+go.sumpresence)
No PHP, no Java — out of scope, on purpose.
SARIF findings include the manifest line number where a manifest format exposes it (requirements, go.mod), so GitHub maps each finding to a line, not just a file.
GitHub Actions
A ready-to-use workflow ships in .github/workflows/slopgate.yml. It runs on PRs that touch a manifest, evaluates only newly-added deps, uploads SARIF to the Security tab, and fails the check on a BLOCK.
A note on building this with an AI coding agent
This tool was scaffolded with an AI coding agent; the irony is not lost on me. If you do the same, dogfood the thesis: pin every dependency the agent proposes, review the dependency diff before install, don't run package operations on auto-approve, and sandbox the agent. The most honest demo of a slopsquatting gate is catching the moment your own agent reaches for a package that doesn't exist.
License
MIT.
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 slopgate-0.1.0.tar.gz.
File metadata
- Download URL: slopgate-0.1.0.tar.gz
- Upload date:
- Size: 21.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
de357b2a8c73c710495a0d34590b94bac54e13d2dcb181fea69252b60973ed33
|
|
| MD5 |
15221b5d23c7aa33bae6217a7127ec1c
|
|
| BLAKE2b-256 |
10af6de50e89ce2771627c3bb7182c7c3e812ffffebcc07d6b14ef576b39f35f
|
Provenance
The following attestation bundles were made for slopgate-0.1.0.tar.gz:
Publisher:
release.yml on nullracodes/slopgate
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
slopgate-0.1.0.tar.gz -
Subject digest:
de357b2a8c73c710495a0d34590b94bac54e13d2dcb181fea69252b60973ed33 - Sigstore transparency entry: 1768860525
- Sigstore integration time:
-
Permalink:
nullracodes/slopgate@4b34219afd03e72d77c7efd0ea2fb4631d496d0c -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/nullracodes
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4b34219afd03e72d77c7efd0ea2fb4631d496d0c -
Trigger Event:
push
-
Statement type:
File details
Details for the file slopgate-0.1.0-py3-none-any.whl.
File metadata
- Download URL: slopgate-0.1.0-py3-none-any.whl
- Upload date:
- Size: 21.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f9efaa7ef7ba243ebc14a71689f046dfaf1a2afe72c31e5b92e7f274934b699c
|
|
| MD5 |
25e182cb4999ae213c8f75f1ea17c1a6
|
|
| BLAKE2b-256 |
2991ea7edb1ff3776fcda47d45b85d00b168c484b647811d6435a1ed9fb99717
|
Provenance
The following attestation bundles were made for slopgate-0.1.0-py3-none-any.whl:
Publisher:
release.yml on nullracodes/slopgate
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
slopgate-0.1.0-py3-none-any.whl -
Subject digest:
f9efaa7ef7ba243ebc14a71689f046dfaf1a2afe72c31e5b92e7f274934b699c - Sigstore transparency entry: 1768860854
- Sigstore integration time:
-
Permalink:
nullracodes/slopgate@4b34219afd03e72d77c7efd0ea2fb4631d496d0c -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/nullracodes
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4b34219afd03e72d77c7efd0ea2fb4631d496d0c -
Trigger Event:
push
-
Statement type: