依赖管理与供应链安全 🟢
你将学到:
- 使用
cargo-audit扫描已知漏洞- 使用
cargo-deny执行许可证、咨询和来源策略- 使用 Mozilla 的
cargo-vet验证供应链信任- 跟踪过时的依赖并检测破坏性 API 变更
- 可视化和去重你的依赖树
交叉引用:发布 Profile —
cargo-udeps修剪在此处发现的未使用依赖 · CI/CD 流水线 — 流水线中的审计和 deny 任务 · 构建脚本 —build-dependencies也是你的供应链的一部分
Rust 二进制文件不仅仅包含你的代码——它还包含 Cargo.lock 中的每个传递依赖。依赖树中任何位置的漏洞、许可证违规或恶意 crate 都会成为你的问题。本章介绍使依赖管理可审计和自动化的工具。
cargo-audit — 已知漏洞扫描
cargo-audit 根据 RustSec 咨询数据库 检查你的 Cargo.lock,该数据库跟踪已发布 crate 中的已知漏洞。
# 安装
cargo install cargo-audit
# 扫描已知漏洞
cargo audit
# 输出:
# Crate: chrono
# Version: 0.4.19
# Title: localtime_r 调用中潜在的 segfault
# Date: 2020-11-10
# ID: RUSTSEC-2020-0159
# URL: https://rustsec.org/advisories/RUSTSEC-2020-0159
# Solution: 升级到 >= 0.4.20
# 检查并在存在漏洞时使 CI 失败
cargo audit --deny warnings
# 生成 JSON 输出用于自动处理
cargo audit --json
# 通过更新 Cargo.lock 修复漏洞
cargo audit fix
CI 集成:
# .github/workflows/audit.yml
name: Security Audit
on:
schedule:
- cron: '0 0 * * *' # 每日检查 — 咨询持续出现
push:
paths: ['Cargo.lock']
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: rustsec/audit-check@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
cargo-deny — 综合策略执行
cargo-deny 远超漏洞扫描。它在四个维度执行策略:
- Advisories — 已知漏洞(类似 cargo-audit)
- Licenses — 允许/拒绝的许可证列表
- Bans — 禁止的 crate 或重复版本
- Sources — 允许的注册表和 git 来源
# 安装
cargo install cargo-deny
# 初始化配置
cargo deny init
# 创建带有文档化默认值的 deny.toml
# 运行所有检查
cargo deny check
# 运行特定检查
cargo deny check advisories
cargo deny check licenses
cargo deny check bans
cargo deny check sources
示例 deny.toml:
# deny.toml
[advisories]
vulnerability = "deny" # 在已知漏洞时失败
unmaintained = "warn" # 在未维护的 crate 时警告
yanked = "deny" # 在被撤回的 crate 时失败
notice = "warn" # 在信息性咨询时警告
[licenses]
unlicensed = "deny" # 所有 crate 必须有许可证
allow = [
"MIT",
"Apache-2.0",
"BSD-2-Clause",
"BSD-3-Clause",
"ISC",
"Unicode-DFS-2016",
]
copyleft = "deny" # 此项目中无 GPL/LGPL/AGPL
default = "deny" # 拒绝任何未明确允许的内容
[bans]
multiple-versions = "warn" # 如果同一 crate 出现在 2 个版本则警告
wildcards = "deny" # 依赖中无 path = "*"
# 禁止特定有问题的 crate
deny = [
# openssl-sys 引入 C OpenSSL — предпочитать rustls
{ name = "openssl-sys", wrappers = ["native-tls"] },
]
# 允许特定重复版本(当不可避免时)
[[bans.skip]]
name = "syn"
version = "1.0" # syn 1.x 和 2.x 经常共存
[sources]
unknown-registry = "deny" # 只允许 crates.io
unknown-git = "deny" # 无随机 git 依赖
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
许可证执行对于商业项目特别有价值:
# 检查你的依赖树中有哪些许可证
cargo deny list
# 输出:
# MIT — 127 crates
# Apache-2.0 — 89 crates
# BSD-3-Clause — 12 crates
# MPL-2.0 — 3 crates ← 可能需要法律审查
# Unicode-DFS — 1 crate
cargo-vet — 供应链信任验证
cargo-vet(来自 Mozilla)解决一个不同的问题:不是“这个 crate 有已知 bug 吗?“而是“有可信的人实际审查过这段代码吗?”
# 安装
cargo install cargo-vet
# 初始化(创建 supply-chain/ 目录)
cargo vet init
# 检查哪些 crate 需要审查
cargo vet
# 审查 crate 后,认证它:
cargo vet certify serde 1.0.203
# 记录你已为你的标准审计了 serde 1.0.203
# 从可信组织导入审计
cargo vet import mozilla
cargo vet import google
cargo vet import bytecode-alliance
工作原理:
supply-chain/
├── audits.toml ← 你团队的审计认证
├── config.toml ← 信任配置和标准
└── imports.lock ← 从其他组织导入的固定
cargo-vet 对于具有严格供应链要求的组织(政府、金融、基础设施)最有价值。对于大多数团队,cargo-deny 提供足够的保护。
cargo-outdated 和 cargo-semver-checks
cargo-outdated — 查找有更新的依赖:
cargo install cargo-outdated
cargo outdated --workspace
# 输出:
# Name Project Compat Latest Kind
# serde 1.0.193 1.0.203 1.0.203 Normal
# regex 1.9.6 1.10.4 1.10.4 Normal
# thiserror 1.0.50 1.0.61 2.0.3 Normal ← 主要版本可用
cargo-semver-checks — 在发布前检测破坏性 API 变更。对于库 crate 至关重要:
cargo install cargo-semver-checks
# 检查你的更改是否是 semver 兼容的
cargo semver-checks
# 输出:
# ✗ 函数 `parse_gpu_csv` 现在是私有的(之前是公共的)
# → 这是 BREAKING 更改。提升 MAJOR 版本。
#
# ✗ 结构体 `GpuInfo` 有新的必需字段 `power_limit_w`
# → 这是 BREAKING 更改。提升 MAJOR 版本。
#
# ✓ 函数 `parse_gpu_csv_v2` 已添加(非破坏性)
cargo-tree — 依赖可视化和去重
cargo tree 内置于 Cargo 中(无需安装),对于理解依赖图非常宝贵:
# 完整依赖树
cargo tree
# 查找为什么包含特定 crate
cargo tree --invert --package openssl-sys
# 显示从你的 crate 到 openssl-sys 的所有路径
# 查找重复版本
cargo tree --duplicates
# 输出:
# syn v1.0.109
# └── serde_derive v1.0.193
#
# syn v2.0.48
# ├── thiserror-impl v1.0.56
# └── tokio-macros v2.2.0
# 仅显示直接依赖
cargo tree --depth 1
# 显示依赖特性
cargo tree --format "{p} {f}"
# 计算依赖总数
cargo tree | wc -l
去重策略:当 cargo tree --duplicates 显示同一 crate 有两个主要版本时,检查是否可以更新依赖链以统一它们。每个重复都会增加编译时间和二进制大小。
应用:多 Crate 依赖卫生
workspace 使用 [workspace.dependencies] 进行集中版本管理——这是一个极好的实践。与用于大小分析的 cargo tree --duplicates 结合使用,可以防止版本漂移并减少二进制膨胀:
# 根 Cargo.toml — 所有版本集中在一个地方固定
[workspace.dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
regex = "1.10"
thiserror = "1.0"
anyhow = "1.0"
rayon = "1.8"
项目推荐的添加:
# 添加到 CI 流水线:
cargo deny init # 一次性设置
cargo deny check # 每个 PR — 许可证、咨询、禁止
cargo audit --deny warnings # 每个推送 — 漏洞扫描
cargo outdated --workspace # 每周 — 跟踪可用更新
项目推荐的 deny.toml:
[advisories]
vulnerability = "deny"
yanked = "deny"
[licenses]
allow = ["MIT", "Apache-2.0", "BSD-2-Clause", "BSD-3-Clause", "ISC", "Unicode-DFS-2016"]
copyleft = "deny" # 硬件诊断工具 — 无 copyleft
[bans]
multiple-versions = "warn" # 跟踪重复,暂时不阻塞
wildcards = "deny"
[sources]
unknown-registry = "deny"
unknown-git = "deny"
供应链审计流水线
flowchart LR
PR["Pull Request"] --> AUDIT["cargo audit<br/>已知 CVE"]
AUDIT --> DENY["cargo deny check<br/>许可证 + 禁止 + 来源"]
DENY --> OUTDATED["cargo outdated<br/>每周计划"]
OUTDATED --> SEMVER["cargo semver-checks<br/>仅库 crate"]
AUDIT -->|"失败"| BLOCK["❌ 阻止合并"]
DENY -->|"失败"| BLOCK
SEMVER -->|"破坏性更改"| BUMP["提升主要版本"]
style BLOCK fill:#ff6b6b,color:#000
style BUMP fill:#ffd43b,color:#000
style PR fill:#e3f2fd,color:#000
🏋️ 练习
🟢 练习 1:审计你的依赖
在任何 Rust 项目上运行 cargo audit 和 cargo deny init && cargo deny check。发现多少咨询?你的树中有多少许可证类别?
解决方案
cargo audit
# 注意任何咨询 — 通常是 chrono、time 或较旧的 crate
cargo deny init
cargo deny list
# 显示许可证细分:MIT (N)、Apache-2.0 (N) 等
cargo deny check
# 显示所有四个维度的完整审计
🟡 练习 2:查找并消除重复依赖
在 workspace 上运行 cargo tree --duplicates。找到出现在两个版本的 crate。你能更新 Cargo.toml 来统一它们吗?测量编译时间和二进制大小的影响。
解决方案
cargo tree --duplicates
# 典型:syn 1.x 和 syn 2.x
# 查找谁引入了旧版本:
cargo tree --invert --package syn@1.0.109
# 输出:serde_derive 1.0.xxx -> syn 1.0.109
# 检查是否有更新的 serde_derive 使用 syn 2.x:
cargo update -p serde_derive
cargo tree --duplicates
# 如果 syn 1.x 消失了,你已经消除了一个重复
# 测量影响:
time cargo build --release # 之前和之后
cargo bloat --release --crates | head -20
关键要点
cargo audit捕获已知 CVE — 在每个推送和每日计划上运行它cargo deny执行四个策略维度:咨询、许可证、禁止和来源- 使用
[workspace.dependencies]在多 crate workspace 中集中版本管理 cargo tree --duplicates揭示膨胀;每个重复都会增加编译时间和二进制大小cargo-vet适用于高安全环境;cargo-deny对大多数团队足够