allow-plugins 是 Composer 2.2+ 的插件白名单机制,默认拒绝所有插件;设为 true 等同于关闭安全闸门,必须显式授权可信包如 "symfony/flex": true,并配合 --no-plugins 用于生产环境。

Composer 插件不是“装了就能用”的工具,而是拥有项目构建期代码执行权的第三方逻辑——它能改 autoload、劫持下载、注入脚本。不加限制等于把构建流程的控制权交给任意包作者。
allow-plugins 是什么,为什么不能设为 true
allow-plugins 是 composer.json 中 config 下的白名单开关,从 Composer 2.2 起默认拒绝所有插件运行。它不是“启用插件功能”,而是“授权哪些包可以注册插件”。
- 设
"allow-plugins": true等同于关掉安全闸门,任何依赖(包括 transitive 依赖)都能悄悄加载并执行代码 - 设
"allow-plugins": {"*": false}是最小化起点,但必须显式添加你真正需要的包,比如"symfony/flex"或"laravel/pint" - 通配符
"vendor/*"不被支持,只能写具体包名;别试图用"*/*"绕过,它会被忽略
如何安全地添加一个插件到 allow-plugins
不是看到报错就加进白名单,得先确认这个插件是否真有必要、是否可信:
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
- 查它的 Packagist 页面:是否有 Verified 标识、LICENSE 是否明确(MIT/Apache-2.0)、GitHub stars ≥ 500 且最近 6 个月有提交
- 看它注册了哪些 hook:运行
composer show vendor/name,检查输出中是否含Plugin字样,并翻其源码看是否调用exec()、eval()或写入敏感路径 - 在 composer.json 中只写一行,例如:
"symfony/flex": true,不要加注释或空格干扰 JSON 解析 - 加完立刻跑
composer install --dry-run验证是否仍被拦截;若还报错,说明该插件依赖另一个未授权插件(如某些 scaffold 工具链会带二级插件)
--no-plugins 不是可选项,是生产环境硬要求
--no-plugins 强制跳过所有插件加载,包括全局和项目级,是 CI/CD 和部署阶段不可妥协的安全边界。
- 它必须紧跟命令之后:
composer install --no-plugins✅,而composer --no-plugins install❌(等价于composer --help) - CI 脚本里不能靠“开发者记得加”,必须写死——哪怕用了 wrapper 脚本(如
bin/composer),也要确保它没偷偷加插件 flag - 与
--no-scripts不互斥:前者防插件,后者防脚本;生产环境推荐组合使用:composer install --no-plugins --no-scripts --no-dev --prefer-dist - 它不影响
vendor/autoload.php生成,也不阻止依赖下载解压,只切断“执行链”——这才是关键
全局插件和 allow-plugins 的冲突点
全局安装的插件(composer global require)不受项目级 allow-plugins 控制,它们会在每次 composer install/update 时自动激活。
- 这意味着:即使你在项目中把
allow-plugins设为{"*": false},一个全局安装的恶意插件仍可能执行 - 验证方式:
composer global list查看已装全局包;composer diagnose会提示 “You have plugins installed globally” - 对策:禁用全局插件机制,设环境变量
COMPOSER_NO_PLUGINS=1,或直接删掉~/.composer/vendor/下的插件目录 - 更根本的解决是避免
composer global require—— 用项目级require --dev替代,让依赖可见、可审计、可锁定
最易被忽略的其实是“谁在维护这个插件”和“它为什么必须现在运行”。allow-plugins 列表不该是静态快照,而应随每次 composer update 审查更新——因为一个今天可信的插件,明天可能被接管、被投毒、被弃用。


















