
Shields against supply-chain, slopsquatting, and typosquatting attacks from dependencies and code.
cargo install sloppy-joe
Compromised releases can use a legitimate package name. The LiteLLM maintainer report identifies malicious versions 1.82.7 and 1.82.8 in March 2026. sloppy-joe's default 72-hour publication-age gate can reject newly published versions when you scan the resolved dependency graph with
--cior--fullbefore installation. This is a timing policy, not inspection of the malicious payload.
Research documents package hallucinations in generated code. sloppy-joe checks dependency names and metadata before installation; existence alone cannot establish that a registered package is safe.
Package names are limited to 214 bytes for npm and 1,024 bytes for other ecosystems to bound typo-analysis work. Longer names report Scan not supported (exit 2).
# Install (single static binary, no runtime dependencies)
cargo install sloppy-joe
# Or download a prebuilt binary archive from GitHub Releases
# https://github.com/brennhill/sloppy-joe/releases
# Fast local guardrail — auto-detects ecosystem from manifest files
sloppy-joe check
# Strict online scan (recommended before push / release)
sloppy-joe check --full
# Strict CI-oriented scan
sloppy-joe check --ci
# Check a specific directory
sloppy-joe check --dir ./my-project
# Check only npm dependencies
sloppy-joe check --type npm
# Check the Python runtime plus selected groups/extras
sloppy-joe check --python-groups dev,test --python-version 3.12
sloppy-joe check --python-extras docs --python-platform linux --python-version 3.12
# Enforce canonical rules and org standards via config
sloppy-joe check --config /etc/sloppy-joe/config.json
# Config from a URL (useful in CI — no secrets to manage)
sloppy-joe check --config https://raw.githubusercontent.com/yourorg/security-configs/main/sloppy-joe.json
# JSON output for CI pipelines
sloppy-joe check --json
# Review exact maintainer-change exceptions with evidence
sloppy-joe check --review-exceptions
# Create and register a safe per-repo config outside the repo
sloppy-joe init --register
# Create an ecosystem-specific greenfield starter policy
sloppy-joe init --greenfield --ecosystem npm
# Print review-only bootstrap suggestions for an npm or Cargo repo
sloppy-joe init --from-current
# Or write/register those suggestions safely outside the repo
sloppy-joe init --from-current --register
# Or write a config manually to a secure path outside the repo
sloppy-joe init > /secure/location/sloppy-joe.json
nix profile install github:brennhill/sloppy-joe
The flake on main pins the published binary release and its checksums.
Scan modes:
sloppy-joe check runs the fast local guardrail. It always enforces manifest parsing, lockfile/sync, provenance, and unsupported-source policy. If dependency or policy state changed, or the last successful full scan is older than 24 hours, it recommends sloppy-joe check --full.sloppy-joe check --full runs the strict online scan and refreshes the recorded successful full-scan state.sloppy-joe check --ci runs the same strict coverage as --full, with CI-oriented intent.sloppy-joe check evaluates the runtime profile by default. If scoped dependencies exist, it warns and tells you to pass explicit --python-groups, --python-extras, --python-platform, and/or --python-version flags for CI/build parity.sloppy-joe check output always reminds you to use --ci or --full for CI and production gating.Exit codes: 0 = no blocking issues found in the selected mode, 1 = blocking findings, 2 = unsupported scan input or runtime error. Unsupported syntax/configuration is not a malware finding.
Supports: JavaScript (npm, pnpm, Yarn, Bun), Python, Rust, Go, Ruby, PHP, JVM (Gradle/Maven), and .NET — auto-detected from manifest files.
Ecosystem guides: see docs/ecosystems/README.md for the current trust model, supported features, and fail-closed limits for each ecosystem.
| Ecosystem | Required manifest | Trusted lockfile / project state |
|---|---|---|
| JavaScript / npm | package.json | package-lock.json or npm-shrinkwrap.json; legacy npm v1 blocked by default |
| JavaScript / pnpm | package.json | pnpm-lock.yaml |
| JavaScript / Yarn | package.json | yarn.lock |
| JavaScript / Bun | package.json | bun.lock |
| Python | pyproject.toml, requirements*.txt, Pipfile, setup.cfg, or setup.py | trusted Poetry path uses poetry.lock, trusted uv path uses uv.lock, and fully hash-locked pip-tools is trusted only when the committed requirements graph binds --index-url and any --extra-index-url values exactly; repo-visible Python indexes can be allowlisted via trusted_indexes.pypi; trusted Python modes evaluate one selected install profile at a time (runtime by default, explicit groups/extras/platform/arch/version via CLI); legacy manifests allowed with warnings by default |
| Rust | Cargo.toml | Cargo.lock |
| Go | go.mod | go.sum required for external deps |
| Ruby | Gemfile | Gemfile.lock |
| PHP / Composer | composer.json | composer.lock |
| JVM / Gradle | build.gradle or build.gradle.kts | gradle.lockfile |
| JVM / Maven | pom.xml | warning-only: no trusted project-local lockfile path yet |
| .NET / NuGet | .csproj | packages.lock.json |
Scan scope: recursive scans ignore .planning by default. Configure "ignore_dirs": [".planning", "experiments"] in the external policy to exclude other directory names, or use "ignore_dirs": [] to disable configurable exclusions. See directory exclusions.
Repositories with multiple projects: Sloppy Joe runs each discovered project's dependency chain separately and combines the results. A backend, UI, admin app, and tools can use different managers and lockfiles. An incomplete ancestor npm/pnpm workspace declaration does not claim an independently locked child unless the ancestor explicitly declares packageManager; Sloppy Joe reports that selection as a warning. Active workspaces retain their shared-lockfile checks. Human and JSON reports identify each manifest, manager, lockfile, package count, and outcome. In multi-project scans, an unsupported project produces an incomplete result (exit 2) while other independently bound projects still run. Package counts include each project's coverage, so shared dependencies can appear in multiple project runs.