Composer依赖审计关键在可达性:仅锁文件中存在不等于实际加载,需验证漏洞包是否被代码真实调用;dev依赖、虚拟包、autoload遗漏等易致误报;--no-dev不改变lock内容,真正生效的是运行时环境隔离与类/函数级加载验证。

Composer 依赖审计不是“扫一遍 composer.lock 就完事”,真正影响修复可行性的,是哪些包实际被你的代码调用、哪些只是间接引入却从未执行——即「可达性(reachability)」。不区分这点,90% 的扫描告警根本不用动。
哪些包会被 osv-scanner 或 pip-audit 类工具默认纳入?
它们默认只看 composer.lock 里列出的所有包,不管是否在运行时加载。这会导致大量「理论漏洞」:比如一个 dev-only 的测试工具链里含漏洞组件,但生产环境根本不会加载它。
-
require-dev下的包默认计入扫描范围(除非你显式禁用) -
replace或provide声明的虚拟包可能被误判为真实依赖 - 通过
classmap或files方式 autoload 的脚本,若未被任何 PSR-4/PSR-0 命名空间引用,容易漏掉其依赖链
composer install --no-dev 能缩小审计范围吗?
不能。这个命令只影响安装行为,不影响 composer.lock 文件本身的内容结构。锁文件仍完整保留 require-dev 下所有包及其版本和哈希值,所有审计工具读取的仍是同一份数据。
- 真正生效的是构建时的环境隔离:CI 中用
--no-dev安装 + 运行时autoload.php不加载 dev 命名空间,才能让漏洞包不可达 - 若审计报告里某漏洞出现在
phpunit/phpunit,而你确认线上从不 require PHPUnit 类,则该条可标记为「不可达」,无需升级 - 注意:
autoload-dev中定义的类,若被主 autoload 规则意外覆盖(如路径重叠),仍可能被加载
如何手动验证某个包是否可达?
最直接的方式是检查运行时是否真会加载它的类或函数。别信文档,看实际调用链:
- 用
grep -r 'use.*SomeVulnerableClass' src/ app/查显式引用 - 用
php -d display_errors=1 -d error_reporting=-1 -r "require 'vendor/autoload.php'; class_exists('Vulnerable\Class') && print 'loaded\n';"快速试探 - 若项目用了
composer dump-autoload --optimize,检查生成的vendor/composer/autoload_classmap.php是否包含该类路径 - 对函数型包(如
symfony/polyfill),需确认是否有function_exists()判断或直接调用
真正的难点不在扫描,而在判断「这个漏洞对我有没有攻击面」——它取决于你的代码怎么用它,而不是它存不存在。很多团队卡在 PR 合并前反复争论某条 CVE 是否要修,其实只需要跑一次 class_exists 就能终结讨论。











