slopguard-cli 0.1.0__tar.gz → 0.1.1__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 → slopguard_cli-0.1.1}/.gitignore +11 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/PKG-INFO +3 -3
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/README.md +2 -1
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/pyproject.toml +1 -1
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/slopguard/__init__.py +1 -1
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/tests/test_cli.py +2 -2
- slopguard_cli-0.1.0/.github/workflows/ci.yml +0 -52
- slopguard_cli-0.1.0/.github/workflows/release.yml +0 -68
- slopguard_cli-0.1.0/.github/workflows/slopguard.yml.example +0 -35
- slopguard_cli-0.1.0/CONTRIBUTING.md +0 -30
- slopguard_cli-0.1.0/LICENSE +0 -21
- slopguard_cli-0.1.0/docs/ci-integration.md +0 -106
- slopguard_cli-0.1.0/docs/detection.md +0 -77
- slopguard_cli-0.1.0/docs/usage.md +0 -103
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/.ruff.toml +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/Makefile +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/scripts/generate_seed_data.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/slopguard/__main__.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/slopguard/cli.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/slopguard/config.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/slopguard/data/__init__.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/slopguard/data/hallucinations_seed.json +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/slopguard/data/popular_packages.json +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/slopguard/models.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/slopguard/parsers/__init__.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/slopguard/parsers/base.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/slopguard/parsers/npm.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/slopguard/parsers/python.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/slopguard/registry/__init__.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/slopguard/registry/base.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/slopguard/registry/npm.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/slopguard/registry/pypi.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/slopguard/report/__init__.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/slopguard/report/json.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/slopguard/report/terminal.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/slopguard/scoring/__init__.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/slopguard/scoring/engine.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/slopguard/scoring/signals.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/slopguard/update.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/tests/__init__.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/tests/conftest.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/tests/fixtures/.slopguard.yaml +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/tests/fixtures/package.json +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/tests/fixtures/pyproject.toml +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/tests/fixtures/requirements.txt +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/tests/test_misc.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/tests/test_parsers_npm.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/tests/test_parsers_python.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/tests/test_registry_npm.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/tests/test_registry_pypi.py +0 -0
- {slopguard_cli-0.1.0 → slopguard_cli-0.1.1}/tests/test_scoring_engine.py +0 -0
|
@@ -41,3 +41,14 @@ Thumbs.db
|
|
|
41
41
|
|
|
42
42
|
# Local scan artifacts
|
|
43
43
|
slopguard-report.json
|
|
44
|
+
|
|
45
|
+
# Node / Next.js (Phase A onwards)
|
|
46
|
+
node_modules/
|
|
47
|
+
.next/
|
|
48
|
+
out/
|
|
49
|
+
.turbo/
|
|
50
|
+
|
|
51
|
+
# Local secrets — never commit. Use Doppler / 1Password / Fly.io secrets.
|
|
52
|
+
.env
|
|
53
|
+
.env.*
|
|
54
|
+
!.env.example
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: slopguard-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: Defend developers and AI coding agents against slopsquatting (hallucinated package names).
|
|
5
5
|
Project-URL: Homepage, https://github.com/hariomunknownslab/slopguard
|
|
6
6
|
Project-URL: Repository, https://github.com/hariomunknownslab/slopguard
|
|
7
7
|
Project-URL: Issues, https://github.com/hariomunknownslab/slopguard/issues
|
|
8
8
|
Author-email: SlopGuard <contact@unknownslab.com>
|
|
9
9
|
License: MIT
|
|
10
|
-
License-File: LICENSE
|
|
11
10
|
Keywords: ai,llm,package-hallucination,security,slopsquatting,supply-chain
|
|
12
11
|
Classifier: Development Status :: 3 - Alpha
|
|
13
12
|
Classifier: Intended Audience :: Developers
|
|
@@ -40,7 +39,8 @@ Description-Content-Type: text/markdown
|
|
|
40
39
|
# SlopGuard
|
|
41
40
|
|
|
42
41
|
[](https://github.com/hariomunknownslab/slopguard/actions/workflows/ci.yml)
|
|
43
|
-
[](https://pypi.org/project/slopguard/)
|
|
42
|
+
[](https://pypi.org/project/slopguard-cli/)
|
|
43
|
+
[](https://pypi.org/project/slopguard-cli/)
|
|
44
44
|
[](https://opensource.org/licenses/MIT)
|
|
45
45
|
|
|
46
46
|
**Slopsquatting** is what happens when an LLM hallucinates a plausible-sounding
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# SlopGuard
|
|
2
2
|
|
|
3
3
|
[](https://github.com/hariomunknownslab/slopguard/actions/workflows/ci.yml)
|
|
4
|
-
[](https://pypi.org/project/slopguard/)
|
|
4
|
+
[](https://pypi.org/project/slopguard-cli/)
|
|
5
|
+
[](https://pypi.org/project/slopguard-cli/)
|
|
5
6
|
[](https://opensource.org/licenses/MIT)
|
|
6
7
|
|
|
7
8
|
**Slopsquatting** is what happens when an LLM hallucinates a plausible-sounding
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "slopguard-cli"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.1"
|
|
8
8
|
description = "Defend developers and AI coding agents against slopsquatting (hallucinated package names)."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -107,7 +107,7 @@ def runner() -> CliRunner:
|
|
|
107
107
|
def test_version_subcommand(runner: CliRunner) -> None:
|
|
108
108
|
result = runner.invoke(app, ["version"])
|
|
109
109
|
assert result.exit_code == 0
|
|
110
|
-
assert result.stdout.strip() == "0.1.
|
|
110
|
+
assert result.stdout.strip() == "0.1.1"
|
|
111
111
|
|
|
112
112
|
|
|
113
113
|
def test_scan_fixtures_no_network_terminal(runner: CliRunner, fixtures_dir: Path) -> None:
|
|
@@ -183,4 +183,4 @@ def test_module_main_runs() -> None:
|
|
|
183
183
|
check=False,
|
|
184
184
|
)
|
|
185
185
|
assert result.returncode == 0
|
|
186
|
-
assert result.stdout.strip() == "0.1.
|
|
186
|
+
assert result.stdout.strip() == "0.1.1"
|
|
@@ -1,52 +0,0 @@
|
|
|
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
|
|
@@ -1,68 +0,0 @@
|
|
|
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
|
|
@@ -1,35 +0,0 @@
|
|
|
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
|
|
@@ -1,30 +0,0 @@
|
|
|
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.
|
slopguard_cli-0.1.0/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
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.
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
# CI integration
|
|
2
|
-
|
|
3
|
-
SlopGuard is built to fail your CI before a slopsquatted package reaches
|
|
4
|
-
`node_modules` or `site-packages`.
|
|
5
|
-
|
|
6
|
-
## GitHub Actions
|
|
7
|
-
|
|
8
|
-
Copy `.github/workflows/slopguard.yml.example` into your repo as
|
|
9
|
-
`.github/workflows/slopguard.yml`:
|
|
10
|
-
|
|
11
|
-
```yaml
|
|
12
|
-
name: SlopGuard
|
|
13
|
-
on:
|
|
14
|
-
pull_request:
|
|
15
|
-
push:
|
|
16
|
-
branches: [main]
|
|
17
|
-
|
|
18
|
-
jobs:
|
|
19
|
-
slopguard:
|
|
20
|
-
runs-on: ubuntu-latest
|
|
21
|
-
steps:
|
|
22
|
-
- uses: actions/checkout@v4
|
|
23
|
-
- uses: actions/setup-python@v5
|
|
24
|
-
with:
|
|
25
|
-
python-version: "3.11"
|
|
26
|
-
- run: pip install slopguard-cli
|
|
27
|
-
- run: slopguard scan . --format json --output slopguard-report.json
|
|
28
|
-
- uses: actions/upload-artifact@v4
|
|
29
|
-
if: always()
|
|
30
|
-
with:
|
|
31
|
-
name: slopguard-report
|
|
32
|
-
path: slopguard-report.json
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
The workflow fails the job whenever SlopGuard exits non-zero. Tune
|
|
36
|
-
`--fail-on hallucinated` if you want suspicious-tier findings to surface as
|
|
37
|
-
warnings rather than block the build.
|
|
38
|
-
|
|
39
|
-
## Pre-commit
|
|
40
|
-
|
|
41
|
-
SlopGuard isn't packaged as a pre-commit hook in v0.1 (see
|
|
42
|
-
[deferred items](https://github.com/hariomunknownslab/slopguard) on the v0.2 roadmap),
|
|
43
|
-
but a local hook is one line:
|
|
44
|
-
|
|
45
|
-
```yaml
|
|
46
|
-
# .pre-commit-config.yaml
|
|
47
|
-
repos:
|
|
48
|
-
- repo: local
|
|
49
|
-
hooks:
|
|
50
|
-
- id: slopguard
|
|
51
|
-
name: slopguard scan
|
|
52
|
-
entry: slopguard scan
|
|
53
|
-
language: system
|
|
54
|
-
pass_filenames: false
|
|
55
|
-
always_run: true
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## Other CI providers
|
|
59
|
-
|
|
60
|
-
`slopguard scan` is provider-agnostic. Anything that can install Python and
|
|
61
|
-
run a shell command works:
|
|
62
|
-
|
|
63
|
-
```bash
|
|
64
|
-
pip install slopguard-cli
|
|
65
|
-
slopguard scan . --format json --output slopguard-report.json
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
If a finding lands above the configured threshold, SlopGuard exits 1 and your
|
|
69
|
-
pipeline fails.
|
|
70
|
-
|
|
71
|
-
## CI-friendly flags
|
|
72
|
-
|
|
73
|
-
- `--format json --output <file>` — machine-readable artifact you can hand to
|
|
74
|
-
a downstream summariser or commenter.
|
|
75
|
-
- `--fail-on hallucinated` — strictest tier. Useful when you do not want
|
|
76
|
-
suspicious findings to break the build but still want them in the artifact.
|
|
77
|
-
- `--no-network` — useful in air-gapped CI. Detection falls back to the
|
|
78
|
-
embedded seed DB + name-pattern signal.
|
|
79
|
-
- `--concurrency` — bump up to 32 or 64 on a fast runner with many
|
|
80
|
-
dependencies; the public registries tolerate it.
|
|
81
|
-
|
|
82
|
-
## Reading the JSON
|
|
83
|
-
|
|
84
|
-
```json
|
|
85
|
-
{
|
|
86
|
-
"slopguard_version": "0.1.0",
|
|
87
|
-
"summary": { "total": 47, "clean": 44, "suspicious": 2, "hallucinated": 1, "errors": 0 },
|
|
88
|
-
"findings": [
|
|
89
|
-
{
|
|
90
|
-
"name": "react-codeshift",
|
|
91
|
-
"ecosystem": "npm",
|
|
92
|
-
"manifest": "package.json",
|
|
93
|
-
"risk": "hallucinated",
|
|
94
|
-
"score": 0.95,
|
|
95
|
-
"signals": [
|
|
96
|
-
{ "type": "hallucination_db_hit", "weight": 0.9, "detail": "Matched seed DB entry; recurrence 0.71 across 3 models." }
|
|
97
|
-
],
|
|
98
|
-
"remediation": "Remove this dependency. Verify the package your AI suggested actually exists at the source you trust."
|
|
99
|
-
}
|
|
100
|
-
],
|
|
101
|
-
"exit_code": 1
|
|
102
|
-
}
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
The schema is documented in section 8 of the v0.1 spec. Treat it as a public
|
|
106
|
-
API contract — version-bump SlopGuard if you ever need to break it.
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
# How detection works
|
|
2
|
-
|
|
3
|
-
SlopGuard scores each dependency by combining independent signals into a single
|
|
4
|
-
risk score in `[0.0, 1.0]`, then maps the score to a tier:
|
|
5
|
-
|
|
6
|
-
| Tier | Default range | Meaning |
|
|
7
|
-
|---|---|---|
|
|
8
|
-
| `hallucinated` | score ≥ 0.85 | High confidence the dependency is a hallucinated / slopsquat package. |
|
|
9
|
-
| `suspicious` | 0.40 ≤ score < 0.85 | Worth a human look before installing. |
|
|
10
|
-
| `clean` | score < 0.40 | No actionable evidence of risk. |
|
|
11
|
-
| `error` | n/a | SlopGuard could not score the dependency (e.g. registry unreachable). |
|
|
12
|
-
|
|
13
|
-
Thresholds are configurable in `.slopguard.yaml`.
|
|
14
|
-
|
|
15
|
-
## Signals
|
|
16
|
-
|
|
17
|
-
| Signal | Trigger | Weight |
|
|
18
|
-
|---|---|---|
|
|
19
|
-
| `hallucination_db_hit` | Exact match (lowercased) in the embedded seed DB. | +0.90 |
|
|
20
|
-
| `registry_not_found` | Registry returned 404 for the name. | +0.85 |
|
|
21
|
-
| `very_recently_published` | First release < 7 days ago. | +0.35 |
|
|
22
|
-
| `recently_published` | First release < 30 days ago. | +0.20 |
|
|
23
|
-
| `low_downloads` | < 100 downloads (last month for npm, last week for PyPI). | +0.15 |
|
|
24
|
-
| `new_publisher` | Publisher account < 30 days old. | +0.20 |
|
|
25
|
-
| `single_release_new_account` | Publisher's only release; account < 60 days old. | +0.30 |
|
|
26
|
-
| `levenshtein_typo` | Levenshtein distance 1–2 from a top-1000 popular package. | +0.25 |
|
|
27
|
-
| `name_pattern_suspicious` | Matches a classic hallucination shape: `<stem>-(helpers?\|utils?\|async\|pro\|kit\|sdk\|tools?\|extras?\|plus\|next\|core)`. | +0.10 |
|
|
28
|
-
|
|
29
|
-
The final score is the sum of triggered signal weights, capped at 1.0.
|
|
30
|
-
|
|
31
|
-
## Order of operations per dependency
|
|
32
|
-
|
|
33
|
-
1. **Hallucination-DB hit.** Short-circuit to `hallucinated`. Skip the registry
|
|
34
|
-
probe unless `--verbose` is set (verbose still probes for completeness in the
|
|
35
|
-
JSON report).
|
|
36
|
-
2. **`--no-network`.** Only the DB hit and `name_pattern_suspicious` apply.
|
|
37
|
-
3. **Registry probe.** Async, bounded by `--concurrency`, with timeouts and
|
|
38
|
-
exponential back-off on 5xx / 429.
|
|
39
|
-
4. **404 → `registry_not_found` + stop.** The name doesn't exist on the
|
|
40
|
-
registry; Levenshtein and name-pattern signals are still emitted for
|
|
41
|
-
diagnostic value.
|
|
42
|
-
5. **200 → full signal sweep.** Compute every applicable signal from registry
|
|
43
|
-
metadata.
|
|
44
|
-
|
|
45
|
-
## Edge cases
|
|
46
|
-
|
|
47
|
-
- **Scoped npm packages (`@org/name`):** registry probe is skipped (private /
|
|
48
|
-
internal packages are common at these names). They are scored `clean` unless
|
|
49
|
-
the seed DB has them.
|
|
50
|
-
- **Local file dependencies (`file:`, `link:`):** ignored entirely.
|
|
51
|
-
- **Git URL dependencies:** out of scope for v0.1 scoring — emitted as `clean`
|
|
52
|
-
with an informational `git_dependency` signal in the JSON output.
|
|
53
|
-
- **Version pins:** not used for v0.1 detection. Version-specific signals are
|
|
54
|
-
on the v0.2 roadmap.
|
|
55
|
-
|
|
56
|
-
## The hallucination database
|
|
57
|
-
|
|
58
|
-
`slopguard/data/hallucinations_seed.json` ships embedded in the wheel and is
|
|
59
|
-
loaded via `importlib.resources` at runtime. The current seed is **placeholder
|
|
60
|
-
data**: the production database will be assembled from academic research on
|
|
61
|
-
LLM-suggested packages plus a live probing harness. The schema is fixed at
|
|
62
|
-
`schema_version: 1`; future updates will preserve backwards compatibility.
|
|
63
|
-
|
|
64
|
-
## Popular-package lists
|
|
65
|
-
|
|
66
|
-
Top-1000 npm and PyPI names live in `slopguard/data/popular_packages.json` and
|
|
67
|
-
back the Levenshtein-typo signal. The lists are synthesised for v0.1 and will
|
|
68
|
-
be replaced with authoritative download-count rankings before release.
|
|
69
|
-
|
|
70
|
-
## What detection does *not* do (v0.1)
|
|
71
|
-
|
|
72
|
-
- No live LLM probing.
|
|
73
|
-
- No CVE / advisory matching.
|
|
74
|
-
- No license scanning, no SBOM.
|
|
75
|
-
- No supply-chain attestation (Sigstore, provenance, etc.).
|
|
76
|
-
|
|
77
|
-
See the build spec, section 14, for the full list of deferred capabilities.
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
# Usage
|
|
2
|
-
|
|
3
|
-
## Install
|
|
4
|
-
|
|
5
|
-
```bash
|
|
6
|
-
pip install slopguard-cli
|
|
7
|
-
# or
|
|
8
|
-
brew install slopguard # formula ships in dist/homebrew/ in a later release
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
The PyPI distribution name is `slopguard-cli`; the installed binary and
|
|
12
|
-
Python import are still `slopguard`.
|
|
13
|
-
|
|
14
|
-
## Commands
|
|
15
|
-
|
|
16
|
-
### `slopguard scan`
|
|
17
|
-
|
|
18
|
-
```text
|
|
19
|
-
slopguard scan [PATH] [OPTIONS]
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
| Argument / Option | Default | Notes |
|
|
23
|
-
|---|---|---|
|
|
24
|
-
| `PATH` | current working directory | A project directory or a single manifest file. |
|
|
25
|
-
| `--format` | `terminal` | `terminal` for Rich output, `json` for the wire format. |
|
|
26
|
-
| `--output` | — | Write JSON to a file (only with `--format=json`). |
|
|
27
|
-
| `--config` | auto-detected `.slopguard.yaml` | Path to a config file. SlopGuard walks up 3 parents from PATH. |
|
|
28
|
-
| `--fail-on` | `suspicious` | `any` / `hallucinated` / `suspicious` / `none` — risk level that triggers exit 1. |
|
|
29
|
-
| `--no-network` | off | Skip registry probes; rely on the seed DB + name-pattern signal. |
|
|
30
|
-
| `--timeout` | 5.0s | Per-request timeout for registry probes. |
|
|
31
|
-
| `--concurrency` | 16 | Maximum concurrent registry probes. |
|
|
32
|
-
| `--verbose` / `-v` | off | Show debug logs. With `--verbose`, DB-hit findings still probe the registry for completeness. |
|
|
33
|
-
|
|
34
|
-
### `slopguard version`
|
|
35
|
-
|
|
36
|
-
Prints the package version and exits 0.
|
|
37
|
-
|
|
38
|
-
## Exit codes
|
|
39
|
-
|
|
40
|
-
| Code | Meaning |
|
|
41
|
-
|---|---|
|
|
42
|
-
| 0 | Scan complete, no findings at or above the `--fail-on` threshold. |
|
|
43
|
-
| 1 | Scan complete, findings at or above the `--fail-on` threshold present. |
|
|
44
|
-
| 2 | Could not run (bad input, no manifests found, IO error, etc.). |
|
|
45
|
-
|
|
46
|
-
## Manifest auto-detection
|
|
47
|
-
|
|
48
|
-
`slopguard scan` walks the target directory up to 2 levels deep, skipping
|
|
49
|
-
`node_modules`, `.venv`, `venv`, `__pycache__`, `.git`, `dist`, `build`.
|
|
50
|
-
|
|
51
|
-
Recognised manifests:
|
|
52
|
-
|
|
53
|
-
- `package.json`, `package-lock.json` (npm)
|
|
54
|
-
- `requirements.txt`, `pyproject.toml`, `Pipfile` (Python)
|
|
55
|
-
|
|
56
|
-
If a `package.json` and `package-lock.json` sit in the same directory, the
|
|
57
|
-
lockfile is skipped — the manifest's directly-declared dependencies are what
|
|
58
|
-
matter for slopsquat detection.
|
|
59
|
-
|
|
60
|
-
## `.slopguard.yaml`
|
|
61
|
-
|
|
62
|
-
```yaml
|
|
63
|
-
# Every field is optional.
|
|
64
|
-
|
|
65
|
-
ignore:
|
|
66
|
-
packages: # exact package names to skip
|
|
67
|
-
- internal-tool
|
|
68
|
-
- my-private-pkg
|
|
69
|
-
patterns: # regex patterns matched against package names
|
|
70
|
-
- "^@mycompany/"
|
|
71
|
-
|
|
72
|
-
fail_on: suspicious # any | hallucinated | suspicious | none
|
|
73
|
-
|
|
74
|
-
network:
|
|
75
|
-
enabled: true
|
|
76
|
-
timeout_seconds: 5
|
|
77
|
-
concurrency: 16
|
|
78
|
-
|
|
79
|
-
scoring:
|
|
80
|
-
suspicious_min_score: 0.4
|
|
81
|
-
hallucinated_min_score: 0.85
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
Merge precedence: **CLI flags > `.slopguard.yaml` > defaults.**
|
|
85
|
-
|
|
86
|
-
## Three useful invocations
|
|
87
|
-
|
|
88
|
-
```bash
|
|
89
|
-
# 1. Default scan of the current directory.
|
|
90
|
-
slopguard scan
|
|
91
|
-
|
|
92
|
-
# 2. Scan a specific service in a monorepo.
|
|
93
|
-
slopguard scan ./mono/services/api
|
|
94
|
-
|
|
95
|
-
# 3. CI mode: JSON output, file artifact, fail only on hallucinated.
|
|
96
|
-
slopguard scan --format json --output slopguard-report.json --fail-on hallucinated
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
## Performance
|
|
100
|
-
|
|
101
|
-
A scan of ~200 dependencies completes in **under 10 seconds** on a residential
|
|
102
|
-
broadband connection with `--concurrency 16`. Registry round-trips dominate;
|
|
103
|
-
results are memoised in-process so duplicate dependency entries are free.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|