别用单行 composer audit 当安全扫描脚本——它默认跳过 dev 依赖、不校验平台兼容性、不连 Snyk、对私有包静默忽略,CI 里跑完等于没跑。

直接结论:别用单行 composer audit 当安全扫描脚本——它默认跳过 dev 依赖、不校验平台兼容性、不连 Snyk、对私有包静默忽略,CI 里跑完等于没跑。
为什么 composer audit 在 CI 里总“假阴性”?
常见现象是命令执行成功(退出码 0),日志却只显示 No advisories found,但项目其实存在高危漏洞(比如 guzzlehttp/guzzle:6.5.0 的 CVE-2021-3114)。原因不是没漏洞,而是审计根本没真正启动:
-
composer.lock缺失或哈希与vendor/不一致 →audit主动跳过,不报错也不扫描 - 用了国内镜像(如阿里云、腾讯云)→ 安全通告接口未同步,
Could not fetch advisories静默失败 - 版本低于 2.5.0 → 命令存在但不对接 Snyk,CVE-2023-41277 这类 Laravel 高危项直接漏掉
-
COMPOSER_NO_AUDIT=1环境变量被设为 true → 全局禁用,加任何参数都无效
composer audit 必须带的参数组合
生产级 CI 脚本不能只写 composer audit。真实可用的最小安全扫描命令是:
composer audit --dev --severity=critical,high --no-interaction --timeout=30 --format=json
说明:
-
--dev:必须加。否则phpunit/phpunit、mockery/mockery等开发依赖漏洞全被跳过 -
--severity=critical,high:只拦截真正可利用的风险。low/medium 漏洞太多,会阻断构建但无实质意义 -
--no-interaction:防止流水线卡在交互提示上 -
--timeout=30:网络抖动时避免无限等待(Packagist 安全接口有时响应慢) -
--format=json:结构化输出,方便后续用jq或脚本解析.advisories_count字段判断是否真有漏洞
怎么确认脚本真的在查漏洞,不是空转?
光看命令执行成功没用,得验证审计逻辑是否生效:
- 运行
composer config config.audit,输出必须是完整 JSON 对象(如{"block-insecure": true}),不是null或报错 - 手动引入已知问题包:
composer require guzzlehttp/guzzle:6.5.0,再跑composer audit --dev -v,日志里必须出现Found 1 security vulnerability和对应 CVE ID - 删掉
composer.lock后执行composer install -v,观察日志是否含Auditing dependencies行 —— 没这行说明配置未加载 - 检查
composer config repositories输出,确认没混入非 HTTPS 自定义源(尤其域名生僻的),这类源默认不拉取安全元数据
漏洞扫出来之后,脚本该做什么?
composer audit 只报不修,脚本不能停在“发现漏洞”就结束:
- 用
jq '.advisories[] | select(.severity == "critical") | .package'提取高危包名,再触发composer update <package></package>(注意加锁策略,避免破坏兼容性) - 必须并行跑
composer validate --strict:audit 不管 PHP 版本或扩展是否匹配config.platform,而validate会校验,防止部署时崩溃 - 对私有包,
audit默认跳过。若需覆盖,得在composer.json的repositories里显式声明"type": "package"并提供security-advisories字段 - 别信退出码 0 就安全——关键看 JSON 输出里的
advisories_count字段值是否为 0;某些镜像会伪造空响应但实际没查
最易被忽略的一点:composer audit 的结果依赖 composer.lock 的精确性,而 lock 文件本身可能被 gitignore、手动生成或 CI 缓存污染。每次扫描前,务必确保它是 composer install 或 composer update 生成的真实快照。


















