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 等效工具。

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` 字段以追踪其来源。
## 架构

## 许可证
MIT
标签:cargo-vet 等价, CI/CD 安全, deferred audits, delta审计, PyPI, Python, Python安全, pyvet, 依赖审计, 依赖风险, 包管理, 可信发布者, 增量审计, 安全共享, 安全合规, 审计追踪, 数据投毒防御, 无后门, 版本差异审计, 第三方依赖, 网络代理, 软件供应链, 逆向工具, 零努力启动