尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

Cargo deny 与依赖治理:阻止有漏洞的 crate 进入你的项目

Cargo deny 与依赖治理:阻止有漏洞的 crate 进入你的项目
📅 发布时间:2026/7/15 21:50:10

Cargo deny 与依赖治理:阻止有漏洞的 crate 进入你的项目

更可怕的是,我问了自己三个问题:这些 crate 有已知漏洞吗?它们的开源许可证我能商用吗?有没有重复编译不同版本的同一个库(比如syn 1.x和syn 2.x同时存在)?——我一个都答不上来。

今天这篇文章,就是我用cargo-deny来解决这三个问题的一手记录。

一、cargo-deny 是什么,为什么需要它

cargo-deny是一个 Cargo 子命令,专门做依赖审计(Dependency Audit)。它检查三件事:

  1. 安全漏洞(Advisories):依赖的 crate 是否在 RustSec 数据库中登记了已知漏洞?
  2. 许可证合规(Licenses):依赖的许可证是否与你的项目兼容?
  3. 依赖源头(Sources):依赖是否来自受信任的源(比如 crates.io)?

还有一个很实用的功能:Ban 列表——你可以禁止特定 crate(或特定版本)进入项目。

# 安装 cargo-deny(只需要做一次) cargo install cargo-deny # 在项目根目录生成默认配置文件 cargo deny init # 运行完整的审计检查 cargo deny check

第一次跑完cargo deny check后的输出大概是这样:

error[A001]: net2 0.2.39 has been yanked! ┌─ /home/user/project/Cargo.lock:42:1 │ 42 │ net2 0.2.39 registry+https://github.com/rust-lang/crates.io-index │ ------------------------------------------------------------- yanked crate detected │ = net2 v0.2.39 └── mio v0.6.23 └── tokio-reactor v0.1.12 └── ... error[L001]: failed to get license requirements ┌─ ring 0.16.20 │ = ring v0.16.20 ├── LICENSE: OpenSSL └── warning[B004]: found 2 duplicate entries for crate 'syn' ┌─ /home/user/project/Cargo.lock:85:1 │ 85 │ syn 1.0.109 registry+https://github.com/rust-lang/crates.io-index │ ------------------------------------------------------------- syn v1.0.109 │ 87 │ syn 2.0.87 registry+https://github.com/rust-lang/crates.io-index │ --------------------------------------------------------- syn v2.0.87

初次看到这个输出时,我心里只有一个想法:幸好跑了。然后开始逐项修。

二、配置 deny.toml:定制你的依赖策略

cargo deny init生成的配置文件是deny.toml,这是整个依赖治理的核心。下面是我在 AI CLI 项目中实际使用的配置,逐段解读:

# ===== deny.toml ===== # 这是我 AI CLI 项目的依赖审计配置 # ---------- 1. 安全漏洞 ---------- [advisories] # 忽略社区尚未处理的漏洞(谨慎使用!) # 只有确认过不影响自己项目才能加进来 ignore = [ # 例子:RUSTSEC-2020-0071(time crate 的段错误问题) # 我确认了我的项目不使用 time crate 的受影响 API # "RUSTSEC-2020-0071", ] # 漏洞数据库的 URL(默认是 GitHub 上的 RustSec 仓库) db-url = "https://github.com/rustsec/advisory-db" # 拉取间隔:多久检查一次是否有新的漏洞报告 db-path = "~/.cargo/advisory-db" # 如果 CI 中因为网络问题拉不到数据库,宽限期允许跳过 # 生产构建时不建议跳过(设置成 [] 就是不留情面) yanked = "deny" unmaintained = "warn" # 无人维护的 crate,给警告 unsound = "deny" # API 不安全但未修复,拒绝 notice = "warn" # 一般性公告,提醒但允许 # ---------- 2. 许可证合规 ---------- [licenses] # 我的 AI CLI 是 MIT 许可证,只允许以下兼容的许可证 allow = [ "MIT", "Apache-2.0", "Apache-2.0 WITH LLVM-exception", "BSD-3-Clause", "Unicode-DFS-2016", "ISC", "MPL-2.0", # Mozilla 公共许可证 "BSL-1.0", # Boost 软件许可证 "CC0-1.0", # 公共领域 ] # 如果有依赖的许可证不兼容,CI 直接挂掉 # 但允许一些"例外"(手动确认过合规的) exceptions = [ # 某些 crate 许可证文件名称不规范,手动指定 # { name = "ring", allow = ["OpenSSL"] }, ] # 未知许可证的处理(有些 crate 没声明许可证) unknown = "deny" # 对商业项目这是必须的 copyleft = "deny" # GPL/AGPL 等传染性许可证不能用 # ---------- 3. 禁止列表 ---------- [bans] # 禁止引入某些 crate deny = [ # 阻止 unicode-* 系列老 crate 的意外引入 # 因为 Rust 标准库已经内置了 unicode 支持 { name = "openssl-sys", wrappers = [] }, # 我只用 rustls { name = "native-tls", wrappers = [] }, # 同上,强制 rustls ] # 允许重复依赖(不触发 warning 的例外) # 有时候同一个库的多个版本是被迫的(间接依赖冲突) skip = [ { name = "windows-sys", version = "0.45" }, { name = "windows-sys", version = "0.48" }, { name = "windows-sys", version = "0.52" }, # ↑ Windows API 的版本碎片化是 ecosystem 的问题 # 我们作为应用层只能接受,但不加 skip 会一直报警 ] # 精简依赖树:某些 crate 有更好更小的替代品 skip-tree = [ # 如果项目中已经没有使用 time crate,但依然出现在依赖树里 # 这通常是某个间接依赖的残留,需要排查 ] # ---------- 4. 依赖源限制 ---------- [sources] # 只允许来自官方 crates.io 的依赖 # 如果你的项目还会从私有 registry 拉,需要在这里配置 unknown-registry = "deny" unknown-git = "deny" # 不允许直接依赖 Git 仓库 # 如果需要内网私有 registry,在这里配置 # 但我的 AI CLI 只用公共 crates allow-registry = ["https://github.com/rust-lang/crates.io-index"]

