composer audit可用需同时满足三条件:Composer≥2.5.0、全局启用experimental.audit、项目存在有效composer.lock;它仅比对lock中精确版本与FriendsOfPHP安全数据库,不扫描私有包、fork包、源码或PHP配置漏洞。

直接用 composer audit,但前提是 Composer 版本 ≥ 2.5.0 —— 低于这个版本会报 “Command 'audit' is not defined”,不是命令写错,是压根没这个功能。
怎么确认 composer audit 能用
别跳过这步,CI 或本地环境经常卡在这儿:
- 运行
composer --version:输出必须是Composer version 2.5.x或更高;2.4.5这类就无效 - 运行
composer list | grep audit:没输出 = 命令未加载(即使版本对,某些企业镜像或自定义配置也会禁用) - 若版本达标但仍不可用,检查全局配置:
composer config --global experimental.audit应返回true;否则手动开启:composer config --global experimental.audit true - CI 中常见陷阱:基础镜像锁死了 Composer 2.4,
composer self-update权限不足或被代理拦截 —— 此时得换镜像或手动下载composer.phar
composer audit 扫什么、不扫什么
它只比对 composer.lock 里记录的**精确版本 + 完整哈希** 和 FriendsOfPHP/security-advisories 数据库里的已知条目。不是“看 vendor 目录猜”,也不是“读 composer.json 约束推演”:
- ✅ 扫:
doctrine/dbal锁在3.6.4,而数据库里明确写了该版本存在CVE-2023-30518 - ❌ 不扫:你
composer.json写了"monolog/monolog": "^2.0",但lock里实际装的是2.10.0—— audit 只认2.10.0,不关心^2.0能不能升 - ❌ 不扫:私有包(如
acme/internal-sdk)、fork 包(如myorg/guzzlehttp-guzzle)、带-dev后缀的版本(如dev-main),默认直接跳过,且不报 warning - ❌ 不扫:NVD 原始库、PHP 配置漏洞、代码逻辑缺陷、SQL 注入点 —— 它只做 CVE 映射,不是 SAST 工具
CI 里怎么跑才不误伤、不漏报
默认行为在流水线里几乎必然失败:一条 low 级旧漏洞就能让构建红掉,而真正该拦的 critical 却可能因缓存或网络被跳过:
- 加
--force:强制绕过本地缓存,每次查远程数据库,避免 CI 节点缓存过期导致漏报 - 加
--no-dev:生产环境依赖链里不该包含 PHPUnit、phpstan 这类开发工具,它们的漏洞不影响线上服务 - 用
--severity=high --severity=critical:只让 high/critical 级别触发非零退出码;--ignore-severity=low是无效参数,别信过时文档 - 加
--fail-on-security-violations:配合 severity 使用,否则即使有 critical 漏洞,命令也默认成功退出 - 别省略
--locked:虽然它是默认行为,但在 CI 脚本里显式声明,能防止未来 Composer 行为变更带来的隐性风险
发现漏洞后,为什么 composer update 不自动修复
因为 Composer 不理解“安全”,它只忠实地满足 composer.json 里的版本约束。一个典型陷阱:
- 你的
composer.json写着"guzzlehttp/guzzle": "~7.2",当前lock是7.2.0,它有 RCE 漏洞 -
composer update guzzlehttp/guzzle可能只升到7.2.9(仍在~7.2范围内),但修复版其实是7.5.0 - 必须人工干预:要么放宽约束(如改
^7.0),要么指定升级目标(composer update guzzlehttp/guzzle:^7.5),再验证composer audit是否清零 - 临时屏蔽某个 CVE?可用
"config": { "audit": { "ignore": ["CVE-2023-1234"] } },但记得加注释说明原因和过期时间 —— 这个配置不会被composer update自动清除
真正危险的从来不是“有没有漏洞”,而是“你以为扫过了,其实没扫准”。composer audit 的结果高度依赖 composer.lock 的完整性、版本号的精确性、以及数据库收录的及时性。别让它跑在缺失 lock 文件、混用 fork 包、或跳过 --force 的 CI 节点上。


















