
サプライチェーン攻撃、スロップスクワッティング、タイポスクワッティングから依存関係やコードを防御します。
cargo install sloppy-joe
** LiteLLMサプライチェーン攻撃(2026年3月)は、月間9700万ダウンロードのパッケージを侵害しました。攻撃者は公開用認証情報を盗み、SSHキー、クラウド認証情報、K8sシークレットを収集する悪意のあるバージョンを押し込みました。sloppy-joeのデフォルトの72時間バージョンエイジゲートは、両方の毒入りバージョンをブロックしていたでしょう — それらは数時間以内に発見され、ゲートが開く前でした。CIで
sloppy-joe checkを実行していれば、この攻撃は失敗します。** 完全分析
AIコード生成器はパッケージ名を約20%の確率で幻覚(ハルシネーション)します。攻撃者はその名前を登録して待機します。sloppy-joeはnpm installやpip installが実行される前にCIでそれらをキャッチします。
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
Scan modes:
sloppy-joe check は高速なローカルガードレールを実行します。常にマニフェスト解析、ロックファイル/同期、来歴、および未サポートソースポリシーを強制します。依存関係またはポリシーの状態が変更された場合、または最後に成功したフルスキャンが24時間以上前の場合、sloppy-joe check --full を推奨します。sloppy-joe check --full は厳格なオンラインスキャンを実行し、記録された成功したフルスキャン状態を更新します。sloppy-joe check --ci は --full と同じ厳格なカバレッジを、CI指向の意図で実行します。sloppy-joe check はデフォルトで runtime プロファイルを評価します。スコープ付き依存関係が存在する場合、警告を表示し、CI/ビルドのパリティのために明示的な --python-groups、--python-extras、--python-platform、および/または --python-version フラグを渡すように指示します。sloppy-joe check 出力は、CIおよびプロダクションゲーティングのために --ci または を使用するように常に促します。Exit codes: 0 = 選択したモードでブロッキング問題が見つかりませんでした、1 = ブロッキング問題が見つかりました、2 = ランタイムエラー。
Supports: JavaScript(npm、pnpm、Yarn、Bun)、Python、Rust、Go、Ruby、PHP、JVM(Gradle/Maven)、.NET — マニフェストファイルから自動検出されます。
Ecosystem guides: 各エコシステムの現在の信頼モデル、サポートされている機能、およびフェイルクローズド制限については、docs/ecosystems/README.md を参照してください。
Config sources: ローカルファイルパス、HTTPS URL、または SLOPPY_JOE_CONFIG 環境変数。設定はプロジェクトディレクトリから読み取られることはありません(理由については CONFIG.md を参照)。
Onboarding: リポジトリに一致するブートストラップモードを使用します:
sloppy-joe init --greenfield --ecosystem <eco> は、新しいプロジェクト向けのエコシステム固有のスターターポリシーを出力します。現在、グリーンフィールドプリセットは npm、pypi、cargo に対して実装されています。他のエコシステムは「未サポート」エラーで失敗します。--register を追加すると、リポジトリ外に書き込み、安全に登録します。sloppy-joe init --from-current は現在のリポジトリを検査し、レビューのみのブートストラップ提案を出力します。現在、--from-current はファーストパーティコードが npm および/または cargo であるリポジトリに対してのみ実装されています。他のエコシステムは「未実装」エラーでフェイルクローズします。--register を追加して、生成された設定を書き込み、登録します。sloppy-joe init は中立的な手動テンプレートを出力します。単一バイナリ。8つのエコシステム。16種類の攻撃。生成チェックでの誤検出ゼロ。AIエージェントが改ざんできない設定。
ほとんどの依存関係セキュリティツールは、1つまたは2つのこと(存在確認、または編集距離)をチェックします。sloppy-joeは1回のパスで16の攻撃ベクトルをチェックします:幻影パッケージ、10種類のタイポスクワッティング(ホモグリフ、スコープスクワッティング、繰り返し文字、セパレータ混乱、単語並べ替え、隣接スワップ、文字欠落、紛らわしい形式、大文字小文字のバリエーション、バージョンサフィックス)、正規化強制、バージョンエイジゲート、インストールスクリプト増幅、依存関係爆発、メンテナ変更、およびOSV.devによる既知の脆弱性。
ランタイム依存関係のない単一のRustバイナリとして実行されます。8つの主要なパッケージエコシステムすべてをサポートしています。そしてその設定はセキュリティのために設計されています:プロジェクトディレクトリから読み取られることはなく、CI用にURLから読み込み可能で、何か問題があった場合は明確なエラーメッセージが表示されます。
🔶 = ベータ/実験的
攻撃: AIが import ai_json_helper を生成します。パッケージは存在しません。攻撃者がマルウェアを含む ai-json-helper をPyPIに登録します。次回誰かが pip install を実行すると、悪意のあるパッケージを入手します。
sloppy-joeによる阻止方法: 存在チェックがPyPI APIにアクセスし、404を受け取ります。ビルドがブロックされます。``` 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. タイポスクワッティング(生成的チェック+編集距離フォールバック)
**攻撃の内容:** 攻撃者は `express` から一文字変えて `expresz` をnpmに登録します。AIが生成するか、開発者がタイポしてしまいます。そのパッケージは存在するため、存在チェックを通過し、マルウェアをインストールします。
**sloppy-joeによる防御:** sloppy-joeは編集距離にフォールバックする前に、10種類の生成的チェックを実行します。各生成的チェックは依存関係名の特定の変異(文字の入れ替え、繰り返しの圧縮、サフィックスの除去、単語の並び替え、セパレータの正規化、ホモグリフの置換、スコープのチェック)を生成し、既知の人気パッケージとの完全一致をテストします。このアプローチは、[Rust FoundationのTypomania](https://github.com/rustfoundation/typomania)ライブラリに着想を得ており、変異後に完全一致した場合のみ発動するため、誤検知がほぼゼロです。
レーベンシュタイン編集距離は、先行するチェックで予期されなかった新たな変異に対するセーフティネットとして最後に実行されます。これらを組み合わせることで、既知の攻撃パターン(正確に)と未知の攻撃パターン(広く)の両方をカバーします。```
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.
攻撃: expresss (余分なs) や reeact (余分なe)。これらは一般的なAIの幻覚パターンであり、モデルが繰り返し文字を含む plausible な名前を生成します。
sloppy-joe がブロックする方法: 繰り返し文字チェックは、一度に1つの重複を削除し、その結果が既知のパッケージと一致するか確認します。expresss → 1つのsを削除 → express → 一致。```
ERROR expresss [similarity/repeated-chars]
'expresss' matches 'express' after removing a repeated character.
Fix: Use 'express' — remove the repeated characters.
### 4. セパレータ混同
**攻撃:** `python-dateutil` vs `python_dateutil` vs `pythondateutil`。一部のレジストリでは、これらは異なるパッケージです。攻撃者はバリアントを登録します。
**sloppy-joeのブロック方法:** 比較の前にすべてのセパレータ(`-`、`_`、`.`)を正規化します。正規化された形式が既知のパッケージと一致する場合、フラグが立てられます。```
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.
攻撃: parse-json vs json-parse。レーベンシュタイン距離は8 — 編集距離によるチェックでは見えない。しかし、攻撃者は並べ替えられた名前を登録できる。
sloppy-joeの防御方法: セパレータで分割し、セグメントのすべての順列を生成し、それぞれをコーパスと照合する。parse-json → 順列化 → json-parse → 一致。```
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. 隣接文字の入れ替え
**攻撃:** `reqeust` を `request` の代わりに使用。隣接する2文字が入れ替わる——攻撃者が悪用する一般的なタイプミス。
**sloppy-joe の防御方法:** 依存関係名のすべての隣接入れ替えバリアントを生成し、それぞれをコーパスと照合します。```
ERROR reqeusts [similarity/char-swap]
'reqeusts' matches 'requests' with two adjacent characters swapped.
Fix: Use 'requests' — two characters are transposed.
攻撃: reqests(uが欠落)をrequestsの代わりに。AIが文字を1つ落とし、結果は有効に見える名前になります。
sloppy-joeがそれをブロックする方法: 名前にすべての位置にa-zの文字を挿入し、いずれかの結果が既知のパッケージと一致するかチェックします。 reqests + 位置3の u → requests → 一致。```
ERROR reqests [similarity/omitted-char]
'reqests' matches 'requests' with one character inserted.
Fix: Use 'requests' — a character appears to be missing.
### 8. ホモグリフ(視覚的に類似した文字)
**攻撃の仕組み:** ラテン文字の `e` (U+0065) の代わりにキリル文字の `е` (U+0435) を使用した `rеquests`。見た目はまったく同じです。パッケージ名は `requests` とまったく同じに見えますが、別の悪意のあるパッケージに解決されます。
**sloppy-joe による阻止方法:** 17種類の既知のホモグリフ文字(キリル文字、全角文字、筆記体の変種など)を対応するラテン文字に置き換え、その結果が既知のパッケージと一致するかどうかをチェックします。```
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.
攻撃: py-utils と python-utils。PyPIではこれらは異なるパッケージです。AIが意図したものとは別のものを生成します。同様に、Goモジュールにおける github.com と gitlab.com。
sloppy-joeがそれを阻止する方法: エコシステム固有の置換ルール(PyPIではpy↔python、Goではgithub↔gitlab)を適用し、いずれかのバリアントが既知のパッケージと一致するかどうかを確認します。``` ERROR py-flask [similarity/confused-form] 'py-flask' is a confused form of 'flask'. Fix: Use the canonical name 'flask'.
### 10. ケースバリアント攻撃(大文字小文字を区別するレジストリ)
**攻撃方法:** Go、Maven、Rubyでは、`Rails`と`rails`は異なるパッケージです。攻撃者は大文字で始まるバリアントを登録します。
**sloppy-joeの防御方法:** 大文字小文字を区別するレジストリでは、既知のパッケージの大文字小文字のバリアントはすべてエラーとしてフラグが立てられます。大文字小文字を区別しないレジストリ(npm、PyPI、Cargo、NuGet、PHP)では、大文字小文字のバリアントは安全であり、スキップされます。```
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.
攻撃: requests2 や lodash-4 のように、AIがバージョンを適切に指定せずにパッケージ名にバージョン番号を付加します。
sloppy-joeがどのようにブロックするか: 末尾の数字と区切り文字を取り除き、ベース名が既知のパッケージと一致するか確認します。``` 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. スコープスワッティング (npm, PHP, Go, JVM)
**攻撃手法:** 攻撃者は npm に `@typos/lodash` を登録します — `@types/lodash` と一文字違いです。あるいは Packagist に `larvael/framework` を — `laravel/framework` と二文字違いです。または Go に `github.com/gooogle/protobuf` を — `o` が一つ余分です。スコープは一見して正当に見えます。パッケージは解決され、マルウェアがインストールされます。
これは稀ですがあり得ます — そして「稀でありあり得る」こそが sloppy-joe が存在する理由です。2021 年の `ua-parser-js` インシデントはスコープ関連でした。毎週何百万ものダウンロードがあるパッケージに起こり得るなら、あなたのパッケージにも起こり得ます。
**sloppy-joe による防御方法:** 依存関係名からスコープ/名前空間を抽出し、編集距離を使用して既知の正当なスコープのリストと比較します。npm (`@scope`)、PHP (`vendor/`)、Go (`github.com/org`)、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.
Please provide the Markdown content to translate.``` 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. 非正規パッケージ(攻撃ではなく — 一貫性ゲート)
**攻撃内容:** 攻撃ではありません — 一貫性の問題です。AI が訓練データで人気だったため `moment` を選びますが、あなたのチームは `dayjs` を使用しています。同じタスクに異なるチームが異なるパッケージを使用すると、メンテナンス負債と依存関係の肥大化が発生します。
**sloppy-joe がそれをブロックする方法:** あなたの設定では、各正規パッケージを拒否する代替パッケージにマッピングします。依存関係が代替パッケージと一致すると、ビルドが失敗します。```
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.
攻撃: 攻撃者がパッケージメンテナのアカウントを乗っ取り(またはメンテナが悪意を持ち)、悪意のあるパッチバージョンを公開します。これは通常のアップデートのように見えます。CIがすぐにインストールすると、誰も気づかないうちに侵害されます。
sloppy-joeが阻止する方法: バージョン経過時間によるゲートにより、min_version_age_hours(デフォルト: 72時間)よりも前に公開されたバージョンの依存関係のみがブロックされます。これにより、コミュニティ、Socket.dev、およびその他のスキャナーが悪意のあるバージョンを検出する時間が確保されます。```
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. Brand-new packages
**攻撃:** 昨日作成され、ダウンロード数が3のパッケージで、人気パッケージと似た名前を持っています。タイポスクワッティングまたは将来の攻撃のためのプレースホルダーである可能性が高いです。
**sloppy-joeがブロックする方法:** 30日以内に作成されたパッケージをフラグ付けします。```
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.
攻撃の内容: ダウンロード数が12のパッケージで、requests と一文字違いのもの。ほぼ間違いなくタイポスクワットです。
sloppy-joeの防御方法: ダウンロード数が100未満のパッケージをフラグします(レジストリがダウンロードデータを提供する場合 — 現在はnpm、crates.io、RubyGems)。``` ERROR requsets [metadata/low-downloads] 'requsets' has only 12 downloads. Fix: Verify 'requsets' is the package you intend to use.
---
## 対応エコシステム
| エコシステム | マニフェスト | ロックファイルポリシー | 存在確認 | メタデータ | 経過時間ゲート |
|-----------|----------|-----------------|:---------:|:--------:|:--------:|
| npm | package.json | `package-lock.json` または `npm-shrinkwrap.json` が必要 | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| PyPI | `pyproject.toml`, `requirements*.txt`, `Pipfile`, `setup.cfg`, `setup.py` | Poetry は `poetry.lock` で信頼され、uv は `uv.lock` で信頼されます。完全にハッシュロックされた pip-tools は、コミットされた requirements グラフが `--index-url` をバインドし、許可リストに正確に一致する `--extra-index-url` 値を指定している場合にのみ信頼されます。また、リポジトリで可視の Poetry/uv カスタムインデックスは、正確な `trusted_indexes.pypi` 許可リストによってのみ信頼できます。レガシーマニフェストは、`python_enforcement` が `poetry_only` でない限り、実行のたびに警告を出力します。 | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Cargo | Cargo.toml | `Cargo.lock` が必要 | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Go | go.mod | 外部依存関係には `go.sum` が必要;stdlibのみまたはすべてローカルの `replace` には不要 | :white_check_mark: | :x: | :x: |
| Ruby | Gemfile | `Gemfile.lock` が必要 | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| PHP | composer.json | `composer.lock` が必要 | :white_check_mark: | :x: | :x: |
| JVM (Gradle) | build.gradle / build.gradle.kts | `gradle.lockfile` が必要 | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| JVM (Maven) | pom.xml | 警告のみ:厳格なロックファイル強制はなし | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| .NET | *.csproj | `packages.lock.json` が必要 | :white_check_mark: | :x: | :x: |
すべてのエコシステムは、存在確認 + 類似性 + 正規性チェックを受けます。メタデータと経過時間ゲートは、レジストリAPIが公開する内容に依存します。ロックファイルサポートにより、エコシステムが信頼できるプロジェクトローカルのロックファイルモデルを提供する場合、推移的依存関係スキャンと正確なバージョン解決が可能になります。
## クイックスタート```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
| コード | 意味 |
|---|---|
0 | すべてのチェックに合格 |
1 | 問題が見つかりました |
2 | 実行時エラー |
{ "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`** — キーは承認されたパッケージ、値は拒否された代替パッケージです。
**`internal`** — あなたの組織のパッケージ。すべてのチェックをスキップします。これらは常に変更されます。
**`allowed`** — 審査済みの外部パッケージ。存在確認+類似性をスキップしますが、バージョン経過時間ゲートの対象です。
**`similarity_exceptions`** — レビュー済みの類似性偽陽性に対する正確なパッケージ/候補/ジェネレーター抑制。特定の類似性エッジが間違っているが、パッケージの通常チェックは引き続き必要な場合に使用します。
**`metadata_exceptions`** — レビュー済みのメタデータ抑制。現在は `metadata/maintainer-change` のみサポートしており、パッケージ/バージョン/前の公開者/現在の公開者の完全一致が必要です。
メンテナー変更のブロッカーを確認する必要がある場合は、`sloppy-joe check --review-exceptions` を使用します。スキャンは通常通りブロックされますが、人間向けの出力に `REVIEW EXCEPTIONS` セクションが追加され、所有者、リポジトリURL、コピー&ペースト可能な `metadata_exceptions` スニペットが表示されます。`--json` は同じデータをトップレベルの `review_candidates` フィールドに含めます。
**`min_version_age_hours`** — この時間数より前に公開されたバージョンをブロックします。デフォルト: 72(3日)。0に設定すると無効化されます。内部パッケージは対象外です。
**`allow_legacy_npm_v1_lockfile`** — npm v5/v6 の `lockfileVersion: 1` による npm ロックファイルを信頼度低減モードで許可します。デフォルト: `false`。意図的にレガシー npm に留まり、大きな警告と npm トランジティブカバレッジの低下を受け入れる場合以外は、この設定をオフにしておいてください。
**`python_enforcement`** — Python の信頼ポリシーを制御します。`prefer_poetry`(デフォルト)は Poetry プロジェクトと uv プロジェクトを信頼し、完全にハッシュロックされた pip-tools requirements は、requirements グラフが `--index-url` と任意の非 PyPI `--extra-index-url` の値を正確にバインドしている場合のみ信頼し、それ以外は pip-tools を信頼度低減に格下げします。ハッシュ化されていない `requirements*.txt`、`Pipfile`、`setup.cfg`、`setup.py`、Poetry/uv 以外の `pyproject.toml` などのレガシーマニフェストは、実行ごとに警告を表示します。`poetry_only` は、これら non-Poetry の Python ワークフローをブロックし、Poetry を要求します。
### 設定のセキュリティ
設定は**プロジェクトディレクトリから読み取られることはありません**。シェルアクセス権を持つ AI エージェントがリポジトリ内の設定を書き換えて、任意のものを許可リストに追加する可能性があります。
設定の解決順序:
1. `--config /path/to/config.json` — ローカルファイル(CLIフラグ、最優先)
2. `--config https://example.com/config.json` — URLから取得
3. `SLOPPY_JOE_CONFIG=...` — 環境変数(ファイルパスまたはURL)
4. 設定なし = 存在確認+類似性+メタデータチェックのみ
不正な設定は**厳格に失敗**し、実用的なエラーメッセージが表示されます。壊れた設定が無警告で保護なしにフォールバックすることはありません。
完全な形式リファレンス、CI統合パターン、例については [CONFIG.md](https://github.com/brennhill/sloppy-joe/blob/HEAD/CONFIG.md) を参照してください。
ブートストラップ設定:```bash
sloppy-joe init --greenfield --ecosystem npm
sloppy-joe init --from-current
sloppy-joe init --from-current --register
sloppy-joe init --register
sloppy-joeをCIパイプラインに追加する最速の方法 — GitHub Releasesからプリビルドされたバイナリをダウンロードします(Rustツールチェーンは不要):```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
#### アクション入力
| 入力 | 説明 | デフォルト |
|-------|-------------|---------|
| `config` | 設定ファイルのパスまたはHTTPS URL | *(none)* |
| `dir` | スキャン対象のプロジェクトディレクトリ | `.` |
| `type` | エコシステム(`npm`、`pypi`、`cargo`、`go`、`ruby`、`php`、`jvm`、`dotnet`) | auto-detect |
| `deep` | 推移的依存関係の類似性チェックを有効にする | `false` |
| `paranoid` | ビットフリップミューテーションを有効にする | `false` |
| `args` | 追加のCLI引数 | *(none)* |
| `version` | インストールするsloppy-joeのバージョン | `latest` |
#### 例```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は[pre-commit](https://pre-commit.com)フレームワークと連携します。
`.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
このフックは、すべてのコミット(およびオプションでプッシュ)に対して sloppy-joe check を実行します。マニフェストファイルからエコシステムを自動検出します。追加の引数は args で渡します。```yaml
- id: sloppy-joe
args: [--config, "https://example.com/config.json"]
または、フレームワークを使わずにシンプルなシェルフックを使用してください:```bash
#!/bin/sh
sloppy-joe check || exit 1
sloppy-joeはレジストリベースの生成アプローチを使用して類似性を検出します。すべての依存関係を編集距離で静的なコーパスと比較する代わりに(偽陽性を生成する)、各依存関係名の特定の変形を生成し、レジストリに問い合わせて変形が存在するか確認し、完全一致にフラグを立てます。``` Pipeline (in order):
Similarityは4つのフェーズで実行されます:
- **Phase 0: Scope squatting** — ローカルチェック、ネットワークなし。スコープ/名前空間を既知の正当なスコープとレーベンシュタイン距離を使って比較します。
- **Phase 1: Intra-manifest** — ローカルチェック。同じマニフェスト内の2つの依存関係が互いにミューテーションである場合にフラグを立てます。
- **Phase 2: Registry query** — ミューテーションを生成し、レジストリに一括クエリして存在を確認し、結果をキャッシュします(7日間のTTL)。
- **Phase 3: Metadata enrichment** — 一致するもののダウンロード数と公開日を取得して、レポートに証拠を追加します。
各ミューテーションジェネレーターは出力にタグを付けるため、報告されるチェックタイプ(例: `similarity/homoglyph`)は決定論的です。複数のジェネレーターが同じ候補を生成した場合、最も重大度の高いジェネレーターが優先されます。
## CIの信頼性
sloppy-joeは、不安定な失敗が許されないCIパイプライン向けに設計されています。
**リトライとバックオフ。** すべてのレジストリHTTP呼び出しは、一時的な障害(5xx、タイムアウト、接続エラー)に対して指数バックオフ(200ms、400ms、800ms)で3回リトライします。1回のネットワーク不具合でビルドが失敗することはありません。
**クエリエラーでのフェイルクローズド。** レジストリまたはOSVクエリが失敗した場合、sloppy-joeはチェックを黙ってスキップする代わりに、ブロッキングエラー`registry-unreachable`を発行します。スキャンはブロックする前にエコシステムごとのしきい値やサンプルサイズのカットオフに依存しなくなります。
**類似性キャッシュ。** ミューテーションの存在結果は7日間キャッシュされます。最初のスキャン後、ほとんどのクエリはネットワーク呼び出しなしでキャッシュから提供されます。新しい依存関係のみがレジストリクエリをトリガーします。
**ロックファイル対応の解決。** サポートされているロックファイルが存在し信頼できる場合(`package-lock.json`、`npm-shrinkwrap.json`、`Cargo.lock`、`Gemfile.lock`、Poetryプロジェクトの`poetry.lock`、uvプロジェクトの`uv.lock`、`composer.lock`、`gradle.lockfile`、`packages.lock.json`)、sloppy-joeは範囲から推測する代わりにそれから正確なバージョンを解決します。完全にハッシュロックされた`requirements*.txt`も正確な固定バージョンを提供でき、コミットされたrequirementsグラフが自身の`--index-url`と正確に許可された`--extra-index-url`の値をバインドする場合、それらは完全に信頼されるようになります。
## テスト
テストスイートは、類似性チェック、メタデータシグナル、OSV動作、設定の解析と検証、ロックファイル解決、マニフェストとロックファイルの事前チェックポリシー、レポートフォーマット、HTTPリトライロジックをカバーしています。```bash
cargo test
他ツールの強み: Socket.dev は、sloppy-joe のフラグベースのアプローチをはるかに超える、動作検出による深いインストールスクリプト分析を提供します。cargo-deny は、ライセンスコンプライアンスチェックで最高クラスですが、sloppy-joe では意図的に範囲外としています。ライセンスポリシーは依存関係のセキュリティ制御ではなく、コンプライアンス問題だからです。npm audit と pip-audit は、単一エコシステムの脆弱性スキャンにおけるゼロインストールの選択肢です。
sloppy-joe の違い: パッケージが実際にレジストリに存在するかを確認し(AI の幻覚を捕捉)、11 個のタイポスクワッティングジェネレータをほぼゼロの偽陽性で実行し、正規のパッケージ選択を強制し、設定をリポジトリ外に保持するため AI エージェントが自身のチェックを弱体化できません。これらを実現する唯一のツールです。
Apache 2.0
--full| エコシステム | 必須マニフェスト | 信頼されるロックファイル/プロジェクト状態 |
|---|
| JavaScript / npm | package.json | package-lock.json または npm-shrinkwrap.json;デフォルトでレガシー npm v1 はブロック |
| 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、または setup.py | 信頼されるPoetryパスは poetry.lock を使用し、信頼されるuvパスは uv.lock を使用し、完全にハッシュロックされたpip-toolsは、コミットされたrequirementsグラフが --index-url および任意の --extra-index-url 値を正確にバインドしている場合にのみ信頼されます。リポジトリから参照可能なPythonインデックスは trusted_indexes.pypi を介して許可リストに追加できます。信頼されるPythonモードは、一度に1つの選択されたインストールプロファイルを評価します(デフォルトでは runtime、CLI経由で明示的なgroups/extras/platform/arch/version)。レガシーマニフェストはデフォルトで警告付きで許可されます。 |
| Rust | Cargo.toml | Cargo.lock |
| Go | go.mod | 外部依存関係には go.sum が必要 |
| Ruby | Gemfile | Gemfile.lock |
| PHP / Composer | composer.json | composer.lock |
| JVM / Gradle | build.gradle または build.gradle.kts | gradle.lockfile |
| JVM / Maven | pom.xml | 警告のみ:信頼されるプロジェクトローカルロックファイルパスはまだありません |
| .NET / NuGet | .csproj | packages.lock.json |
| sloppy-joe | Socket.dev | GuardDog | Phantom Guard | antislopsquat |
|---|
| 存在チェック | ✅ | ✅ | ❌ | ✅ | ✅ |
| 類似性/タイポスクワッティング | ✅ | ✅ | ✅ | ✅ | ❌ |
| ホモグリフ検出 | ✅ | ❌ | ❌ | ❌ | ❌ |
| スコープスクワッティング | ✅ | ❌ | ❌ | ❌ | ❌ |
| 正規化強制 | ✅ | ❌ | ❌ | ❌ | ❌ |
| バージョンエイジゲート | ✅ | ❌ | ❌ | ❌ | ❌ |
| インストールスクリプト増幅 | ✅ | ✅ | ❌ | ❌ | ❌ |
| 依存関係爆発 | ✅ | ❌ | ❌ | ❌ | ❌ |
| メンテナ変更 | ✅ | ✅ | ❌ | ❌ | ❌ |
| OSV脆弱性チェック | ✅ | ✅ | ❌ | ❌ | ❌ |
| 設定セキュリティ(リポジトリ外) | ✅ | N/A | ❌ | ❌ | ❌ |
| 内部+許可リスト | ✅ | ❌ | ❌ | ❌ | ❌ |
| npm | ✅ | ✅ | ✅ | ✅ | ❌ |
| PyPI | ✅ | ✅ | ✅ | ✅ | ✅ |
| Cargo | ✅ | ✅ | ❌ | ✅ | ❌ |
| Go | ✅ | ✅ | ✅ | ❌ | ❌ |
| Ruby | ✅ | ✅ | ✅ | ❌ | ❌ |
| PHP | ✅ | 🔶 | ❌ | ❌ | ❌ |
| JVM (Gradle/Maven) | ✅ | ✅ | ❌ | ❌ | ❌ |
| .NET (NuGet) | ✅ | ✅ | ❌ | ❌ | ❌ |
| 単一バイナリ | ✅ | ❌ | ❌ | ❌ | ❌ |
| オープンソース | Apache 2.0 | Commercial | Apache 2.0 | MIT | OSS |
| 言語 | Rust | SaaS | Python | Python | Python |
| 機能 | sloppy-joe | Socket.dev | cargo-deny | pip-audit | npm audit |
|---|
| 幻覚パッケージ検出 | ✅ | ❌ | ❌ | ❌ | ❌ |
| タイポスクワッティング検出 | ✅ 11 個のジェネレータ | 部分的 | ❌ | ❌ | ❌ |
| 正規名の強制 | ✅ | ❌ | ❌ | ❌ | ❌ |
| 既知の脆弱性スキャン | ✅ OSV 経由 | ✅ | ✅ | ✅ | ✅ |
| インストールスクリプト分析 | 基本(フラグ + リポジトリなし) | ✅ 深い分析 | ❌ | ❌ | ❌ |
| ライセンスコンプライアンス | OOS: コンプライアンス、セキュリティではない | ✅ | ✅ 優れている | OOS: コンプライアンス、セキュリティではない | OOS: コンプライアンス、セキュリティではない |
| マルチエコシステム | 8 エコシステム | npm, PyPI, Go, Ruby, Java, .NET | Rust のみ | Python のみ | npm のみ |
| AI エージェント安全(リポジトリ外設定) | ✅ | ❌ | ❌ | ❌ | ❌ |
| オフライン/CI 対応 | ✅ どこでも実行可能 | Socket プラットフォームが必要 | ✅ | ✅ | ✅ |
| 無料 / オープンソース | Apache 2.0 | 無料プラン + 有料 | Apache 2.0 | Apache 2.0 | 組み込み |