cargo install sloppy-joe
被入侵的发布版本可能使用合法的包名。LiteLLM 维护者报告指出了 2026 年 3 月的恶意版本 1.82.7 和 1.82.8。sloppy-joe 默认的 72 小时发布时长门槛,可以在安装之前使用
--ci或--full扫描已解析的依赖图时,拒绝新发布的版本。这是一种时间策略,而非对恶意载荷的检查。
研究文献记录了生成代码中的包幻觉。sloppy-joe 在安装前检查依赖名称和元数据;仅凭存在性无法确定已注册的包是安全的。
包名长度限制为 npm 的 214 字节和其他生态系统的 1,024 字节,以限定拼写错误分析的工作量。更长的名称会报告 Scan not supported(退出码 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
main 上的 flake 固定了已发布的二进制版本及其校验和。
扫描模式:
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 了解各生态系统当前的信任模型、支持的功能和失败关闭限制。
| 生态系统 | 必需的清单 | 受信任的锁文件 / 项目状态 |
|---|---|---|
| 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 模式一次评估一个选定的安装配置文件(默认 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 |
扫描范围: 递归扫描默认忽略 .planning。在外部策略中配置 "ignore_dirs": [".planning", "experiments"] 以排除其他目录名,或使用 "ignore_dirs": [] 禁用可配置的排除项。参见目录排除。
包含多个项目的仓库: Sloppy Joe 会分别运行每个已发现项目的依赖链,并合并结果。后端、UI、管理应用和工具可以使用不同的包管理器和锁文件。不完整的祖先 npm/pnpm 工作区声明不会认领独立锁定的子项目,除非祖先显式声明了 packageManager;Sloppy Joe 会将这种选择报告为警告。活动工作区保留其共享锁文件检查。人类可读报告和 JSON 报告会标识每个清单、包管理器、锁文件、包数量和结果。在多项目扫描中,不受支持的项目会产生不完整结果(退出码 2),而其他独立绑定的项目仍会运行。包数量包含每个项目的覆盖范围,因此共享依赖可能出现在多个项目运行中。
私有 npm 注册表(npm 和 pnpm): 支持项目本地的 .npmrc 默认注册表和带作用域的 HTTPS 注册表路由。路由到自定义注册表的包会在人类可读输出和 JSON 输出中产生非阻塞的 resolution/private-registry-unvetted 警告。公共包(包括私有包的公共依赖)保留其正常检查。私有包的身份信息不会发送到公共注册表或 OSV;其存在性、拼写相似度、发布时间、发布者历史和漏洞均未经审查。锁文件一致性、完整性元数据和声明的注册表来源边界仍然适用。扫描器绝不会展开或发送注册表凭据。参见 JavaScript 指南。
配置来源: 本地文件路径、HTTPS URL 或 SLOPPY_JOE_CONFIG 环境变量。配置绝不会从项目目录读取(原因见 CONFIG.md)。
入门引导: 使用与仓库匹配的引导模式:
sloppy-joe init --greenfield --ecosystem <eco> 会为新项目打印特定生态系统的起始策略。目前,greenfield 预设已为 npm、pypi 和 cargo 实现;其他生态系统会以“尚不支持”错误失败。添加 --register 可将其写入仓库外部并安全注册。sloppy-joe init --from-current 会检查当前仓库并打印仅供审查的引导建议。目前,--from-current 仅对第一方代码为 npm 和/或 cargo 的仓库实现;其他生态系统会以“尚未实现”错误安全失败。添加 --register 可写入并注册生成的配置。sloppy-joe init 会打印一个中性的手动模板。sloppy-joe 将包存在性、名称相似度启发式、规范包策略、发布时间门控、元数据风险信号和 OSV 公告查询整合到一个 Rust 二进制文件中。
完整扫描和 CI 扫描包括对受支持的已锁定传递依赖的元数据和漏洞检查;--deep 还会检查传递依赖的名称相似度。受支持的清单、锁文件、来源和策略边界验证也会在快速模式下运行。不受支持的输入会停止验证,而不是产生一次干净的扫描。
名称相似度和元数据信号可能会标记合法包。请审查发现结果,并在受信任的配置中使用范围狭窄的例外。请将该配置保留在被扫描仓库之外,这样仓库编辑就无法直接削弱它。
攻击方式: 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.
攻击方式: 攻击者在 npm 上注册 expresz——与 express 仅差一个字符。可能是 AI 生成的,也可能是开发者手误打错的。该包确实存在,通过了存在性检查,并安装了恶意软件。
sloppy-joe 如何标记它: 生成名称变异,并检查候选名称是否存在于注册表或同一清单中。作用域/命名空间检查使用针对已知作用域的编辑距离。分段重叠规则还使用选定的热门包根名。这些是审查信号,而非恶意软件的证明;不存在通用的包名编辑距离回退机制。
ERROR expresz [similarity/keyboard-proximity]
'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 幻觉模式——模型会生成看似合理但带有重复字符的名称。
sloppy-joe 如何拦截: 重复字符检查会一次折叠一个重复字符,并检查结果是否匹配已知包。expresss → 移除一个 s → express → 匹配。