slopguard-cli 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. slopguard_cli-0.1.0/.github/workflows/ci.yml +52 -0
  2. slopguard_cli-0.1.0/.github/workflows/release.yml +68 -0
  3. slopguard_cli-0.1.0/.github/workflows/slopguard.yml.example +35 -0
  4. slopguard_cli-0.1.0/.gitignore +43 -0
  5. slopguard_cli-0.1.0/.ruff.toml +25 -0
  6. slopguard_cli-0.1.0/CONTRIBUTING.md +30 -0
  7. slopguard_cli-0.1.0/LICENSE +21 -0
  8. slopguard_cli-0.1.0/Makefile +26 -0
  9. slopguard_cli-0.1.0/PKG-INFO +197 -0
  10. slopguard_cli-0.1.0/README.md +158 -0
  11. slopguard_cli-0.1.0/docs/ci-integration.md +106 -0
  12. slopguard_cli-0.1.0/docs/detection.md +77 -0
  13. slopguard_cli-0.1.0/docs/usage.md +103 -0
  14. slopguard_cli-0.1.0/pyproject.toml +89 -0
  15. slopguard_cli-0.1.0/scripts/generate_seed_data.py +540 -0
  16. slopguard_cli-0.1.0/slopguard/__init__.py +7 -0
  17. slopguard_cli-0.1.0/slopguard/__main__.py +13 -0
  18. slopguard_cli-0.1.0/slopguard/cli.py +321 -0
  19. slopguard_cli-0.1.0/slopguard/config.py +139 -0
  20. slopguard_cli-0.1.0/slopguard/data/__init__.py +40 -0
  21. slopguard_cli-0.1.0/slopguard/data/hallucinations_seed.json +5603 -0
  22. slopguard_cli-0.1.0/slopguard/data/popular_packages.json +2007 -0
  23. slopguard_cli-0.1.0/slopguard/models.py +133 -0
  24. slopguard_cli-0.1.0/slopguard/parsers/__init__.py +9 -0
  25. slopguard_cli-0.1.0/slopguard/parsers/base.py +28 -0
  26. slopguard_cli-0.1.0/slopguard/parsers/npm.py +146 -0
  27. slopguard_cli-0.1.0/slopguard/parsers/python.py +269 -0
  28. slopguard_cli-0.1.0/slopguard/registry/__init__.py +14 -0
  29. slopguard_cli-0.1.0/slopguard/registry/base.py +107 -0
  30. slopguard_cli-0.1.0/slopguard/registry/npm.py +78 -0
  31. slopguard_cli-0.1.0/slopguard/registry/pypi.py +99 -0
  32. slopguard_cli-0.1.0/slopguard/report/__init__.py +8 -0
  33. slopguard_cli-0.1.0/slopguard/report/json.py +17 -0
  34. slopguard_cli-0.1.0/slopguard/report/terminal.py +87 -0
  35. slopguard_cli-0.1.0/slopguard/scoring/__init__.py +7 -0
  36. slopguard_cli-0.1.0/slopguard/scoring/engine.py +235 -0
  37. slopguard_cli-0.1.0/slopguard/scoring/signals.py +183 -0
  38. slopguard_cli-0.1.0/slopguard/update.py +15 -0
  39. slopguard_cli-0.1.0/tests/__init__.py +0 -0
  40. slopguard_cli-0.1.0/tests/conftest.py +12 -0
  41. slopguard_cli-0.1.0/tests/fixtures/.slopguard.yaml +11 -0
  42. slopguard_cli-0.1.0/tests/fixtures/package.json +15 -0
  43. slopguard_cli-0.1.0/tests/fixtures/pyproject.toml +14 -0
  44. slopguard_cli-0.1.0/tests/fixtures/requirements.txt +5 -0
  45. slopguard_cli-0.1.0/tests/test_cli.py +186 -0
  46. slopguard_cli-0.1.0/tests/test_misc.py +176 -0
  47. slopguard_cli-0.1.0/tests/test_parsers_npm.py +126 -0
  48. slopguard_cli-0.1.0/tests/test_parsers_python.py +140 -0
  49. slopguard_cli-0.1.0/tests/test_registry_npm.py +85 -0
  50. slopguard_cli-0.1.0/tests/test_registry_pypi.py +66 -0
  51. slopguard_cli-0.1.0/tests/test_scoring_engine.py +240 -0
