
Protegge dagli attacchi alla supply chain, slopsquatting e typosquatting provenienti da dipendenze e codice.
cargo install sloppy-joe
L'attacco alla supply chain di LiteLLM (marzo 2026) ha compromesso un pacchetto con 97 milioni di download mensili. Gli attaccanti hanno rubato le credenziali di pubblicazione e hanno spinto versioni malevole che hanno raccolto chiavi SSH, credenziali cloud e segreti K8s. La soglia di età predefinita di 72 ore di sloppy-joe avrebbe bloccato entrambe le versioni avvelenate: sono state scoperte in poche ore, ben prima che la soglia si aprisse. Se esegui
sloppy-joe checkin CI, questo attacco fallisce. Analisi completa
I generatori di codice AI allucinano i nomi dei pacchetti circa il 20% delle volte. Gli attaccanti registrano quei nomi e aspettano. sloppy-joe li intercetta in CI prima che vengano eseguiti npm install o pip install.
cargo install sloppy-joe
sloppy-joe check
sloppy-joe check --full
sloppy-joe check --ci
sloppy-joe check --dir ./my-project
sloppy-joe check --type npm
sloppy-joe check --python-groups dev,test --python-version 3.12 sloppy-joe check --python-extras docs --python-platform linux --python-version 3.12
sloppy-joe check --config /etc/sloppy-joe/config.json
sloppy-joe check --config https://raw.githubusercontent.com/yourorg/security-configs/main/sloppy-joe.json
sloppy-joe check --json
sloppy-joe check --review-exceptions
sloppy-joe init --register
sloppy-joe init --greenfield --ecosystem npm
sloppy-joe init --from-current
sloppy-joe init --from-current --register
sloppy-joe init > /secure/location/sloppy-joe.json
### Nix```bash
nix profile install github:brennhill/sloppy-joe
Modalità di scansione:
sloppy-joe check esegue il guardrail locale veloce. Impone sempre l'analisi del manifest, il lockfile/sync, la provenienza e la politica sulle sorgenti non supportate. Se lo stato delle dipendenze o della politica è cambiato, o se l'ultima scansione completa con successo è più vecchia di 24 ore, consiglia sloppy-joe check --full.sloppy-joe check --full esegue la scansione online rigorosa e aggiorna lo stato registrato dell'ultima scansione completa con successo.sloppy-joe check --ci esegue la stessa copertura rigorosa di --full, con finalità CI.sloppy-joe check valuta il profilo runtime per impostazione predefinita. Se esistono dipendenze con ambito, avvisa e dice di passare flag espliciti --python-groups, --python-extras, --python-platform e/o --python-version per la parità CI/build.sloppy-joe check ricorda sempre di usare o per CI e gating di produzione.Codici di uscita: 0 = nessun problema bloccante trovato nella modalità selezionata, 1 = problemi bloccanti trovati, 2 = errore runtime.
Supporta: JavaScript (npm, pnpm, Yarn, Bun), Python, Rust, Go, Ruby, PHP, JVM (Gradle/Maven) e .NET — rilevati automaticamente dai file manifest.
Guide per ecosistemi: consulta docs/ecosystems/README.md per il modello di fiducia corrente, le funzionalità supportate e i limiti di fail-closed per ogni ecosistema.
Fonti di configurazione: percorso file locale, URL HTTPS o variabile d'ambiente SLOPPY_JOE_CONFIG. La configurazione non viene mai letta dalla directory del progetto (vedi CONFIG.md per il motivo).
Onboarding: usa la modalità bootstrap che corrisponde al repository:
sloppy-joe init --greenfield --ecosystem <eco> stampa una politica iniziale specifica per l'ecosistema per nuovi progetti. Oggi, i preset greenfield sono implementati per npm, pypi e cargo; altri ecosistemi falliscono con un errore “not supported yet”. Aggiungi --register per scriverla fuori dal repository e registrarla in modo sicuro.sloppy-joe init --from-current ispeziona il repository corrente e stampa suggerimenti bootstrap solo per revisione. Oggi, --from-current è implementato solo per repository il cui codice di prima parte è npm e/o cargo; altri ecosistemi falliscono chiusi con un errore “not implemented yet”. Aggiungi --register per scrivere e registrare la configurazione generata.sloppy-joe init senza modalità stampa un template manuale neutro.Singolo binario. 8 ecosistemi. 16 tipi di attacco. Zero falsi positivi sui controlli generativi. Configurazione che gli agenti AI non possono manomettere.
La maggior parte degli strumenti di sicurezza delle dipendenze controlla una o due cose — esistenza o distanza di modifica. sloppy-joe controlla 16 vettori d'attacco in un unico passaggio: pacchetti allucinati, 10 tipi di typosquatting (omoglifi, scope squatting, caratteri ripetuti, confusione di separatori, riordino di parole, scambi adiacenti, caratteri omessi, forme confuse, varianti di caso, suffissi di versione), enforcement canonico, gating per età della versione, amplificazione dello script di installazione, esplosione di dipendenze, cambiamenti di manutentore e vulnerabilità note tramite OSV.dev.
Viene eseguito come singolo binario Rust senza dipendenze runtime. Supporta tutti gli 8 principali ecosistemi di pacchetti. E la sua configurazione è progettata per la sicurezza: mai letta dalla directory del progetto, caricabile da un URL per CI, con messaggi di errore chiari quando qualcosa non va.
🔶 = beta/sperimentale
L'attacco: L'IA genera import ai_json_helper. Il pacchetto non esiste. Un attaccante registra ai-json-helper su PyPI con malware. La prossima volta che qualcuno esegue pip install, ottiene il pacchetto malevolo.
Come sloppy-joe lo blocca: Il controllo di esistenza interroga l'API PyPI e ottiene un 404. Build bloccata.``` ERROR ai-json-helper [existence] Package 'ai-json-helper' does not exist on the pypi registry. It may be hallucinated by an AI code generator. Fix: Remove 'ai-json-helper' from your dependencies.
### 2. Typosquatting (controlli generativi + fallback basato sulla distanza di edit)
**L'attacco:** Un utente malintenzionato registra `expresz` su npm — un carattere diverso da `express`. L'IA lo genera, o uno sviluppatore lo scrive a tastiera. Il pacchetto esiste, supera il controllo di esistenza e installa malware.
**Come sloppy-joe lo blocca:** sloppy-joe esegue 10 controlli generativi prima di ricorrere alla distanza di edit. Ogni controllo generativo produce una mutazione specifica del nome della dipendenza (scambia caratteri, comprime ripetizioni, rimuovi suffissi, riordina parole, normalizza separatori, sostituisci omoglifi, controlla gli scope) e testa una corrispondenza esatta con pacchetti popolari noti. Questo approccio, ispirato alla libreria [Typomania della Rust Foundation](https://github.com/rustfoundation/typomania), ha falsi positivi quasi nulli perché si attiva solo su corrispondenze esatte dopo la mutazione.
La distanza di edit di Levenshtein viene eseguita per ultima come rete di sicurezza per mutazioni inedite che nessun controllo specifico aveva previsto. Insieme, coprono sia schemi di attacco noti (con precisione) sia quelli sconosciuti (in modo ampio).```
ERROR expresz [similarity/edit-distance]
'expresz' is 1 character away from 'express'. This could be a typosquat.
Fix: If you meant 'express', fix the name in your manifest.
L'attacco: expresss (s in più) o reeact (e in più). Questi sono comuni schemi di allucinazione dell'IA — il modello genera nomi dall'aspetto plausibile con caratteri ripetuti.
Come sloppy-joe lo blocca: Il controllo dei caratteri ripetuti collassa un duplicato alla volta e verifica se il risultato corrisponde a un pacchetto noto. expresss → rimuovi una s → express → corrispondenza.```
ERROR expresss [similarity/repeated-chars]
'expresss' matches 'express' after removing a repeated character.
Fix: Use 'express' — remove the repeated characters.
### 4. Confusione dei separatori
**L'attacco:** `python-dateutil` vs `python_dateutil` vs `pythondateutil`. In alcuni registri, questi sono pacchetti diversi. Un attaccante registra la variante.
**Come sloppy-joe lo blocca:** Normalizza tutti i separatori (`-`, `_`, `.`) prima del confronto. Se la forma normalizzata corrisponde a un pacchetto noto, viene segnalato.```
ERROR socket_io [similarity/separator-confusion]
'socket_io' matches 'socket.io' after normalizing separators.
Fix: Use the canonical name 'socket.io' with the correct separators.
L'attacco: parse-json vs json-parse. La distanza di Levenshtein è 8 — invisibile ai controlli basati sulla distanza di modifica. Ma un utente malintenzionato può registrare il nome riordinato.
Come sloppy-joe lo blocca: Suddivide in base ai separatori, genera tutte le permutazioni dei segmenti e verifica ciascuna rispetto al corpus. parse-json → permuta → json-parse → corrispondenza.```
ERROR parse-json [similarity/word-reorder]
'parse-json' is a reordering of 'json-parse'.
Fix: Use 'json-parse' — the segments are in the wrong order.
### 6. Scambi di caratteri adiacenti
**L'attacco:** `reqeust` invece di `request`. Due caratteri adiacenti trasposti — un errore di battitura comune che gli attaccanti sfruttano.
**Come sloppy-joe lo blocca:** Genera tutte le varianti di scambio adiacente del nome della dipendenza e controlla ciascuna rispetto al corpus.```
ERROR reqeusts [similarity/char-swap]
'reqeusts' matches 'requests' with two adjacent characters swapped.
Fix: Use 'requests' — two characters are transposed.
L'attacco: reqests (mancante u) invece di requests. L'AI omette un carattere e il risultato è un nome dall'aspetto valido.
Come sloppy-joe lo blocca: Inserisce ogni carattere a-z in ogni posizione del nome e controlla se qualche risultato corrisponde a un pacchetto noto. reqests + u alla posizione 3 → requests → corrispondenza.```
ERROR reqests [similarity/omitted-char]
'reqests' matches 'requests' with one character inserted.
Fix: Use 'requests' — a character appears to be missing.
### 8. Omoglifi (somiglianze visive)
**L'attacco:** `rеquests` con una `е` cirillica (U+0435) invece della `e` latina (U+0065). Visivamente identico. Il nome del pacchetto sembra esattamente `requests` ma risolve in un pacchetto diverso e malevolo.
**Come sloppy-joe lo blocca:** Sostituisce 17 caratteri omoglifi noti (cirillici, a larghezza intera, varianti script) con i loro equivalenti latini e controlla se il risultato corrisponde a un pacchetto noto.```
ERROR rеquests [similarity/homoglyph]
'rеquests' contains characters that look identical to 'requests'
but are different Unicode codepoints (homoglyphs).
Fix: Replace the lookalike characters with standard ASCII.
L'attacco: py-utils vs python-utils. Su PyPI, questi sono pacchetti diversi. L'AI genera uno quando intendevi l'altro. Allo stesso modo, github.com vs gitlab.com nei moduli Go.
Come sloppy-joe lo blocca: Applica regole di sostituzione specifiche dell'ecosistema (py↔python per PyPI, github↔gitlab per Go) e verifica se una qualsiasi variante corrisponde a un pacchetto noto.``` ERROR py-flask [similarity/confused-form] 'py-flask' is a confused form of 'flask'. Fix: Use the canonical name 'flask'.
### 10. Attacchi con varianti di maiuscole/minuscole (registry case-sensitive)
**L'attacco:** Su Go, Maven e Ruby, `Rails` e `rails` sono pacchetti diversi. Un attaccante registra la variante con iniziale maiuscola.
**Come sloppy-joe lo blocca:** Nei registry case-sensitive, qualsiasi variante di maiuscole/minuscole di un pacchetto noto viene segnalata come errore. Nei registry case-insensitive (npm, PyPI, Cargo, NuGet, PHP), le varianti di maiuscole/minuscole sono sicure e vengono saltate.```
ERROR Rails [similarity/case-variant]
'Rails' differs from 'rails' only in letter casing.
On case-sensitive registries (ruby) these resolve to different packages.
Fix: Use the exact casing 'rails' in your manifest.
L'attacco: requests2 o lodash-4. L'IA aggiunge un numero di versione al nome del pacchetto invece di specificare la versione correttamente.
Come sloppy-joe lo blocca: Rimuove le cifre e i separatori finali e verifica se il nome base corrisponde a un pacchetto noto.``` ERROR requests2 [similarity/version-suffix] 'requests2' looks like 'requests' with a version suffix appended. Fix: Use 'requests' and specify the version in your manifest's version field.
### 12. Scope squatting (npm, PHP, Go, JVM)
**L'attacco:** Un attaccante registra `@typos/lodash` su npm — un carattere di differenza da `@types/lodash`. Oppure `larvael/framework` su Packagist — due caratteri di differenza da `laravel/framework`. Oppure `github.com/gooogle/protobuf` su Go — una `o` in più. Lo scope sembra legittimo a prima vista. Il pacchetto viene risolto. Il malware si installa.
È raro ma plausibile — e "raro ma plausibile" è esattamente ciò per cui esiste sloppy-joe. L'incidente `ua-parser-js` del 2021 era legato allo scope. Se può accadere a un pacchetto con milioni di download settimanali, può accadere al vostro.
**Come sloppy-joe lo blocca:** Estrae lo scope/namespace dal nome della dipendenza e lo confronta con un elenco di scope noti come affidabili utilizzando la distanza di modifica. Funziona su npm (`@scope`), PHP (`vendor/`), Go (`github.com/org`) e JVM (`com.group`).```
ERROR @typos/lodash [similarity/scope-squatting]
Scope '@typos' is 1 character away from the known scope '@types'.
Scope squatting is a known supply chain attack vector.
Fix: If you meant '@types/lodash', fix the scope in your manifest.
yush è fatto con ♥ da @thehappydinoa e rilasciato sotto la licenza MIT.
© 2021 Aidan Holland```
ERROR github.com/gooogle/protobuf [similarity/scope-squatting] Scope 'github.com/gooogle' is 1 character away from 'github.com/google'. Fix: If you meant 'github.com/google/protobuf', fix the org name.
### 13. Pacchetti non canonici (non è un attacco — un gate di coerenza)
**L'attacco:** Non è un attacco — un problema di coerenza. L'IA sceglie `moment` perché era popolare nei dati di addestramento, ma il tuo team usa `dayjs`. Team diversi che usano pacchetti diversi per lo stesso compito creano debito di manutenzione e gonfiore delle dipendenze.
**Come sloppy-joe lo blocca:** La tua configurazione mappa ogni pacchetto canonico alle sue alternative rifiutate. Se una dipendenza corrisponde a un'alternativa, la build fallisce.```
ERROR moment [canonical]
'moment' is not the approved package for this purpose.
Your team uses 'dayjs'.
Fix: Replace 'moment' with 'dayjs' in your manifest file.
L'attacco: Un attaccante compromette l'account di un manutentore (oppure un manutentore si ribella) e pubblica una versione patch dannosa. Sembra un aggiornamento normale. Se la tua CI lo installa immediatamente, sei compromesso prima che qualcuno se ne accorga.
Come sloppy-joe lo blocca: Il gate dell'età della versione blocca qualsiasi dipendenza la cui versione è stata pubblicata meno di min_version_age_hours ore fa (default: 72 ore). Questo dà il tempo alla community, a Socket.dev e ad altri scanner di segnalare le versioni dannose.```
ERROR react [metadata/version-age]
Version '^19.0.0' of 'react' was published 6 hours ago (minimum: 72 hours).
New versions need time for the community and security scanners to review them.
Fix: Wait until the version is at least 72 hours old, or pin to an older version.
### 15. Pacchetti nuovissimi
**L'attacco:** Un pacchetto creato ieri con 3 download che ha un nome simile a un pacchetto popolare. Alta probabilità che si tratti di un typosquat o di un segnaposto per un futuro attacco.
**Come sloppy-joe lo blocca:** Segnala qualsiasi pacchetto creato meno di 30 giorni fa.```
ERROR sketchy-lib [metadata/new-package]
'sketchy-lib' was first published 2 days ago.
New packages are higher risk.
Fix: Verify 'sketchy-lib' at its registry page and source repository.
L'attacco: Un pacchetto con 12 download che si trova a un solo carattere di distanza da requests. Quasi certamente un typosquatting.
Come sloppy-joe lo blocca: Segnala i pacchetti con meno di 100 download (laddove il registro fornisce dati sui download — attualmente npm, crates.io, RubyGems).``` ERROR requsets [metadata/low-downloads] 'requsets' has only 12 downloads. Fix: Verify 'requsets' is the package you intend to use.
---
## Ecosistemi Supportati
| Ecosistema | Manifesto | Politica del Lockfile | Esistenza | Metadati | Controllo Età |
|-----------|----------|-----------------|:---------:|:--------:|:--------:|
| npm | package.json | `package-lock.json` o `npm-shrinkwrap.json` richiesto | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| PyPI | `pyproject.toml`, `requirements*.txt`, `Pipfile`, `setup.cfg`, `setup.py` | Poetry è considerato affidabile con `poetry.lock`, uv è considerato affidabile con `uv.lock`, pip-tools con hash completamente bloccato è considerato affidabile solo quando il grafo delle dipendenze committato vincola `--index-url` e valori esatti di `--extra-index-url` nella allowlist, e gli indici personalizzati di Poetry/uv visibili nel repository possono essere considerati affidabili solo tramite la allowlist esatta di `trusted_indexes.pypi`; i manifesti legacy avvisano a ogni esecuzione a meno che `python_enforcement` non sia `poetry_only` | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Cargo | Cargo.toml | `Cargo.lock` richiesto | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Go | go.mod | `go.sum` richiesto per dipendenze esterne; non richiesto per solo stdlib o per tutti i `replace` locali | :white_check_mark: | :x: | :x: |
| Ruby | Gemfile | `Gemfile.lock` richiesto | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| PHP | composer.json | `composer.lock` richiesto | :white_check_mark: | :x: | :x: |
| JVM (Gradle) | build.gradle / build.gradle.kts | `gradle.lockfile` richiesto | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| JVM (Maven) | pom.xml | solo avviso: nessuna applicazione rigorosa del lockfile | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| .NET | *.csproj | `packages.lock.json` richiesto | :white_check_mark: | :x: | :x: |
Tutti gli ecosistemi ricevono controlli di esistenza, similarità e canonicità. I metadati e il controllo età dipendono da ciò che espone l'API del registro. Il supporto al lockfile abilita la scansione delle dipendenze transitive e la risoluzione esatta delle versioni dove l'ecosistema fornisce un modello di lockfile locale al progetto attendibile.
## Avvio Rapido```bash
# Install
cargo install sloppy-joe
# Check current project (auto-detects ecosystem)
sloppy-joe check
# Check with canonical enforcement and age gate
sloppy-joe check --config /etc/sloppy-joe/config.json
# Output as JSON for CI
sloppy-joe check --json
| Codice | Significato |
|---|---|
0 | Tutti i controlli superati |
1 | Problemi trovati |
2 | Errore di runtime |
{ "canonical": { "npm": { "lodash": ["underscore", "ramda", "lazy.js"], "dayjs": ["moment", "luxon"], "axios": ["request", "got", "node-fetch", "superagent"] }, "pypi": { "httpx": ["urllib3", "requests"], "ruff": ["flake8", "pylint"] } }, "internal": { "go": ["github.com/yourorg/"], "npm": ["@yourorg/"] }, "allowed": { "npm": ["some-vetted-external-pkg"] }, "similarity_exceptions": { "cargo": [ { "package": "serde_json", "candidate": "serde", "generator": "segment-overlap" } ] }, "metadata_exceptions": { "cargo": [ { "package": "colored", "check": "metadata/maintainer-change", "version": "2.2.0", "previous_publisher": "kurtlawrence", "current_publisher": "hwittenborn" } ] }, "min_version_age_hours": 72, "allow_legacy_npm_v1_lockfile": false, "python_enforcement": "prefer_poetry" }
**`canonical`** — le chiavi sono pacchetti approvati; i valori sono alternative rifiutate.
**`internal`** — i pacchetti della tua organizzazione. Salta TUTTI i controlli. Questi cambiano costantemente.
**`allowed`** — pacchetti esterni verificati. Salta esistenza + similarità, ma ancora soggetti al gate dell'età della versione.
**`similarity_exceptions`** — soppressioni esatte di pacchetto/candidato/generatore per falsi positivi di similarità revisionati. Usa questo quando uno specifico bordo di similarità è sbagliato ma desideri comunque i controlli normali sul pacchetto.
**`metadata_exceptions`** — soppressioni esatte di metadati revisionati. Attualmente questo supporta solo `metadata/maintainer-change`, e richiede una corrispondenza esatta di pacchetto/versione/editore-precedente/editore-attuale.
Usa `sloppy-joe check --review-exceptions` quando hai bisogno di revisionare i blocchi per cambiamento del manutentore. La scansione blocca ancora normalmente, ma l'output umano aggiunge una sezione `REVIEW EXCEPTIONS` con proprietari, URL del repository e un frammento `metadata_exceptions` pronto da incollare. `--json` include gli stessi dati in un campo `review_candidates` di primo livello.
**`min_version_age_hours`** — blocca qualsiasi versione pubblicata meno di questo numero di ore fa. Default: 72 (3 giorni). Imposta a 0 per disabilitare. I pacchetti interni sono esenti.
**`allow_legacy_npm_v1_lockfile`** — consenti i lockfile npm `lockfileVersion: 1` da npm v5/v6 in modalità fiducia ridotta. Default: `false`. Tienilo disattivato a meno che tu non sia intenzionalmente bloccato su npm legacy e accetti avvisi rumorosi più una copertura di fiducia ridotta delle dipendenze transitive npm.
**`python_enforcement`** — controlla la politica di fiducia per Python. `prefer_poetry` (default) si fida dei progetti Poetry e uv, si fida dei requisiti pip-tools completamente bloccati da hash solo quando il grafico dei requisiti committato lega esattamente `--index-url` e qualsiasi valore `--extra-index-url` non PyPI, e altrimenti declassa pip-tools a fiducia ridotta. I manifest legacy come `requirements*.txt` senza hash, `Pipfile`, `setup.cfg`, `setup.py` e `pyproject.toml` non Poetry/non uv avvertono a ogni esecuzione. `poetry_only` blocca quei flussi di lavoro Python non Poetry e richiede Poetry.
### Sicurezza della Configurazione
La configurazione **non viene mai letta dalla directory del progetto**. Un agente AI con accesso shell potrebbe riscrivere una configurazione nel repository per inserire nella whitelist ciò che desidera.
Risoluzione della configurazione:
1. `--config /path/to/config.json` — file locale (flag CLI, priorità massima)
2. `--config https://example.com/config.json` — recupera da URL
3. `SLOPPY_JOE_CONFIG=...` — variabile d'ambiente (percorso file o URL)
4. Nessuna configurazione = solo controlli di esistenza + similarità + metadati
Configurazioni malformate **falliscono duramente** con messaggi di errore utilizzabili — una configurazione rotta non ricade mai silenziosamente su nessuna protezione.
Vedi [CONFIG.md](https://github.com/brennhill/sloppy-joe/blob/HEAD/CONFIG.md) per il riferimento completo al formato, modelli di integrazione CI ed esempi.
Configurazione di bootstrap:```bash
sloppy-joe init --greenfield --ecosystem npm
sloppy-joe init --from-current
sloppy-joe init --from-current --register
sloppy-joe init --register
Il modo più veloce per aggiungere sloppy-joe alla tua pipeline CI — scarica un binario precompilato da GitHub Releases (nessuna toolchain Rust richiesta):```yaml
name: Dependency Check on: [push, pull_request]
jobs: sloppy-joe: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: brennhill/[email protected] with: config: https://raw.githubusercontent.com/yourorg/configs/main/sloppy-joe.json
#### Input di azione
| Input | Descrizione | Default |
|-------|-------------|---------|
| `config` | Percorso del file di configurazione o URL HTTPS | *(nessuno)* |
| `dir` | Directory del progetto da analizzare | `.` |
| `type` | Ecosistema (`npm`, `pypi`, `cargo`, `go`, `ruby`, `php`, `jvm`, `dotnet`) | rilevamento automatico |
| `deep` | Abilita i controlli di somiglianza delle dipendenze transitive | `false` |
| `paranoid` | Abilita mutazioni bitflip | `false` |
| `args` | Argomenti CLI aggiuntivi | *(nessuno)* |
| `version` | versione di sloppy-joe da installare | `latest` |
#### Esempi```yaml
# Minimal — CI-oriented scan, auto-detect ecosystem, no config
- uses: brennhill/[email protected]
# With org config from a URL
- uses: brennhill/[email protected]
with:
config: https://raw.githubusercontent.com/yourorg/configs/main/sloppy-joe.json
# Deep scan with paranoid mode
- uses: brennhill/[email protected]
with:
config: ${{ secrets.SLOPPY_JOE_CONFIG }}
deep: true
paranoid: true
# Scan a subdirectory, pin to a specific version
- uses: brennhill/[email protected]
with:
dir: ./packages/api
version: '1.1.0'
dependency-guard: script: - cargo install sloppy-joe - sloppy-joe check --ci --config $SLOPPY_JOE_CONFIG
### pre-commit
sloppy-joe funziona con il framework [pre-commit](https://pre-commit.com).
Aggiungilo al tuo `.pre-commit-config.yaml`:```yaml
# .pre-commit-config.yaml
repos:
- repo: https://github.com/brennhill/sloppy-joe
rev: v1.1.0
hooks:
- id: sloppy-joe
L'hook esegue sloppy-joe check su ogni commit (e opzionalmente su push).
Rileva automaticamente il tuo ecosistema dai file manifest. Passa argomenti aggiuntivi tramite args:```yaml
- id: sloppy-joe
args: [--config, "https://example.com/config.json"]
Oppure usa un semplice hook di shell senza il framework:```bash
#!/bin/sh
sloppy-joe check || exit 1
sloppy-joe utilizza un approccio generativo basato su registro per il rilevamento delle somiglianze. Invece di confrontare ogni dipendenza con un corpus statico utilizzando la distanza di modifica (che produce falsi positivi), genera mutazioni specifiche del nome di ogni dipendenza, interroga il registro per verificare se la mutazione esiste e segnala le corrispondenze esatte.``` Pipeline (in order):
La similarità esegue 4 fasi:
- **Fase 0: Scope squatting** — controllo locale, senza rete. Confronta lo scope/namespace con scopes noti come sicuri tramite la distanza di Levenshtein.
- **Fase 1: Intra-manifest** — controllo locale. Segnala quando due dipendenze nello stesso manifest sono mutazioni l'una dell'altra.
- **Fase 2: Query al registry** — genera mutazioni, interroga in batch il registry per verificarne l'esistenza, memorizza nella cache i risultati (TTL di 7 giorni).
- **Fase 3: Arricchimento dei metadati** — recupera i conteggi dei download e le date di pubblicazione per le corrispondenze, per aggiungere prove ai report.
Ogni generatore di mutazioni etichetta il proprio output, quindi il tipo di controllo riportato (es. `similarity/homoglyph`) è deterministico — il generatore con la severità più alta vince quando più generatori producono lo stesso candidato.
## Affidabilità della CI
sloppy-joe è progettato per pipeline CI in cui fallimenti intermittenti sono inaccettabili.
**Riprova con backoff.** Tutte le chiamate HTTP al registry riprovano 3 volte con backoff esponenziale (200ms, 400ms, 800ms) in caso di errori transitori (5xx, timeout, errori di connessione). Un singolo problema di rete non farà fallire la build.
**Fail-closed sugli errori di query.** Se le query al registry o a OSV falliscono, sloppy-joe emette un errore bloccante `registry-unreachable` invece di saltare silenziosamente i controlli. La scansione non si basa più su soglie per ecosistema o limiti di dimensione del campione prima di bloccarsi.
**Cache di similarità.** I risultati sull'esistenza delle mutazioni vengono memorizzati nella cache per 7 giorni. Dopo la prima scansione, la maggior parte delle query viene servita dalla cache senza chiamate di rete. Solo le nuove dipendenze attivano query al registry.
**Risoluzione basata su lockfile.** Quando un lockfile supportato è presente e attendibile (`package-lock.json`, `npm-shrinkwrap.json`, `Cargo.lock`, `Gemfile.lock`, `poetry.lock` per progetti Poetry, `uv.lock` per progetti uv, `composer.lock`, `gradle.lockfile`, `packages.lock.json`), sloppy-joe risolve le versioni esatte da esso invece di indovinare dagli intervalli. I file `requirements*.txt` completamente bloccati tramite hash possono anche fornire versioni esatte bloccate, e diventano completamente attendibili quando il grafico delle dipendenze impegnato vincola il proprio `--index-url` e i valori esatti di `--extra-index-url` consentiti.
## Test
La suite di test copre i controlli di similarità, i segnali di metadati, il comportamento di OSV, l'analisi e la validazione della configurazione, la risoluzione dei lockfile, la politica di preflight per manifest e lockfile, la formattazione dei report e la logica di ripetizione HTTP.```bash
cargo test
Dove gli altri sono più forti: Socket.dev esegue un'analisi approfondita degli script di installazione con rilevamento comportamentale che va ben oltre l'approccio basato su flag di sloppy-joe. cargo-deny ha un controllo di conformità delle licenze di prim'ordine, ma ciò è intenzionalmente fuori dallo scopo di sloppy-joe perché la politica delle licenze è un problema di conformità, non un controllo di sicurezza delle dipendenze. npm audit e pip-audit sono opzioni a installazione zero per la scansione di vulnerabilità in un singolo ecosistema.
Dove sloppy-joe è diverso: È l'unico strumento che verifica che i pacchetti esistano effettivamente nei registry (catturando le allucinazioni dell'IA), esegue 11 generatori di typosquatting con falsi positivi quasi nulli, applica scelte di pacchetti canonici e mantiene la sua configurazione al di fuori del repository in modo che gli agenti IA non possano indebolire i propri controlli.
Apache 2.0
--ci--full| Ecosistema | Manifest richiesto | Lockfile / stato progetto attendibile |
|---|
| JavaScript / npm | package.json | package-lock.json o npm-shrinkwrap.json; npm v1 legacy bloccato per impostazione predefinita |
| 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 o setup.py | il percorso Poetry attendibile usa poetry.lock, il percorso uv attendibile usa uv.lock, e pip-tools con hash completo è attendibile solo quando il grafo dei requisiti committato lega esattamente --index-url e qualsiasi valore --extra-index-url; gli indici Python visibili nel repository possono essere inseriti nella whitelist tramite trusted_indexes.pypi; le modalità Python attendibili valutano un profilo di installazione selezionato alla volta (runtime per impostazione predefinita, gruppi/extras/platform/arch/version espliciti tramite CLI); i manifest legacy sono consentiti con avvisi per impostazione predefinita |
| Rust | Cargo.toml | Cargo.lock |
| Go | go.mod | go.sum richiesto per dipendenze esterne |
| Ruby | Gemfile | Gemfile.lock |
| PHP / Composer | composer.json | composer.lock |
| JVM / Gradle | build.gradle o build.gradle.kts | gradle.lockfile |
| JVM / Maven | pom.xml | solo avviso: nessun percorso di lockfile locale al progetto attendibile ancora |
| .NET / NuGet | .csproj | packages.lock.json |
| sloppy-joe | Socket.dev | GuardDog | Phantom Guard | antislopsquat |
|---|
| Controllo esistenza | ✅ | ✅ | ❌ | ✅ | ✅ |
| Similarità / typosquat | ✅ | ✅ | ✅ | ✅ | ❌ |
| Rilevamento omoglifi | ✅ | ❌ | ❌ | ❌ | ❌ |
| Scope squatting | ✅ | ❌ | ❌ | ❌ | ❌ |
| Enforcement canonico | ✅ | ❌ | ❌ | ❌ | ❌ |
| Gating età versione | ✅ | ❌ | ❌ | ❌ | ❌ |
| Amplificazione script install | ✅ | ✅ | ❌ | ❌ | ❌ |
| Esplosione dipendenze | ✅ | ❌ | ❌ | ❌ | ❌ |
| Cambiamento manutentore | ✅ | ✅ | ❌ | ❌ | ❌ |
| Controllo vulnerabilità OSV | ✅ | ✅ | ❌ | ❌ | ❌ |
| Sicurezza config (fuori repo) | ✅ | N/D | ❌ | ❌ | ❌ |
| Liste interne + consentite | ✅ | ❌ | ❌ | ❌ | ❌ |
| npm | ✅ | ✅ | ✅ | ✅ | ❌ |
| PyPI | ✅ | ✅ | ✅ | ✅ | ✅ |
| Cargo | ✅ | ✅ | ❌ | ✅ | ❌ |
| Go | ✅ | ✅ | ✅ | ❌ | ❌ |
| Ruby | ✅ | ✅ | ✅ | ❌ | ❌ |
| PHP | ✅ | 🔶 | ❌ | ❌ | ❌ |
| JVM (Gradle/Maven) | ✅ | ✅ | ❌ | ❌ | ❌ |
| .NET (NuGet) | ✅ | ✅ | ❌ | ❌ | ❌ |
| Singolo binario | ✅ | ❌ | ❌ | ❌ | ❌ |
| Open source | Apache 2.0 | Commerciale | Apache 2.0 | MIT | OSS |
| Linguaggio | Rust | SaaS | Python | Python | Python |
| Caratteristica | sloppy-joe | Socket.dev | cargo-deny | pip-audit | npm audit |
|---|
| Rilevamento di pacchetti allucinati | ✅ | ❌ | ❌ | ❌ | ❌ |
| Rilevamento di typosquatting | ✅ 11 generatori | Parziale | ❌ | ❌ | ❌ |
| Applicazione del nome canonico | ✅ | ❌ | ❌ | ❌ | ❌ |
| Scansione di vulnerabilità note | ✅ tramite OSV | ✅ | ✅ | ✅ | ✅ |
| Analisi degli script di installazione | Base (flag + no repo) | ✅ Analisi approfondita | ❌ | ❌ | ❌ |
| Conformità delle licenze | OOS: conformità, non sicurezza | ✅ | ✅ Eccellente | OOS: conformità, non sicurezza | OOS: conformità, non sicurezza |
| Multi-ecosistema | 8 ecosistemi | npm, PyPI, Go, Ruby, Java, .NET | Solo Rust | Solo Python | Solo npm |
| Sicurezza per agenti AI (configurazione fuori dal repository) | ✅ | ❌ | ❌ | ❌ | ❌ |
| Compatibile offline/CI | ✅ Funziona ovunque | Richiede la piattaforma Socket | ✅ | ✅ | ✅ |
| Gratuito / open source | Apache 2.0 | Livello gratuito + a pagamento | Apache 2.0 | Apache 2.0 | Integrato |