posidron/python-vet

GitHub: posidron/python-vet

Python 供应链安全工具,模仿 cargo-vet 为 PyPI 依赖建立审计与强制执行机制。

Stars: 0 | Forks: 0

# pyvet Python 供应链安全(PyPI)软件包。 **pyvet** 确保你的 Python 项目中的每个第三方依赖在发布之前都经过受信任实体的审计。它是 Mozilla 的 [cargo-vet](https://mozilla.github.io/cargo-vet/)(用于 Rust crate)的 Python 等效工具。 ![Overview](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/42b3f2e28c073803.png) https://github.com/user-attachments/assets/dd0d309b-ff86-4944-9e9b-fd869fd4a461 ## 为什么 任何人都可以将软件包发布到 PyPI。大多数项目在不审查的情况下引入了数十个传递性依赖项。一个被破坏的包可能会窃取密钥、安装后门或在 CI 运行器上挖掘加密货币。 **pyvet 不会扫描代码或自动检测漏洞。** 它是一个审计 *跟踪* 与 *强制执行* 工具。理念很简单:人类才是审计者。pyvet 记录谁审查了什么、依据什么标准,并确保在未完成审查前不会发布任何内容。 开发者不审计依赖的主要原因是因为工作量太大。pyvet 通过三种机制将这种努力降低到可管理的水平: - **共享** — 公共软件包被多个项目使用。组织可以共享其审计结果,因此同一软件包无需每个团队独立审查。 - **增量审计** — 同一软件包的不同版本通常非常相似。仅审查先前已审计版本与新版本之间的差异,比从头审计要轻松得多。 - **延迟审计** — 初始阶段实现完全覆盖并不总是可行。预先存在的依赖项会自动豁免,回溯清单会随着时间推移逐步减少。 ## 主要特性 - **零努力启动** — `pyvet init` 将所有现有依赖项添加为豁免项。你可以立即受到保护,并随时间推移逐步审计回溯清单。 - **完整与增量审计** — 可以审计整个软件包,或仅审查两个版本之间的差异。 - **受信任的发布者** — 信任某个 PyPI 用户在特定日期范围内发布的所有版本。 - **共享审计** — 导入其他组织的审计集合,避免重复工作。 - **CI 拦截** — `pyvet check` 在失败时以退出代码 1 结束,可作为即插即用的 CI 拦截器。 - **无中心服务器** — 所有内容仅为仓库中 `supply-chain/` 目录下的普通 TOML 文件。 ## 安装 需要 Python ≥ 3.10。 ``` uv add pyvet ``` 或全局安装: ``` uv tool install pyvet ``` ## 快速开始 ### 1. 初始化 ``` pyvet init ``` 这会创建一个 `supply-chain/` 目录,其中包含: - `audits.toml` — 审计记录与标准定义 - `config.toml` — 项目配置、豁免项、导入项与策略 所有当前依赖项会自动添加到豁免列表中。 ### 2. 检查 ``` pyvet check ``` 验证锁文件(`uv.lock` 或 `requirements.txt`)中的每个第三方依赖是否符合审计记录、受信任发布者、导入项与豁免项。成功时退出代码为 0,失败时为 1。 全部通过时的示例输出: ``` ✓ Vetting passed! All 13 dependencies are vetted. Dependency Audit Status ┏━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━┓ ┃ Package ┃ Version ┃ Status ┃ Reason ┃ ┡━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━┩ │ flask │ 3.1.3 │ ✓ vetted │ full-audit │ │ idna │ 3.11 │ ✓ vetted │ trusted │ │ requests │ 2.33.1 │ ✓ vetted │ full-audit │ │ urllib3 │ 2.6.3 │ ✓ vetted │ exemption │ │ ... │ │ │ │ └────────────────────┴───────────┴──────────┴────────────┘ ``` 存在未审计依赖时的示例输出: ``` ✗ Vetting Failed! 1 unvetted dependencies (out of 13 total): ✗ flask==3.1.3 missing safe-to-deploy Use pyvet certify to record audits, or pyvet suggest for recommendations. ``` ### 3. 审计一个软件包 检查源码: ``` pyvet inspect requests 2.33.1 ``` 这会从 PyPI 下载 sdist,验证其 SHA-256 哈希,解压到临时目录,并进入一个 Shell 供你审查代码。使用 `--mode web` 可仅获取 PyPI 链接而非进入 Shell。 记录审计结果: ``` pyvet certify requests 2.33.1 ``` 这会提示你输入审计标准与备注,自动从 `git config` 填充审计者身份,并将条目追加到 `audits.toml`。 ### 4. 增量审计(版本差异) 查看差异: ``` pyvet diff click 8.2.1 8.3.2 ``` 从 PyPI 下载两个版本的 sdist,解压后显示语法高亮的统一差异与变更摘要。 记录增量审计: ``` pyvet certify click 8.3.2 8.2.1 ``` 当提供两个版本时,会记录一个增量审计(`8.2.1 -> 8.3.2`)。 ### 5. 信任发布者 ``` pyvet trust idna --user ofek --start 2024-01-01 --end 2027-01-01 ``` 在指定日期范围内,`idna` 的所有由 PyPI 用户 `ofek` 发布的版本都被视为已审计。 ### 6. 获取建议 ``` pyvet suggest ``` 按优先级列出未审计与豁免的依赖项,并提供可直接复制粘贴的命令: ``` Suggested Audits ┏━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓ ┃ Priority ┃ Package ┃ Version ┃ Action ┃ Criteria ┃ ┡━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩ │ HIGH │ flask │ 3.1.3 │ pyvet inspect flask │ safe-to-deploy │ │ MEDIUM │ click │ 8.3.2 │ pyvet inspect click │ safe-to-deploy │ └──────────┴──────────┴─────────┴──────────────────────┴────────────────┘ ``` ## 命令参考 | 命令 | 描述 | |---|---| | `pyvet init` | 引导 `supply-chain/` 目录,自动豁免所有当前依赖 | | `pyvet check` | 验证所有依赖是否已审计(运行裸 `pyvet` 时为默认行为) | | `pyvet certify [old_ver]` | 记录完整或增量审计 | | `pyvet certify --wildcard ` | 为 PyPI 用户记录通配符审计 | | `pyvet inspect ` | 下载并检查软件包源码 | | `pyvet diff ` | 显示两个版本之间的差异 | | `pyvet suggest` | 推荐最低工作量的审计,并附带代码行数估算 | | `pyvet trust --user --start --end ` | 记录受信任的发布者 | | `pyvet import add --url ` | 添加受信任的外部审计源 | | `pyvet import fetch` | 获取/刷新所有配置的导入源 | | `pyvet import list` | 列出已配置的导入项 | | `pyvet add-exemption ` | 标记软件包为豁免审查 | | `pyvet record-violation ` | 声明某些版本违反特定标准 | | `pyvet regenerate exemptions` | 重新生成豁免项,使 `check` 尽可能通过 | | `pyvet regenerate imports` | 重新获取所有导入项并更新 `imports.lock` | | `pyvet explain-audit [ver]` | 显示软件包的审计路径 | | `pyvet aggregate ` | 将多个来源的审计合并为一个文件 | | `pyvet prune` | 移除过期的审计条目与豁免项 | | `pyvet fmt` | 规范化并排序 TOML 配置文件 | | `pyvet gc` | 清理下载缓存中的旧软件包 | | `pyvet renew [crate]` | 续期通配符审计的过期时间 | ### 全局选项 这些标志可放在子命令之前或之后: ``` --locked Do not fetch new imported audits --frozen Avoid the network entirely (implies --locked) --output-format json Machine-readable JSON output --store-path Custom path to the supply-chain directory ``` ### 常用选项 ``` pyvet certify [old_ver] [--criteria ] [--who ] [--notes ] pyvet certify --wildcard [--start-date ] [--end-date ] pyvet inspect [--mode local|web] pyvet trust --user --start --end [--criteria ] [--notes ] pyvet add-exemption [--criteria ] [--notes ] [--no-suggest] pyvet record-violation [--criteria ] [--who ] [--notes ] pyvet aggregate [--output-file ] pyvet prune [--no-imports] [--no-exemptions] [--no-audits] pyvet gc [--max-age-days ] [--clean] pyvet renew [crate] [--expiring] ``` ## 工作流 ``` ┌─────────────────┐ │ pyvet init │ Bootstrap: create supply-chain/, │ │ exempt all existing deps └────────┬────────┘ │ ▼ ┌─────────────────┐ ┌───│ pyvet check │───┐ │ │ │ │ │ └─────────────────┘ │ PASS │ │ FAIL │ │ ▼ ▼ ┌─────────┐ ┌──────────────────┐ │ Done ✓ │ │ pyvet suggest │ Which deps to audit? └─────────┘ └────────┬─────────┘ │ ┌────────────┼────────────┐ ▼ ▼ ▼ ┌──────────┐ ┌────────────┐ ┌──────────┐ │ inspect │ │ diff │ │ trust │ │ (full) │ │ (delta) │ │(publisher│ └─────┬────┘ └─────┬──────┘ └────┬─────┘ │ │ │ ▼ ▼ │ ┌────────────────────┐ │ │ pyvet certify │ │ └─────────┬──────────┘ │ │ │ └───────────┬──────────┘ │ ▼ ┌─────────────────┐ │ pyvet check │ Re-verify → CI gate └─────────────────┘ ``` ### CI 集成 添加到 CI 流水线(例如 GitHub Actions): ``` - name: Audit dependencies run: pyvet check --locked ``` `pyvet check` 在任何依赖未审计时以退出代码 1 失败,从而阻止构建。在 CI 中使用 `--locked` 可跳过导入获取(依赖缓存的 `imports.lock`)。使用 `--output-format json` 可获得机器可读的输出。 ## 文件结构 ``` your-project/ ├── pyproject.toml ├── uv.lock └── supply-chain/ ├── config.toml # Project config: exemptions, policy, imports ├── audits.toml # Audit records and criteria definitions └── imports.lock # Auto-generated cache of imported audit sets ``` ## 内置标准 ### safe-to-run ### safe-to-deploy `safe-to-deploy` 隐含 `safe-to-run`。可在 `audits.toml` 中定义自定义标准。 ## 配置 ### 豁免项 无需审计即可允许的依赖(通常为 `init` 后的预存在依赖): ``` # supply-chain/config.toml [[exemptions.urllib3]] version = "2.6.3" criteria = "safe-to-deploy" suggest = true notes = "Pre-existing dependency, not yet audited" ``` ### 策略 按软件包覆盖标准要求: ``` [policy."pytest"] dev-criteria = "safe-to-run" ``` ### 导入项 信任其他组织的审计集合: ``` [imports.acme] url = "https://raw.githubusercontent.com/acme-corp/pyvet-audits/main/audits.toml" ``` 可将外部标准映射到本地标准,并排除特定软件包: ``` [imports.acme.criteria-map] their-custom-criteria = "safe-to-deploy" [imports.acme] exclude = ["some-package-we-disagree-on"] ``` ### 通配符审计 信任某个 PyPI 用户发布的所有版本: ``` # supply-chain/audits.toml [[wildcard-audits.click]] who = "Alice " criteria = "safe-to-deploy" user-login = "pallets" start = "2024-01-01" end = "2025-01-01" ``` 通过 CLI 创建:`pyvet certify click 8.3.2 --wildcard pallets` 续期即将过期的通配符审计:`pyvet renew --expiring` ### 违规项 标记永远不应使用的已知不良包,即使已被豁免: ``` # supply-chain/audits.toml [[audits.evil-package]] who = "Alice " criteria = "safe-to-deploy" violation = "*" notes = "Contains cryptocurrency miner" ``` 通过 CLI 创建:`pyvet record-violation evil-package '*' --notes "malware"` ### 依赖项标准覆盖 放宽或收紧某个软件包特定依赖项的标准: ``` [policy."my-app"] dependency-criteria = { "debug-helper" = [] } notes = "debug-helper is never used in production" ``` ### 多仓库聚合 对于拥有多个仓库的组织,可将审计文件合并为一个: ``` # sources.list https://raw.githubusercontent.com/org/repo-a/main/supply-chain/audits.toml https://raw.githubusercontent.com/org/repo-b/main/supply-chain/audits.toml ``` ``` pyvet aggregate sources.list --output-file audits.toml ``` 每个聚合条目都会包含 `aggregated-from` 字段以追踪其来源。 ## 架构 ![Architecture](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/344f401250073804.png) ## 许可证 MIT
标签:cargo-vet 等价, CI/CD 安全, deferred audits, delta审计, PyPI, Python, Python安全, pyvet, 依赖审计, 依赖风险, 包管理, 可信发布者, 增量审计, 安全共享, 安全合规, 审计追踪, 数据投毒防御, 无后门, 版本差异审计, 第三方依赖, 网络代理, 软件供应链, 逆向工具, 零努力启动