cargo install sloppy-joe
2026年3月的LiteLLM供应链攻击攻陷了一个月下载量达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
扫描模式:
sloppy-joe check 运行快速的本地防护栏。它始终强制进行清单解析、锁定文件/同步、来源证明及不支持来源策略。如果依赖项或策略状态发生变化,或者上一次成功的完整扫描距今超过 24 小时,它会建议执行 sloppy-joe check --full。sloppy-joe check --full 运行严格的在线扫描并刷新记录的成功完整扫描状态。sloppy-joe check --ci 运行与 --full 相同的严格覆盖范围,以 CI 为导向。sloppy-joe check 默认评估 runtime 配置文件。如果存在作用域依赖,它会发出警告并提示你传递显式的 --python-groups、--python-extras、--python-platform 和/或 --python-version 标志以实现 CI/构建对等。sloppy-joe check 输出总会提醒你在 CI 和生产门控中使用 --ci 或 --full。退出代码: 0 = 在所选模式下未发现阻塞问题,1 = 发现阻塞问题,2 = 运行时错误。
支持: JavaScript(npm、pnpm、Yarn、Bun)、Python、Rust、Go、Ruby、PHP、JVM(Gradle/Maven)和 .NET — 从清单文件自动检测。
生态系统指南: 请参阅 docs/ecosystems/README.md 了解每个生态系统当前的信任模型、支持的功能以及失败关闭限制。
配置来源: 本地文件路径、HTTPS URL 或 SLOPPY_JOE_CONFIG 环境变量。配置不会从项目目录中读取(原因见 CONFIG.md)。
入门引导: 使用与仓库匹配的引导模式:
sloppy-joe init --greenfield --ecosystem <eco> 为新项目打印特定于生态系统的启动策略。目前,npm、pypi 和 cargo 实现了 greenfield 预设;其他生态系统会失败并显示“暂不支持”错误。添加 --register 将其写入仓库之外并安全注册。sloppy-joe init --from-current 检查当前仓库并打印仅限审查的引导建议。目前,--from-current 仅对第一方代码为 npm 和/或 cargo 的仓库实现;其他生态系统会失败关闭并显示“未实现”错误。添加 --register 写入并注册生成的配置。sloppy-joe init 打印一个中立的模板。单个二进制文件。8 个生态系统。16 种攻击类型。生成式检查零误报。AI 代理无法篡改的配置。
大多数依赖安全工具只检查一两项——存在性或编辑距离。sloppy-joe 在单次扫描中检查 16 个攻击向量:幻觉包、10 种类型的域名抢注(同形字、作用域抢注、重复字符、分隔符混淆、单词重排序、相邻交换、省略字符、混淆形式、大小写变体、版本后缀)、规范执行、版本年龄门槛、安装脚本放大、依赖爆炸、维护者更改以及通过 OSV.dev 的已知漏洞。
它作为一个单独的 Rust 二进制文件运行,没有运行时依赖。它支持所有 8 个主要的包生态系统。其配置专为安全性而设计:绝不从项目目录读取,可从 URL 加载用于 CI,并在出现问题时提供清晰的错误消息。
🔶 = 测试版/实验性
攻击方式: AI 生成 import ai_json_helper。该包不存在。攻击者在 PyPI 上注册了带有恶意软件的 ai-json-helper。下次有人运行 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. 拼写错误劫持(生成式检查 + 编辑距离回退)
**攻击方式:** 攻击者在 npm 上注册 `expresz` – 与 `express` 仅一字之差。可能是 AI 生成的,也可能是开发者手误。该包存在,通过存在性检查,然后安装恶意软件。
**sloppy-joe 如何阻止:** sloppy-joe 先运行 10 项生成式检查,再回退到编辑距离。每项生成式检查都会对依赖名称产生特定的变异(交换字符、合并重复、去除后缀、单词重排、标准化分隔符、替换同形字、检查作用域),并针对已知流行包进行精确匹配测试。这种方法受 [Rust 基金会 Typomania](https://github.com/rustfoundation/typomania) 库的启发,几乎无误报,因为它仅在变异后精确匹配时才触发。
Levenshtein 编辑距离作为最后的安全网,用于处理未预料到的新型变异。两者结合,既精确覆盖已知攻击模式,又广泛覆盖未知模式。```
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 幻觉模式——模型会生成看起来合理但包含重复字符的名称。
slopy-joe 如何阻止: 重复字符检查会每次消除一个重复字符,并检查结果是否匹配已知的包。expresss → 移除一个 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` 与 `python_dateutil` 与 `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 与 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`。两个相邻字符顺序颠倒——这是一种常见的拼写错误,攻击者会利用它。
**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 遗漏了一个字符,但结果看似像一个有效的名称。
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. 同形字(视觉相似字)
**攻击方式:** `rеquests` 使用西里尔字母 `е`(U+0435)代替拉丁字母 `e`(U+0065)。视觉上完全相同。该包名看起来与 `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. Scope squatting(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.
请提供需要翻译的 Markdown 内容。``` 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. 全新包
**攻击方式:** 一个昨天创建的、仅有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 仅当已提交的依赖图绑定 `--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`;仅标准库或全部本地 `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 需求文件仅当提交的需求图精确绑定了 `--index-url` 和任何非 PyPI 的 `--extra-index-url` 值,否则将 pip-tools 降级为低置信度。每次运行都会对未哈希的 `requirements*.txt`、`Pipfile`、`setup.cfg`、`setup.py` 以及非 Poetry/non-uv 的 `pyproject.toml` 等传统清单发出警告。`poetry_only` 则阻止这些非 Poetry 的 Python 工作流,并要求使用 Poetry。
### 配置安全
**配置永远不会从项目目录读取**。拥有 shell 访问权限的 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/main/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 | *(无)* |
| `dir` | 要扫描的项目目录 | `.` |
| `type` | 生态系统 (`npm`, `pypi`, `cargo`, `go`, `ruby`, `php`, `jvm`, `dotnet`) | auto-detect |
| `deep` | 启用传递依赖相似性检查 | `false` |
| `paranoid` | 启用位翻转变异 | `false` |
| `args` | 额外的CLI参数 | *(无)* |
| `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"]
或者无需框架,直接使用简单的 shell 钩子:```bash
#!/bin/sh
sloppy-joe check || exit 1
sloppy-joe 采用基于注册表的生成式方法进行相似性检测。它并不使用编辑距离将每个依赖项与静态语料库进行比较(这会产生误报),而是生成每个依赖项名称的特定变体,查询注册表以检查该变体是否存在,并标记出精确匹配项。``` Pipeline (in order):
相似性检查运行4个阶段:
- **阶段0:作用域抢注** — 本地检查,无需网络。通过莱文斯坦距离比较作用域/命名空间与已知安全的作用域。
- **阶段1:清单内部** — 本地检查。当同一清单中的两个依赖项互为变异时标记。
- **阶段2:注册表查询** — 生成变异,批量查询注册表是否存在,缓存结果(7天TTL)。
- **阶段3:元数据丰富** — 获取匹配项的下载次数和发布日期,以向报告添加证据。
每个变异生成器都会标记其输出,因此报告检查类型(例如 `similarity/homoglyph`)是确定性的——当多个生成器产生相同候选时,严重性最高的生成器获胜。
## CI 可靠性
sloppy-joe 专为不可接受间歇性失败的 CI 流水线设计。
**带退避的重试。** 所有注册表 HTTP 调用在遇到瞬时故障(5xx、超时、连接错误)时会以指数退避(200毫秒、400毫秒、800毫秒)重试3次。单次网络波动不会导致构建失败。
**查询错误时故障关闭。** 如果注册表或 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` 也可以提供精确的固定版本,当提交的需求图绑定了自己的 `--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
| 生态系统 | 必需的清单 | 受信任的锁定文件/项目状态 |
|---|
| 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 值精确绑定时才受信任;可通过 trusted_indexes.pypi 将仓库可见的 Python 索引加入白名单;受信任的 Python 模式一次评估一个选定的安装配置文件(默认为 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 漏洞检查 | ✅ | ✅ | ❌ | ❌ | ❌ |
| 配置安全性(仓库外) | ✅ | 不适用 | ❌ | ❌ | ❌ |
| 内部 + 允许列表 | ✅ | ❌ | ❌ | ❌ | ❌ |
| npm | ✅ | ✅ | ✅ | ✅ | ❌ |
| PyPI | ✅ | ✅ | ✅ | ✅ | ✅ |
| Cargo | ✅ | ✅ | ❌ | ✅ | ❌ |
| Go | ✅ | ✅ | ✅ | ❌ | ❌ |
| Ruby | ✅ | ✅ | ✅ | ❌ | ❌ |
| PHP | ✅ | 🔶 | ❌ | ❌ | ❌ |
| JVM (Gradle/Maven) | ✅ | ✅ | ❌ | ❌ | ❌ |
| .NET (NuGet) | ✅ | ✅ | ❌ | ❌ | ❌ |
| 单个二进制文件 | ✅ | ❌ | ❌ | ❌ | ❌ |
| 开源 | Apache 2.0 | 商业 | Apache 2.0 | MIT | 开源 |
| 语言 | Rust | SaaS | Python | Python | Python |
| 特性 | sloppy-joe | Socket.dev | cargo-deny | pip-audit | npm audit |
|---|
| 幻觉包检测 | ✅ | ❌ | ❌ | ❌ | ❌ |
| 域名抢注检测 | ✅ 11个生成器 | 部分 | ❌ | ❌ | ❌ |
| 规范命名强制执行 | ✅ | ❌ | ❌ | ❌ | ❌ |
| 已知漏洞扫描 | ✅ 通过OSV | ✅ | ✅ | ✅ | ✅ |
| 安装脚本分析 | 基本(标志+无仓库) | ✅ 深度分析 | ❌ | ❌ | ❌ |
| 许可证合规性 | 范围外:合规性,非安全 | ✅ | ✅ 优秀 | 范围外:合规性,非安全 | 范围外:合规性,非安全 |
| 多生态系统 | 8个生态系统 | npm, PyPI, Go, Ruby, Java, .NET | 仅Rust | 仅Python | 仅npm |
| AI代理安全(仓库外配置) | ✅ | ❌ | ❌ | ❌ | ❌ |
| 离线/CI友好 | ✅ 随处运行 | 需要Socket平台 | ✅ | ✅ | ✅ |
| 免费/开源 | Apache 2.0 | 免费套餐+付费 | Apache 2.0 | Apache 2.0 | 内置 |