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.
- slopguard_cli-0.1.0/.github/workflows/ci.yml +52 -0
- slopguard_cli-0.1.0/.github/workflows/release.yml +68 -0
- slopguard_cli-0.1.0/.github/workflows/slopguard.yml.example +35 -0
- slopguard_cli-0.1.0/.gitignore +43 -0
- slopguard_cli-0.1.0/.ruff.toml +25 -0
- slopguard_cli-0.1.0/CONTRIBUTING.md +30 -0
- slopguard_cli-0.1.0/LICENSE +21 -0
- slopguard_cli-0.1.0/Makefile +26 -0
- slopguard_cli-0.1.0/PKG-INFO +197 -0
- slopguard_cli-0.1.0/README.md +158 -0
- slopguard_cli-0.1.0/docs/ci-integration.md +106 -0
- slopguard_cli-0.1.0/docs/detection.md +77 -0
- slopguard_cli-0.1.0/docs/usage.md +103 -0
- slopguard_cli-0.1.0/pyproject.toml +89 -0
- slopguard_cli-0.1.0/scripts/generate_seed_data.py +540 -0
- slopguard_cli-0.1.0/slopguard/__init__.py +7 -0
- slopguard_cli-0.1.0/slopguard/__main__.py +13 -0
- slopguard_cli-0.1.0/slopguard/cli.py +321 -0
- slopguard_cli-0.1.0/slopguard/config.py +139 -0
- slopguard_cli-0.1.0/slopguard/data/__init__.py +40 -0
- slopguard_cli-0.1.0/slopguard/data/hallucinations_seed.json +5603 -0
- slopguard_cli-0.1.0/slopguard/data/popular_packages.json +2007 -0
- slopguard_cli-0.1.0/slopguard/models.py +133 -0
- slopguard_cli-0.1.0/slopguard/parsers/__init__.py +9 -0
- slopguard_cli-0.1.0/slopguard/parsers/base.py +28 -0
- slopguard_cli-0.1.0/slopguard/parsers/npm.py +146 -0
- slopguard_cli-0.1.0/slopguard/parsers/python.py +269 -0
- slopguard_cli-0.1.0/slopguard/registry/__init__.py +14 -0
- slopguard_cli-0.1.0/slopguard/registry/base.py +107 -0
- slopguard_cli-0.1.0/slopguard/registry/npm.py +78 -0
- slopguard_cli-0.1.0/slopguard/registry/pypi.py +99 -0
- slopguard_cli-0.1.0/slopguard/report/__init__.py +8 -0
- slopguard_cli-0.1.0/slopguard/report/json.py +17 -0
- slopguard_cli-0.1.0/slopguard/report/terminal.py +87 -0
- slopguard_cli-0.1.0/slopguard/scoring/__init__.py +7 -0
- slopguard_cli-0.1.0/slopguard/scoring/engine.py +235 -0
- slopguard_cli-0.1.0/slopguard/scoring/signals.py +183 -0
- slopguard_cli-0.1.0/slopguard/update.py +15 -0
- slopguard_cli-0.1.0/tests/__init__.py +0 -0
- slopguard_cli-0.1.0/tests/conftest.py +12 -0
- slopguard_cli-0.1.0/tests/fixtures/.slopguard.yaml +11 -0
- slopguard_cli-0.1.0/tests/fixtures/package.json +15 -0
- slopguard_cli-0.1.0/tests/fixtures/pyproject.toml +14 -0
- slopguard_cli-0.1.0/tests/fixtures/requirements.txt +5 -0
- slopguard_cli-0.1.0/tests/test_cli.py +186 -0
- slopguard_cli-0.1.0/tests/test_misc.py +176 -0
- slopguard_cli-0.1.0/tests/test_parsers_npm.py +126 -0
- slopguard_cli-0.1.0/tests/test_parsers_python.py +140 -0
- slopguard_cli-0.1.0/tests/test_registry_npm.py +85 -0
- slopguard_cli-0.1.0/tests/test_registry_pypi.py +66 -0
- 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
|
+
[](https://github.com/hariomunknownslab/slopguard/actions/workflows/ci.yml)
|
|
43
|
+
[](https://pypi.org/project/slopguard/)
|
|
44
|
+
[](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
|
+
[](https://github.com/hariomunknownslab/slopguard/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/slopguard/)
|
|
5
|
+
[](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).
|