依赖扫描
每次变更自动发现第三方依赖中的已知漏洞。
为什么
现代应用 80-90% 是第三方代码——你的 package.json 拉 1000 个传递依赖,各有自己的 CVE。漏洞代码不是你写的但你发布它且你负责。log4j 漏洞(CVE-2021-44228)让任何未补丁服务器通过精心日志串执行任意代码,存在于数千开发者从未听过 log4j 的应用——它传递性引入。依赖扫描自动化不可能手动做的事: 每次变更把每个依赖对 CVE 数据库查,使传递包中新披露漏洞数小时 内抓非入侵后。
何时使用
每个 PR(新高/危 CVE 即阻止)、主分支每晚(抓上次合并后披露的 CVE)、每次发布前。别跳——一次漏扫是一次漏漏洞。
如何做
1. 选扫描器接入 CI - Snyk(广语言支持、好修复建议) - Dependabot(GitHub 原生、免费、创建 PR 升版本) - npm audit / pnpm audit(内置、快、但上下文少) 2. 每个 PR 和主分支每晚扫 - PR: 变更引入的新高/危 CVE 即构建失败 - 每晚: 扫当前 lockfile,任何新 CVE 开 issue - 不对已存在 CVE 失败(PR 前就在)——分开追踪 3. 按可利用性而非仅严重性分类每个发现 - 仅开发依赖(如测试运行器)中的危 CVE 生产不可利用 - 处理用户输入的生产依赖中的中 CVE 在你上下文可能危 - 用扫描器的可达性分析若可用(Snyk Code、OSV-Scanner) 4. 通过升级修非抑制 - 升到修复版本;若需大升级则排期 - 若无修复用补丁 fork、虚拟补丁(Snyk)或隔离依赖 - 仅在有文档理由和过期日时抑制发现 5. 保持依赖列表小且新 - 删未用依赖(depcheck、knip)——少依赖少 CVE - 启用 Renovate/Dependabot 保持版本新——陈旧依赖累积 CVE - lockfile 中固定传递依赖使子依赖升级不静默引入漏洞
示例
# .github/workflows/dependency-scan.yml
name: 依赖扫描
on: [pull_request, schedule: [{cron: "0 2 * * *"}]] # 每晚 2 点
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high --fail-on=all
# 引入新高/危 CVE 即 PR 失败。
# Dependabot 配置: .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
# Dependabot 开 PR 升版本。及时审查合并——
# 若 CVE 危急,搁置 2 周的 PR 是 2 周暴露。
# 分类示例:
# CVE-2024-1234(危)在 [email protected]——生产用、处理用户输入
# → 立即修: npm i [email protected]
# CVE-2024-5678(高)在 jest@29——仅开发、不在生产 bundle
# → 排下个迭代,今天非生产风险常见问题
- ⚠对所有 CVE 包括已存在的失败构建使团队瘫痪——构建因传递依赖中不可修 CVE 红了数月,新 CVE 被无视,信号丢失。
- ⚠仅扫直接依赖漏传递依赖中漏洞——log4j 漏洞在大多受影响应用中是传递的,浅扫会完全漏它。
- ⚠把严重性当唯一分类因素——仅开发测试框架中的危 CVE 非生产风险,而生产 JSON 解析器中的中原型污染 CVE 在你应用中可能可利用。
- ⚠无理由无过期抑制发现使扫描器变绿——抑制累积,真漏洞被藏,无人审抑制列表直到入侵。
最佳实践
- ✓仅对变更引入的新高/危 CVE 失败 PR,已存在 CVE 追踪在单独 backlog——保持信号可行动而非淹没团队于红构建。
- ✓用 带可达性分析的扫描器(Snyk Code、OSV-Scanner)确定漏洞函数是否真在你代码中被调——不可达代码路径中的 CVE 非即时风险。
- ✓启用 Renovate 或 Dependabot 用自动 PR 保持依赖新——减 CVE 暴露最便宜的方式是不在每个包上落后 20 个小版本。
- ✓周期性审查和清理依赖列表(depcheck、knip)——删未用依赖永久移除其 CVE 并减扫描面。