把一个陌生仓库交给 Coding Agent 后,很多人的第一句话是:“先把项目跑起来。”过去由人配置环境,至少还要读一遍文档、逐条复制命令。Agent 则会读完说明,直接生成并执行安装命令。人也因此很容易把 setup 当成准备工作。
预印本论文 Setup Complete, Now You Are Compromised
指出了这里的时间差:下载依赖本身就可能执行代码。开发者还在等待环境就绪,软件包里的安装脚本或许已经开始运行。等到最终
diff 出现,第一次代码执行可能早已结束。
论文作者围绕这条路径构造了十二个场景、五类攻击,并测试九种由不同模型和客户端组成的 Coding Agent 配置。结果均来自作者实验,目前没有独立复现;这些构造场景也不能证明现实攻击已经普遍发生。实验细节与限制可查阅论文 HTML 版。
为了启动项目,Agent 会先读 README 和依赖配置,例如
pyproject.toml、requirements.txt,有时还要参考
Makefile 或 CI 配置。接着,它调用 pip、npm 或
Cargo,从外部下载软件包并装进开发环境。下文把这类依赖管理工具统称为
package manager。
获取软件包不只是复制文件。Python 包可以在构建或安装时运行
setup.py,首次导入时还会执行 __init__.py;npm
包可以设置 postinstall;Cargo 构建依赖时会运行
build.rs。这些入口原本服务于编译和环境配置,也让软件包能够在安装、构建或首次导入时执行代码。
如果项目说明把 Agent 引向攻击者控制的软件包,package manager 就可能运行其中的代码。此时 Agent 还没开始修改业务文件,开发者也没有最终 diff 可看。项目尚未启动,不代表第三方代码尚未运行。
恶意代码也不必出现在仓库里。项目配置或 Pull Request 可以只有一项看似普通的依赖变更,真正要运行的代码留在外部软件包中,等 package manager 主动下载。
我在 2026 年 4 月的《AI 编程工具的配置文件,现在是攻击入口》里讨论过另一种风险:攻击者把恶意文字放进仓库,Agent 误把这些文字当成指令。这篇论文里的安装要求却不需要伪装成恶意命令。安装依赖本来就是开发者授权的正常任务。
论文作者做了一组对照。他们让程序在报错信息中建议安装攻击者指定的软件包,九种受测配置全部拒绝了请求。在这组实验里,客户端把程序输出当作不可信内容,没有直接照做。
同一项安装建议写进项目说明,角色就变了。Agent 需要参考这些文档才能配置环境;如果一概拒绝文档里的安装要求,“把项目跑起来”也就无法完成。
攻击者因此可以只修改文档或依赖配置,让安装命令指向另一个软件包或下载地址。审查者看到的是一项依赖变更,不会自动看到那个外部软件包在安装时做了什么。
一条安装命令至少决定三件事:安装哪个包、从哪里下载、使用什么版本。论文发现,Agent 对这三项选择的判断并不一样。
明显的错别字最容易引起警觉。作者把 transformers 拼成
tranformers 时,几乎所有受测配置都发现了异常。换成
azurecore 和真实包 azure-core
这样的分隔符差异,结果就取决于具体的模型与客户端组合。同一个模型换一个客户端,实验结果也可能不同。
包名正确,下载来源仍可能改变。package manager 通常从默认的
registry,也就是软件包索引,取回代码。项目使用私有包时,可能通过
--extra-index-url
增加另一个索引;正常包名也可能因此从攻击者控制的来源解析和下载。论文报告,客户端拦截这类场景的次数,整体少于明显错拼包名的场景。
版本是另一条路径。作者保持包名和来源正常,只要求安装已经列入 CVE 公开漏洞目录的旧版本。九种配置都完成了安装,部分配置随后才给出警告。这里没有证据表明攻击者的代码已经运行;结果是开发环境进入已知脆弱状态,为后续利用留下条件。
最终 diff 只能回答 Agent 改了哪些仓库文件。它不会自动列出 package manager 此前下载并执行过的代码。模型在环境配置结束后提醒包名可疑或版本存在漏洞,也无法撤回已经运行的安装脚本。
外部软件包甚至可以运行完脚本后完全不改业务源码。此时 diff 没有漏报仓库变化,只是它从来不负责记录依赖安装的执行历史。
HTTPS、签名和哈希可以帮助确认下载内容没有在途中遭到替换;lockfile 则记录已经解析出的版本,让后续安装复现同样的结果。它们保护传输和复现,却不判断最初选择的软件包、来源和版本是否正确。
如果项目配置一开始就指向攻击者控制的包,哈希可以准确核对那个包,lockfile 也可以继续复现它。安全检查必须发生在 package manager 开始安装以前。
Agent 准备运行安装命令时,客户端可以先暂停,把命令里的包名、registry 和版本单独展示出来。开发者此时看到的是这次安装的实际目标,而不只是一整条难以核对的终端命令。
客户端再按项目规则核对这些信息:名称是否对应团队准备使用的软件包,registry 是否获准使用,版本是否存在已知漏洞。任何一项无法确认,安装就保持暂停,等开发者看清原因后决定是否放行。
真实项目会使用公司内部 registry、Git 依赖或本地脚本,因此这套规则不能简化成一张公共软件包清单。团队需要根据每个项目实际使用的来源和依赖形式设置例外。论文也没有测量这类规则在真实项目中的误报成本。
通过检查的依赖仍应在受限环境中安装。安装过程不接触长期凭证,网络只开放任务需要的目的地。安装前检查减少选错软件包的机会,受限环境则控制代码运行后的影响,两者解决的是不同问题。
以后再让 Coding Agent “先把项目跑起来”,普通用户需要把它理解成一次运行第三方代码的授权。对构建 Agent 或 harness 的团队,安装前解析包名、来源和版本,遇到异常时暂停并请求批准,以及在受限环境中完成安装,都应该成为客户端的默认能力。这条安全边界属于执行层,不能只依赖模型临场判断。