上下文一升职,提权就完成了。
你把防线画在「别信工具返回」。厂商也训练模型:system / developer 压 user,user 压 tool。听起来像分级权限。
问题出在 harness:它从几十个地方捞内容——memory、skills、MCP 描述、配置、环境信息、Git 状态——再贴上消息角色。低信任源一旦被装进高权限角色,模型看到的不是「可疑文本」,是已经升职的指令。

不是注入失败,是装配升职
What's in Your Agent's Context?(arXiv:2609.01222)系统拆了 12 套真实 harness(Codex、Claude Code、Gemini CLI、OpenClaw 等),用工具 C O RA 做上下文源枚举与运行时验证。
硬数字:
- 识别 463 个上下文源,运行时验证 282 个可被 CPE 利用(平均每套 23.5 个);
- 自动枚举 1761 条候选提权路径:940 条 M-CPE、640 条 X-CPE、181 条两者兼有;
- GPT-5.5 上约 74% 路径能把指令装进高权限源,58% 行为上验证成功。
两类攻击很干脆:
- M-CPE(消息角色提权):低权限内容(工具输出、网页)被写进 skills / memory / 配置,再以更高消息角色进入上下文;
- X-CPE(跨作用域提权):本该只活在本轮会话的内容,持久化到用户级或跨项目源,重启换项目还在。
后果不是「多说了一句怪话」:完整接管、远程代码执行、拒绝服务、被劫持的 tool / skill 调用——论文里 Claude Code 可把「探索下载的模板 → 动态加载 .claude/skills → skill 内联 shell」串成 RCE。
你审计的是文本,它升的是职位
多数团队在查:这段输出脏不脏?这段网页有没有 jailbreak?
CPE 说:脏不脏之前,先问 它被贴了什么角色、活在哪个作用域。同一段字节进 tool 消息是噪声;进 system / 高优 user / 自动加载的 skill,就是策略层输入。
更糟的是不透明:配置与环境类源占验证源的大半(配置 34.4%,第三方组件 28.0%,memory/指令 24.1%,环境运行时 13.5%),且常对用户不可见。你看不见装配表,就看不见升职通道。
今晚只改一处判定
别再只测「模型拒不拒注入句」。对你在用的 coding agent,列一张最小装配表:哪些文件自动进上下文、各自消息角色、作用域是 session / project / user。任何能从工具输出或外站内容写回这些源的路径,按提权面处理——锁写、差分审、禁止静默升职。
防的是注入文案,挡不住 harness 发升职令。上下文一升职,提权就完成了。先看职位,再放权。
Context promotion is privilege escalation.
You draw the line at “don’t trust tool output.” Vendors train the model so system / developer outrank user, and user outranks tool. It sounds like a privilege hierarchy.
The leak is the harness. It pulls content from dozens of places — memory, skills, MCP descriptions, config, environment info, Git state — then stamps a message role on each chunk. Once a low-trust source is wrapped as a high-privilege role, the model does not see “suspicious text.” It sees an already promoted instruction.

Not an injection miss — an assembly promotion
What's in Your Agent's Context? (arXiv:2609.01222) systematically dissects 12 real harnesses (Codex, Claude Code, Gemini CLI, OpenClaw, and more) with C O RA: source enumeration plus runtime validation.
Hard numbers:
- 463 sources identified, 282 verified as CPE-usable at runtime (23.5 per harness on average);
- 1761 candidate escalation paths: 940 M-CPE, 640 X-CPE, 181 both;
- On GPT-5.5, roughly 74% of paths load the instruction into a higher-privileged source; 58% are behaviorally verified.
Two clean classes:
- M-CPE (message-role escalation): low-privilege content (tool output, web pages) is written into skills / memory / config, then re-enters context under a higher message role;
- X-CPE (cross-scope escalation): content that should die with the session is persisted into user- or cross-project sources and survives relaunch.
The blast radius is not “one weird sentence”: full compromise, RCE, denial of service, hijacked tool / skill calls — including a Claude Code chain of “explore a downloaded template → auto-load .claude/skills → inline shell in the skill” to RCE.
You audit the text; it promotes the job title
Most teams ask: is this output dirty? Does this page contain a jailbreak?
CPE asks first: what role was stamped on it, and what scope does it live in? The same bytes as a tool message are noise; as system / high-priority user / auto-loaded skill, they are policy input.
Opacity makes it worse: config and environment sources dominate the verified set (config 34.4%, third-party components 28.0%, memory/instructions 24.1%, environment/runtime 13.5%), and many are invisible to users. No assembly map, no promotion map.
One judgment change tonight
Stop scoring only “did the model refuse the injection sentence.” For the coding agent you actually run, write a minimal assembly table: which files auto-enter context, which message role each gets, and whether the scope is session / project / user. Any path that can write back into those sources from tool output or the open web is an escalation surface — lock writes, review diffs, ban silent promotion.
Filtering injection copy does not stop the harness from issuing a promotion. Context promotion is privilege escalation. Read the job title before you grant the keys.