配置完跑一遍之后,所有不合规的 crafe 都会被打回——在 CI 里加了这步之后,我再也没遇到过"不小心引了个 GPL 的库"的尴尬情况。

flowchart TD A["推送代码 / 合并 PR"] --> B["CI: cargo deny check"] B --> C{"检查1: 安全漏洞"} C -->|通过| D{"检查2: 许可证兼容"} C -->|失败| C1["CI 失败<br/>→ 通知开发者修复"] D -->|通过| E{"检查3: Ban 列表"} D -->|失败| D1["CI 失败<br/>→ 替换为兼容许可证的替代库"] E -->|通过| F{"检查4: 依赖源"} E -->|失败| E1["CI 失败<br/>→ 去除被禁用的 crate"] F -->|通过| G["✅ 构建继续"] F -->|失败| F1["CI 失败<br/>→ 确认依赖来源"] style C1 fill:#ffcdd2 style D1 fill:#ffcdd2 style E1 fill:#ffcdd2 style F1 fill:#ffcdd2 style G fill:#c8e6c9

三、修复实战:常见问题怎么解

跑完cargo deny check之后,你一定会遇到一堆报错。下面是我踩过的几个典型坑及修复方法:

# 1. 重复依赖(Duplicate crate) # 问题:syn v1.0 和 syn v2.0 同时出现 # 原因:某个间接依赖还在用 syn v1 # 解决方法: # ① 运行 cargo tree -i syn 查看是谁引用了旧版 # ② 如果那个 crate 有更新版本支持 syn v2,升级它 # ③ 如果是社区的废弃 crate,考虑替换或 fork # 可以在 [bans] 中配置 [bans] multiple-versions = "deny" highlight = "all" # 在输出中高亮显示所有重复的 crate # 2. yanked crate # 问题:某个版本被作者或社区撤回了 # 解决:直接升级或降级,没有商量余地 # 配置中已设 yanked = "deny",CI 会自动拦住 # 3. 许可证不兼容 # 问题:某个传递依赖用了 GPL-3.0 # 解决: # ① cargo tree -i <crate-name> 看是谁带进来的 # ② 找替代品:功能相同但许可证更宽松的 crate # ③ 如果是必需的且确认合规:在 licenses.exceptions 中显式声明

来看一个实际修复例子。我的项目引用了reqwest(HTTP 客户端),它默认依赖native-tls(基于 OpenSSL):

// 修复前:Cargo.toml // reqwest 默认带 native-tls,引入了一堆 OpenSSL 依赖 // [dependencies] // reqwest = { version = "0.12", features = ["json"] } // ↓↓↓ 换成 rustls ↓↓↓
# 修复后:Cargo.toml # 把 native-tls 换成纯 Rust 实现的 rustls [dependencies] reqwest = { version = "0.12", default-features = false, features = [ "json", "rustls-tls", # ← 用 rustls 代替 native-tls "http2", ]} # 依赖树对比(运行 cargo tree 可以看到): # 修复前:reqwest → hyper-tls → native-tls → openssl → openssl-sys # 修复后:reqwest → hyper-rustls → rustls → ring(纯 Rust)

不仅解决了 OpenSSL 许可证问题,还减少了编译时间(openssl-sys需要 C 编译器),二进制体积也小了。

四、把 cargo-deny 嵌入 CI/CD

