composer audit 扫不出深层依赖漏洞是因为它仅检查 vendor/ 下已安装的包,不解析未加载的子依赖;即使 composer.lock 中记录了有漏洞的间接依赖,若其未实际存在于 vendor/ 目录(如被 replace 覆盖或自动丢弃),audit 就无法检测到。

composer audit 为什么扫不出深层依赖的漏洞
因为 composer audit 只检查 vendor/ 目录下**已实际安装**的包,不递归解析未被加载的子依赖——哪怕 composer.lock 里写了某个间接依赖的版本,只要它没出现在 vendor/ 中(例如被 Composer 自动丢弃、或被 replace 规则覆盖),audit 就完全看不见。
常见错误现象:
- 执行
composer audit显示 “No vulnerabilities found”,但composer show --tree guzzlehttp/guzzle显示它拉进了有 CVE 的psr/http-message子依赖 - CI 构建中
composer install --no-dev后跑 audit,结果漏掉require-dev里带漏洞的测试工具链
解决办法:
- 审计前必须先运行
composer install或composer update,确保vendor/是完整快照 - 用
composer show --tree手动展开树,定位可疑包后单独 audit:composer audit guzzlehttp/guzzle - 加
--full参数强制扫描所有已安装包(包括 dev-only):composer audit --full - 若项目用了
replace或provide,需人工核对composer.lock的packages区域是否真删掉了被替代的包
如何从 composer.lock 定位冲突包的真实来源
当 composer install 报错 “Your requirements could not be resolved”,或 composer.lock 里同一包名出现多个版本(如 symfony/console 同时存在 5.4.32 和 6.4.0),说明存在隐式冲突——Composer 已自动降级或拆分依赖,但你未必知道谁在推这个包。
关键动作不是看 composer.json,而是查 composer.lock 的结构:
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
- 搜索
"name": "symfony/console",找到所有匹配块,记下每个的version和source.reference - 对每个版本,执行
composer depends --tree symfony/console:5.4.32,看哪些顶层包在拉它 - 重点盯
dist.shasum:如果两个同名包的哈希一致,大概率是同一个 ZIP 被不同路径引用;如果不一致,说明确实是不同源(比如一个来自 packagist,一个来自 fork) - 检查
platform字段变化:PHP 版本或扩展约束变动会触发 Composer 改变解包策略,导致看似无关的包版本漂移
composer show --tree 输出太长?快速过滤关键路径
composer show --tree 默认展开全部层级,大型项目可能输出上千行。真正要揪的是“谁把高危包拖进来的”,不是背完整棵树。
实操建议:
- 用
composer show --tree | grep -A5 -B5 "vulnerable-package-name"定位上下文 - 加
--direct忽略传递依赖:composer show --tree --direct monolog/monolog - 导出为 JSON 再用
jq筛选深度大于 3 的路径:composer show --tree --format=json | jq 'select(.depth > 3)' - 注意
dev-main或dev-feature/xxx这类不稳定引用——它们不会出现在 Symfony 安全数据库里,composer audit会直接跳过,但可能引入未审计代码
audit 报告里 “ignored” 不等于安全
composer audit --format=json 输出中出现 "ignored": true,不代表那个漏洞已被修复,只代表它被配置规则豁免了。这在大型项目中极易埋雷。
容易被忽略的点:
-
composer.json里"audit": {"ignore": ["CVE-2023-1234"]}是全局生效的,连 CI 都会跳过 - 私有包或重命名包(如
myorg/guzzle)即使和guzzlehttp/guzzle代码完全一样,audit 也认不出来 -
dev分支版本("version": "dev-main")永远不进审计范围,但你 deploy 时可能正用着它 - audit 不校验
platform配置,比如你锁了"php": "8.1",但 PHP 本身有 CVE,它不管
复杂点在于:漏洞是否真实存在,取决于运行时环境+代码路径+输入数据,而 audit 只做静态比对。人工确认仍不可替代。

