@@ -0,0 +1,52 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches: [main]
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ python-version: ["3.11", "3.12"]
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: ${{ matrix.python-version }}
20
+ cache: pip
21
+ - name: Install
22
+ run: |
23
+ python -m pip install --upgrade pip
24
+ pip install -e ".[dev]"
25
+ - name: Lint
26
+ run: |
27
+ ruff check slopguard/ tests/
28
+ ruff format --check slopguard/ tests/
29
+ - name: Type check
30
+ run: mypy --strict slopguard/
31
+ - name: Test
32
+ run: pytest -q --cov=slopguard --cov-report=term --cov-fail-under=85
33
+ - name: Build wheel
34
+ if: matrix.python-version == '3.11'
35
+ run: |
36
+ pip install build
37
+ python -m build
38
+ - name: Smoke-test wheel install
39
+ if: matrix.python-version == '3.11'
40
+ run: |
41
+ python -m venv /tmp/wheel-venv
42
+ /tmp/wheel-venv/bin/pip install dist/*.whl
43
+ /tmp/wheel-venv/bin/slopguard version
44
+ # Fixtures contain known hallucinations on purpose, so scan exits 1.
45
+ # Assert that, then validate the JSON payload.
46
+ set +e
47
+ /tmp/wheel-venv/bin/slopguard scan tests/fixtures --no-network --format json --output /tmp/report.json
48
+ rc=$?
49
+ set -e
50
+ test "$rc" = "1"
51
+ test "$(jq -r .slopguard_version /tmp/report.json)" = "0.1.0"
52
+ test "$(jq -r .summary.hallucinated /tmp/report.json)" -ge 1
@@ -0,0 +1,68 @@
1
+ name: Release to PyPI
2
+
3
+ # Triggered when a GitHub release is published, or manually via the Actions UI.
4
+ # The first run will register the project on PyPI under whoever owns the
5
+ # trusted publisher configuration on the PyPI side.
6
+ #
7
+ # Setup once on PyPI: https://pypi.org/manage/account/publishing/ → "Add a new
8
+ # pending publisher" with:
9
+ # PyPI Project Name: slopguard
10
+ # Owner: hariomunknownslab
11
+ # Repository name: slopguard
12
+ # Workflow name: release.yml
13
+ # Environment name: (leave blank)
14
+ # After the first successful run, PyPI promotes the pending publisher to a real
15
+ # project-scoped trusted publisher; no token is ever stored.
16
+
17
+ on:
18
+ release:
19
+ types: [published]
20
+ workflow_dispatch:
21
+ inputs:
22
+ ref:
23
+ description: "Git ref to publish (tag, branch, or SHA). Defaults to the workflow's ref."
24
+ required: false
25
+ default: ""
26
+
27
+ permissions:
28
+ contents: read
29
+
30
+ jobs:
31
+ build:
32
+ name: Build sdist + wheel
33
+ runs-on: ubuntu-latest
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+ with:
37
+ ref: ${{ inputs.ref || github.ref }}
38
+ - uses: actions/setup-python@v5
39
+ with:
40
+ python-version: "3.11"
41
+ - name: Install build
42
+ run: python -m pip install --upgrade build
43
+ - name: Build
44
+ run: python -m build
45
+ - name: Verify metadata
46
+ run: |
47
+ python -m pip install twine
48
+ python -m twine check dist/*
49
+ - name: Upload artifact
50
+ uses: actions/upload-artifact@v4
51
+ with:
52
+ name: pypi-dists
53
+ path: dist/
54
+
55
+ publish:
56
+ name: Publish to PyPI
57
+ needs: build
58
+ runs-on: ubuntu-latest
59
+ permissions:
60
+ id-token: write # required for OIDC trusted publishing
61
+ steps:
62
+ - name: Download artifact
63
+ uses: actions/download-artifact@v4
64
+ with:
65
+ name: pypi-dists
66
+ path: dist/
67
+ - name: Publish
68
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,35 @@
1
+ # Copy this file into your own repository as `.github/workflows/slopguard.yml`.
2
+ #
3
+ # It installs SlopGuard, scans the repository on every pull request and push to
4
+ # main, and uploads the JSON report as a build artifact. The job fails the
5
+ # build whenever SlopGuard exits non-zero.
6
+
7
+ name: SlopGuard
8
+
9
+ on:
10
+ pull_request:
11
+ push:
12
+ branches: [main]
13
+
14
+ jobs:
15
+ slopguard:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - uses: actions/setup-python@v5
21
+ with:
22
+ python-version: "3.11"
23
+
24
+ - name: Install SlopGuard
25
+ run: pip install slopguard-cli
26
+
27
+ - name: Scan
28
+ run: slopguard scan . --format json --output slopguard-report.json
29
+
30
+ - name: Upload report
31
+ if: always()
32
+ uses: actions/upload-artifact@v4
33
+ with:
34
+ name: slopguard-report
35
+ path: slopguard-report.json
@@ -0,0 +1,43 @@
1
+ # Byte-compiled / cache
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+
7
+ # Distribution / packaging
8
+ build/
9
+ dist/
10
+ *.egg-info/
11
+ *.egg
12
+ .eggs/
13
+
14
+ # Virtual envs
15
+ .venv/
16
+ venv/
17
+ env/
18
+ ENV/
19
+
20
+ # Test / coverage
21
+ .pytest_cache/
22
+ .coverage
23
+ .coverage.*
24
+ htmlcov/
25
+ coverage.xml
26
+ .tox/
27
+
28
+ # Type checking
29
+ .mypy_cache/
30
+ .ruff_cache/
31
+
32
+ # Editors
33
+ .vscode/
34
+ .idea/
35
+ *.swp
36
+ *.swo
37
+
38
+ # OS
39
+ .DS_Store
40
+ Thumbs.db
41
+
42
+ # Local scan artifacts
43
+ slopguard-report.json
@@ -0,0 +1,25 @@
1
+ target-version = "py311"
2
+ line-length = 100
3
+
4
+ [lint]
5
+ select = [
6
+ "E", # pycodestyle errors
7
+ "W", # pycodestyle warnings
8
+ "F", # pyflakes
9
+ "I", # isort
10
+ "B", # bugbear
11
+ "UP", # pyupgrade
12
+ "C4", # comprehensions
13
+ "SIM", # simplify
14
+ "RUF", # ruff-specific
15
+ ]
16
+ ignore = [
17
+ "E501", # line length handled by formatter
18
+ ]
19
+
20
+ [lint.per-file-ignores]
21
+ "tests/**" = ["B011", "SIM117"]
22
+
23
+ [format]
24
+ quote-style = "double"
25
+ indent-style = "space"
@@ -0,0 +1,30 @@
1
+ # Contributing to SlopGuard
2
+
3
+ Thanks for the interest. SlopGuard is MIT-licensed and welcomes contributions.
4
+
5
+ ## Quick start
6
+
7
+ ```bash
8
+ git clone https://github.com/hariomunknownslab/slopguard
9
+ cd slopguard
10
+ python -m venv .venv && source .venv/bin/activate
11
+ make install
12
+ make all # lint, typecheck, test
13
+ ```
14
+
15
+ ## Ground rules
16
+
17
+ - `ruff check` and `ruff format` must pass cleanly.
18
+ - `mypy --strict slopguard/` must pass with zero errors.
19
+ - Test coverage on `slopguard/` must stay ≥ 85%.
20
+ - No `Any`, no unjustified `# type: ignore`.
21
+ - Do not add features outside the v0.1 scope without filing an issue first.
22
+
23
+ ## Reporting hallucinated package names
24
+
25
+ Open an issue with:
26
+ - the package name (and ecosystem),
27
+ - the LLM / tool that suggested it,
28
+ - the prompt that produced the suggestion (if shareable).
29
+
30
+ Curated reports feed the next iteration of the hallucination database.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SlopGuard
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,26 @@
1
+ .PHONY: install test lint typecheck fmt clean build all
2
+
3
+ install:
4
+ pip install -e ".[dev]"
5
+
6
+ test:
7
+ pytest -q --cov=slopguard --cov-report=term-missing
8
+
9
+ lint:
10
+ ruff check slopguard/ tests/
11
+ ruff format --check slopguard/ tests/
12
+
13
+ typecheck:
14
+ mypy --strict slopguard/
15
+
16
+ fmt:
17
+ ruff format slopguard/ tests/
18
+ ruff check --fix slopguard/ tests/
19
+
20
+ clean:
21
+ rm -rf build/ dist/ *.egg-info .pytest_cache .mypy_cache .ruff_cache .coverage htmlcov coverage.xml
22
+
23
+ build:
24
+ python -m build
25
+
26
+ all: lint typecheck test
@@ -0,0 +1,197 @@
1
+ Metadata-Version: 2.4
2
+ Name: slopguard-cli
3
+ Version: 0.1.0
4
+ Summary: Defend developers and AI coding agents against slopsquatting (hallucinated package names).
5
+ Project-URL: Homepage, https://github.com/hariomunknownslab/slopguard
6
+ Project-URL: Repository, https://github.com/hariomunknownslab/slopguard
7
+ Project-URL: Issues, https://github.com/hariomunknownslab/slopguard/issues
8
+ Author-email: SlopGuard <contact@unknownslab.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: ai,llm,package-hallucination,security,slopsquatting,supply-chain
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Security
20
+ Classifier: Topic :: Software Development :: Quality Assurance
21
+ Requires-Python: >=3.11
22
+ Requires-Dist: httpx>=0.27.0
23
+ Requires-Dist: pydantic>=2.6.0
24
+ Requires-Dist: pyyaml>=6.0.1
25
+ Requires-Dist: rich>=13.7.0
26
+ Requires-Dist: tomli>=2.0.1; python_version < '3.11'
27
+ Requires-Dist: typer>=0.12.0
28
+ Provides-Extra: dev
29
+ Requires-Dist: build>=1.2.0; extra == 'dev'
30
+ Requires-Dist: jsonschema>=4.21.0; extra == 'dev'
31
+ Requires-Dist: mypy>=1.10.0; extra == 'dev'
32
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
33
+ Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
34
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
35
+ Requires-Dist: respx>=0.21.0; extra == 'dev'
36
+ Requires-Dist: ruff>=0.4.0; extra == 'dev'
37
+ Requires-Dist: types-pyyaml>=6.0.12; extra == 'dev'
38
+ Description-Content-Type: text/markdown
39
+
40
+ # SlopGuard
41
+
42
+ [![CI](https://github.com/hariomunknownslab/slopguard/actions/workflows/ci.yml/badge.svg)](https://github.com/hariomunknownslab/slopguard/actions/workflows/ci.yml)
43
+ [![PyPI](https://img.shields.io/pypi/v/slopguard.svg)](https://pypi.org/project/slopguard/)
44
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
45
+
46
+ **Slopsquatting** is what happens when an LLM hallucinates a plausible-sounding
47
+ package name that does not exist on the public registry — and then an attacker
48
+ registers that exact name with malware so the *next* developer (or AI agent)
49
+ who follows the suggestion installs it. SlopGuard scans your project's
50
+ dependencies, flags entries that are either known LLM hallucinations or that
51
+ show the behavioural fingerprint of a slopsquat, and exits non-zero so CI
52
+ fails the build before the malware reaches `node_modules` or `site-packages`.
53
+
54
+ > SlopGuard stops AI coding agents from installing packages that LLMs hallucinated.
55
+
56
+ ## Install
57
+
58
+ ```bash
59
+ pip install slopguard-cli
60
+ # Homebrew formula ships in a later release:
61
+ # brew install slopguard
62
+ ```
63
+
64
+ > The PyPI **distribution** name is `slopguard-cli` (the name `slopguard`
65
+ > overlapped with an unrelated existing package on PyPI). The installed
66
+ > command, the Python import, and everything else stays `slopguard`.
67
+
68
+ Python 3.11+ is required.
69
+
70
+ ## Usage
71
+
72
+ ### 1. Scan the current directory
73
+
74
+ ```bash
75
+ slopguard scan
76
+ ```
77
+
78
+ SlopGuard auto-discovers `package.json`, `package-lock.json`,
79
+ `requirements.txt`, `pyproject.toml`, and `Pipfile` (up to two levels deep),
80
+ probes each name against the public registry, and prints a Rich table:
81
+
82
+ ```text
83
+ SlopGuard v0.1.0 — scanning /home/dev/myproj
84
+
85
+ Detected manifests:
86
+ • package.json (npm, 32 deps)
87
+ • requirements.txt (pypi, 15 deps)
88
+
89
+ Scanned 47 dependencies in 3.1s.
90
+
91
+ ┏━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
92
+ ┃ Package ┃ Risk ┃ Reason ┃
93
+ ┡━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
94
+ │ react-codeshift │ HALLUCIN. │ Matched seed DB entry; recurrence 0.71. │
95
+ │ langchain-helpers │ SUSPICIOUS │ Created 14 days ago, 48 downloads, new auth. │
96
+ │ openai-utils │ SUSPICIOUS │ Levenshtein 2 from popular package 'openai'. │
97
+ │ requests │ CLEAN │ Established package. │
98
+ └────────────────────┴────────────┴──────────────────────────────────────────────┘
99
+
100
+ Summary: 1 hallucinated, 2 suspicious, 44 clean, 0 error(s).
101
+ Exit code: 1
102
+ ```
103
+
104
+ ### 2. Scan a specific path
105
+
106
+ ```bash
107
+ slopguard scan ./mono/services/api
108
+ ```
109
+
110
+ ### 3. CI mode — JSON output, strict failure threshold
111
+
112
+ ```bash
113
+ slopguard scan --format json --output report.json --fail-on hallucinated
114
+ ```
115
+
116
+ See [`.github/workflows/slopguard.yml.example`](.github/workflows/slopguard.yml.example)
117
+ for a drop-in GitHub Actions workflow and [`docs/ci-integration.md`](docs/ci-integration.md)
118
+ for details on other CI providers.
119
+
120
+ ## How it works
121
+
122
+ For every dependency, SlopGuard computes a small set of independent signals
123
+ and combines them into a single risk score in `[0.0, 1.0]`:
124
+
125
+ - **Hallucination-DB hit** (weight 0.90) — exact match in an embedded seed
126
+ database of names known to be hallucinated by major LLMs.
127
+ - **Registry not found** (0.85) — the registry returns 404 for the name. The
128
+ most common slopsquat shape: a name that doesn't exist *yet*.
129
+ - **Very recently / recently published** (0.35 / 0.20) — first release < 7
130
+ days / < 30 days old.
131
+ - **Low downloads** (0.15) — < 100 downloads in the last month (npm) or last
132
+ week (PyPI).
133
+ - **New publisher** (0.20) and **single-release new account** (0.30) — a
134
+ brand-new account whose only release is the package you're about to install.
135
+ - **Levenshtein typo** (0.25) — name is 1–2 edits away from a top-1000
136
+ popular package (likely a typosquat).
137
+ - **Suspicious name pattern** (0.10) — matches a classic LLM-hallucination
138
+ shape like `<stem>-helpers`, `<stem>-utils`, `<stem>-async`, `<stem>-pro`.
139
+
140
+ The default cutoffs map scores `≥ 0.85` → **hallucinated**, `≥ 0.40` →
141
+ **suspicious**, else **clean**. Both thresholds are tunable in
142
+ `.slopguard.yaml`. See [`docs/detection.md`](docs/detection.md) for the full
143
+ table, the order of operations, and edge cases.
144
+
145
+ ## Configuration
146
+
147
+ `.slopguard.yaml`, picked up automatically from the scan target or any
148
+ ancestor (up to 3 levels):
149
+
150
+ ```yaml
151
+ ignore:
152
+ packages: ["internal-tool"]
153
+ patterns: ["^@mycompany/"]
154
+
155
+ fail_on: suspicious # any | hallucinated | suspicious | none
156
+
157
+ network:
158
+ enabled: true
159
+ timeout_seconds: 5
160
+ concurrency: 16
161
+
162
+ scoring:
163
+ suspicious_min_score: 0.4
164
+ hallucinated_min_score: 0.85
165
+ ```
166
+
167
+ CLI flags override the file. See [`docs/usage.md`](docs/usage.md) for the full
168
+ reference.
169
+
170
+ ## What it does NOT do (v0.1)
171
+
172
+ - No live LLM probing — the hallucination database is a static seed for v0.1.
173
+ - No SaaS dashboard, no auth, no billing, no telemetry to any remote server.
174
+ - No tarpit registry, no defensive package registration.
175
+ - No Cursor / Claude Code / Copilot IDE plugins.
176
+ - No support for crates.io, pkg.go.dev, Maven Central, RubyGems, NuGet —
177
+ Python and JavaScript only.
178
+ - No license scanning, no CVE matching, no SBOM generation.
179
+ - No remote configuration, no SaaS API client.
180
+
181
+ The full v0.2+ roadmap is tracked in the build spec, section 14.
182
+
183
+ ## Privacy & trust
184
+
185
+ SlopGuard makes **only** the network calls you opt into (the public registry
186
+ probes against `registry.npmjs.org` and `pypi.org`). No analytics, no
187
+ ping-home, no telemetry. The trust model is the moat: run `--no-network` if
188
+ you want to be sure.
189
+
190
+ ## Contributing
191
+
192
+ See [CONTRIBUTING.md](CONTRIBUTING.md). PRs welcome — especially curated
193
+ additions to the hallucination database.
194
+
195
+ ## License
196
+
197
+ MIT. Copyright © 2026 SlopGuard. See [LICENSE](LICENSE).
@@ -0,0 +1,158 @@
1
+ # SlopGuard
2
+
3
+ [![CI](https://github.com/hariomunknownslab/slopguard/actions/workflows/ci.yml/badge.svg)](https://github.com/hariomunknownslab/slopguard/actions/workflows/ci.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/slopguard.svg)](https://pypi.org/project/slopguard/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ **Slopsquatting** is what happens when an LLM hallucinates a plausible-sounding
8
+ package name that does not exist on the public registry — and then an attacker
9
+ registers that exact name with malware so the *next* developer (or AI agent)
10
+ who follows the suggestion installs it. SlopGuard scans your project's
11
+ dependencies, flags entries that are either known LLM hallucinations or that
12
+ show the behavioural fingerprint of a slopsquat, and exits non-zero so CI
13
+ fails the build before the malware reaches `node_modules` or `site-packages`.
14
+
15
+ > SlopGuard stops AI coding agents from installing packages that LLMs hallucinated.
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ pip install slopguard-cli
21
+ # Homebrew formula ships in a later release:
22
+ # brew install slopguard
23
+ ```
24
+
25
+ > The PyPI **distribution** name is `slopguard-cli` (the name `slopguard`
26
+ > overlapped with an unrelated existing package on PyPI). The installed
27
+ > command, the Python import, and everything else stays `slopguard`.
28
+
29
+ Python 3.11+ is required.
30
+
31
+ ## Usage
32
+
33
+ ### 1. Scan the current directory
34
+
35
+ ```bash
36
+ slopguard scan
37
+ ```
38
+
39
+ SlopGuard auto-discovers `package.json`, `package-lock.json`,
40
+ `requirements.txt`, `pyproject.toml`, and `Pipfile` (up to two levels deep),
41
+ probes each name against the public registry, and prints a Rich table:
42
+
43
+ ```text
44
+ SlopGuard v0.1.0 — scanning /home/dev/myproj
45
+
46
+ Detected manifests:
47
+ • package.json (npm, 32 deps)
48
+ • requirements.txt (pypi, 15 deps)
49
+
50
+ Scanned 47 dependencies in 3.1s.
51
+
52
+ ┏━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
53
+ ┃ Package ┃ Risk ┃ Reason ┃
54
+ ┡━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
55
+ │ react-codeshift │ HALLUCIN. │ Matched seed DB entry; recurrence 0.71. │
56
+ │ langchain-helpers │ SUSPICIOUS │ Created 14 days ago, 48 downloads, new auth. │
57
+ │ openai-utils │ SUSPICIOUS │ Levenshtein 2 from popular package 'openai'. │
58
+ │ requests │ CLEAN │ Established package. │
59
+ └────────────────────┴────────────┴──────────────────────────────────────────────┘
60
+
61
+ Summary: 1 hallucinated, 2 suspicious, 44 clean, 0 error(s).
62
+ Exit code: 1
63
+ ```
64
+
65
+ ### 2. Scan a specific path
66
+
67
+ ```bash
68
+ slopguard scan ./mono/services/api
69
+ ```
70
+
71
+ ### 3. CI mode — JSON output, strict failure threshold
72
+
73
+ ```bash
74
+ slopguard scan --format json --output report.json --fail-on hallucinated
75
+ ```
76
+
77
+ See [`.github/workflows/slopguard.yml.example`](.github/workflows/slopguard.yml.example)
78
+ for a drop-in GitHub Actions workflow and [`docs/ci-integration.md`](docs/ci-integration.md)
79
+ for details on other CI providers.
80
+
81
+ ## How it works
82
+
83
+ For every dependency, SlopGuard computes a small set of independent signals
84
+ and combines them into a single risk score in `[0.0, 1.0]`:
85
+
86
+ - **Hallucination-DB hit** (weight 0.90) — exact match in an embedded seed
87
+ database of names known to be hallucinated by major LLMs.
88
+ - **Registry not found** (0.85) — the registry returns 404 for the name. The
89
+ most common slopsquat shape: a name that doesn't exist *yet*.
90
+ - **Very recently / recently published** (0.35 / 0.20) — first release < 7
91
+ days / < 30 days old.
92
+ - **Low downloads** (0.15) — < 100 downloads in the last month (npm) or last
93
+ week (PyPI).
94
+ - **New publisher** (0.20) and **single-release new account** (0.30) — a
95
+ brand-new account whose only release is the package you're about to install.
96
+ - **Levenshtein typo** (0.25) — name is 1–2 edits away from a top-1000
97
+ popular package (likely a typosquat).
98
+ - **Suspicious name pattern** (0.10) — matches a classic LLM-hallucination
99
+ shape like `<stem>-helpers`, `<stem>-utils`, `<stem>-async`, `<stem>-pro`.
100
+
101
+ The default cutoffs map scores `≥ 0.85` → **hallucinated**, `≥ 0.40` →
102
+ **suspicious**, else **clean**. Both thresholds are tunable in
103
+ `.slopguard.yaml`. See [`docs/detection.md`](docs/detection.md) for the full
104
+ table, the order of operations, and edge cases.
105
+
106
+ ## Configuration
107
+
108
+ `.slopguard.yaml`, picked up automatically from the scan target or any
109
+ ancestor (up to 3 levels):
110
+
111
+ ```yaml
112
+ ignore:
113
+ packages: ["internal-tool"]
114
+ patterns: ["^@mycompany/"]
115
+
116
+ fail_on: suspicious # any | hallucinated | suspicious | none
117
+
118
+ network:
119
+ enabled: true
120
+ timeout_seconds: 5
121
+ concurrency: 16
122
+
123
+ scoring:
124
+ suspicious_min_score: 0.4
125
+ hallucinated_min_score: 0.85
126
+ ```
127
+
128
+ CLI flags override the file. See [`docs/usage.md`](docs/usage.md) for the full
129
+ reference.
130
+
131
+ ## What it does NOT do (v0.1)
132
+
133
+ - No live LLM probing — the hallucination database is a static seed for v0.1.
134
+ - No SaaS dashboard, no auth, no billing, no telemetry to any remote server.
135
+ - No tarpit registry, no defensive package registration.
136
+ - No Cursor / Claude Code / Copilot IDE plugins.
137
+ - No support for crates.io, pkg.go.dev, Maven Central, RubyGems, NuGet —
138
+ Python and JavaScript only.
139
+ - No license scanning, no CVE matching, no SBOM generation.
140
+ - No remote configuration, no SaaS API client.
141
+
142
+ The full v0.2+ roadmap is tracked in the build spec, section 14.
143
+
144
+ ## Privacy & trust
145
+
146
+ SlopGuard makes **only** the network calls you opt into (the public registry
147
+ probes against `registry.npmjs.org` and `pypi.org`). No analytics, no
148
+ ping-home, no telemetry. The trust model is the moat: run `--no-network` if
149
+ you want to be sure.
150
+
151
+ ## Contributing
152
+
153
+ See [CONTRIBUTING.md](CONTRIBUTING.md). PRs welcome — especially curated
154
+ additions to the hallucination database.
155
+
156
+ ## License
157
+
158
+ MIT. Copyright © 2026 SlopGuard. See [LICENSE](LICENSE).