依赖审计最大的价值在于自动化。手动跑一次只能解决当下的问题,但新人加了个依赖、或者老依赖被发现新漏洞,你不会知道。

在 GitHub Actions 中配置:

# .github/workflows/audit.yml name: 依赖审计 on: push: branches: [main] pull_request: branches: [main] # 每天定时跑一次(漏洞数据库在更新) schedule: - cron: '0 8 * * *' # 每天早上 8 点 jobs: audit: name: Security Audit + License Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: 安装 Rust 工具链 uses: dtolnay/rust-toolchain@stable - name: 安装 cargo-deny run: cargo install cargo-deny --locked - name: 运行依赖审计 run: cargo deny check advisories bans licenses sources # 四个子命令分别检查: # advisories → 安全漏洞 # bans → 禁止列表 + 重复依赖 # licenses → 许可证合规 # sources → 依赖源
gantt title 依赖治理的持续监控节奏 dateFormat HH:mm axisFormat %H:%M section 每次 PR cargo deny check :active, pr1, 00:00, 2min section 每日定时 拉取 RustSec 数据库 :daily1, 08:00, 30s 全量审计扫描 :daily2, after daily1, 2min 发送告警(如有漏洞):daily3, after daily2, 10s section 每月 依赖更新评审 :monthly1, 00:00, 30min 淘汰过时依赖 :monthly2, after monthly1, 20min

cargo-deny 的定时任务还需要注意:schedule总是跑在默认分支,如果 main 分支和开发分支的deny.toml不一致,定时审计报的漏洞可能跟线上实际环境对不上。建议在 CI 脚本里显式 checkout 到稳定分支。

还有一个现实问题:cargo-deny报的 RUSTSEC 漏洞有些是"理论上的",生产环境不一定能被利用。对于无法立即升级的依赖,可以先用deny.toml的skip字段放行,但必须加注释写明原因和计划修复时间。裸的 skip 没有注释,一星期后没人记得为什么绕过。

五、总结

这篇文章我从"287 个依赖我一个都不知道"的恐慌出发,介绍了cargo-deny的配置和使用方法,以及如何把它嵌入 CI/CD 流程中。

Rust 生态的 crate 数量庞大、质量参差不齐,依赖治理不是一次性的工作,而是一个持续的习惯。我的建议是:

  1. 项目一开始就配置 cargo-deny:不要等项目 500 个依赖了才想起来审计,那时候改一个传递依赖可能需要重构半个项目
  2. CI 里跑,不是本地跑:手动跑没有持续性,下一周可能就忘
  3. 不要跳过 yanked = "deny":看似方便,实则埋雷
  4. 依赖少就是最好的安全策略:定期运行cargo tree看看哪些依赖可以去掉

作为自学者,依赖治理这个词听起来很"高级工程师",但实操起来其实就是 "cargo deny check + 看报错 + 改 Cargo.toml" 三步走。不要被术语吓到,动手跑一次就明白了。

如果你的项目从来没跑过依赖审计,今天就可以试试cargo deny init && cargo deny check,惊喜(或者惊吓)在等着你。我们下篇见!

相关新闻

  • LangGraph入门:从单节点Hello World图理解状态与执行模型
  • 亨得利官方钟表服务中心|官方热线及24小时维修地址权威信息通知(2026年7月更新) - 亨得利官方
  • 万物的骨架:深入理解 Mesh(网格)

最新新闻

  • 卡地亚中国官方售后服务中心|官方热线及全部网点地址权威信息公示(2026年7月更新) - 卡地亚服务中心
  • 长沙开福区适老化改造|维小达|适老厨房、卫生间、卧室改造、全屋无障碍改造、适老定制、老旧小区居家商用一站式适老化设计施工养护服务 - 一点传媒
  • 模板驱动型文档自动化:结构化模板重构文档生产流
  • Agent + 无头网站:未来所有网站,可能都只有一个界面
  • Go 并发模型的性能边界:Goroutine 数量、Channel 吞吐与锁粒度的实验分析
  • 模板驱动型文档自动化:重构企业内容生产流

日新闻

  • 告别启动盘残留:用Diskpart彻底清除U盘EFI分区与恢复完整空间
  • 2026 年宜春诚信的塑料缠绕膜厂家哪个好,缠绕膜背后的秘密:你不知道的成本陷阱 - 领域鉴赏官
  • Arch ECS 入门指南:10分钟掌握C#高性能数据驱动架构

周新闻

  • IX9104 PCIe5.0 高速交换芯片@ACP#完整规格 + 应用场景总结
  • Unity游戏集成Coze智能体:实现NPC智能对话与知识库联动
  • SAP EPIC 建行回单查询:从标准类CL_EPIC_EXAMPLE_CN_CCB_GHTD到Z类的5处关键修改

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号