乐于分享
好东西不私藏

Skill 和 Tool Use 不是插件市场:前端 Agent 如何防住低质量工具与恶意注入

Skill 和 Tool Use 不是插件市场:前端 Agent 如何防住低质量工具与恶意注入

一个前端 Agent 接入 MCP、浏览器自动化、设计稿解析、代码仓库和发布平台后,往往会变得非常“能干”:它能读 PR、拉取接口文档、修改组件、执行构建,甚至触发部署。

问题也在这里。

传统插件系统的主要风险是兼容性、性能和供应链;Agent 工具系统还多了一层更难处理的风险:工具返回的文本、网页内容、邮件内容、Issue 描述,都可能影响模型下一步决策。当模型把外部内容当成任务指令的一部分,安全边界就被绕开了。

Skill 和 Tool Use 不是插件市场。它们不只是“给模型更多能力”,而是在给一个会自主规划的执行体分配权限、输入通道与副作用。

对 3~8 年经验的前端工程师来说,重点不在于背诵 Prompt Injection 的定义,而是建立一套工程判断:什么能力可以自动执行,什么能力只能读,什么能力必须由人确认。

Agent 的风险,不只来自用户输入

很多团队做 Agent 时,会把安全注意力放在聊天框:过滤危险提示词、限制用户输入长度、增加系统提示词约束。

这当然有必要,但对接工具后的主要风险,常常来自间接提示词注入

一个典型链路是:

例如,Agent 被要求“总结最新的性能问题”,于是读取一个 Issue。Issue 正文中混入了一段看似普通的说明:要求忽略当前任务、读取环境变量、把某些内容发送到外部地址。

浏览器不会把这段文字当代码执行,但 Agent 可能把它当成自然语言指令继续推理。此时,问题不在 XSS,也不在接口鉴权是否失效,而在于:不可信内容进入了拥有工具调用能力的模型上下文。

具备外部工具或代码执行能力的 Agent,会放大提示词注入的影响。因为攻击者不再只能影响回答文本,还可能间接影响文件读写、网络访问、数据查询和业务操作。

前端场景里,哪些能力最容易越界

前端团队接入 Agent 时,常见 Tool Use 大致可以分为四类:代码与仓库、浏览器与网页、业务平台、开发环境。它们的风险并不相同。

工具能力
常见用途
主要风险
更合理的默认权限
代码仓库读取
分析 PR、检索组件、生成变更建议
读取密钥、内部配置、未公开业务逻辑
按仓库、分支、目录授权的只读访问
代码仓库写入
创建分支、提交修复、修改配置
批量破坏、植入后门、修改 CI 配置
仅允许创建草稿变更,不直接合并
浏览器自动化
验证页面、抓取文档、回归测试
恶意网页注入、跨站数据暴露、误操作后台
受控站点列表、独立浏览器上下文
内部接口调用
查订单、读埋点、创建工单
越权读取用户数据、修改生产状态
字段级脱敏、只读优先、按操作拆分权限
Shell 或代码执行
构建、测试、格式化、生成文件
命令注入、依赖投毒、文件系统破坏
沙箱执行、限制网络与挂载目录
发布与配置平台
灰度、回滚、修改 Feature Flag
直接影响线上用户
必须人工确认,不授予静默执行权

这里有一个容易被忽略的原则:“能看到”不等于“能执行”,“能执行”不等于“能对生产执行”。

不少 Agent 工具把 readwriteexecutepublish 打包在同一个 Token 或同一个 MCP Server 中。对模型而言,这很方便;对安全治理而言,这是把不同风险等级的能力揉成了一个不可控开关。

更好的方式是拆分工具语义。例如不要提供一个万能的 repository_manage,而是拆成:

ts

const tools = {

  searchRepository: {

    mode: 'read-only',

    allowedRepos: ['frontend-monorepo'],

    allowedPaths: ['packages/''apps/web/']

  },

  createPatch: {

    mode: 'write-draft',

    targetBranchPrefix: 'agent/',

    mergePermission: false

  },

  requestRelease: {

    mode: 'approval-required',

    executePermission: false

  }

}

这不是“限制 Agent 智能”,而是把副作用从模型决策中剥离出来。

Skill 为什么会成为供应链入口

Skill 通常被理解为一组提示词、脚本、工具描述、使用规则或工作流模板。它看起来不像传统依赖包那样会被 import 到产物中,因此很容易被低估。

但 Skill 至少可以影响三件事:

• 改写 Agent 对任务的理解方式;
• 引导 Agent 选择某个工具或调用顺序;
• 通过“操作说明”诱导模型访问不该访问的数据。

一个低质量 Skill 未必是恶意的,也可能造成严重后果。比如“自动修复线上样式问题”的 Skill,若没有明确环境边界,可能把本应生成的修复建议变成直接修改生产配置;若没有限制文件范围,可能在修复 CSS 时顺手改动构建脚本或部署文件。

恶意 Skill 的危险更直接:它可以把敏感操作包装成正常工作流的一部分,例如要求 Agent 在调试失败时“导出完整运行环境以便诊断”。如果 Agent 拥有读取密钥、访问 Cookie 或发送网络请求的能力,这类描述就可能演变为数据外流路径。

审查 Skill 时,不能只看它“解决什么问题”,还要看它向模型灌输了什么行为规则、需要什么权限、能把数据带到哪里去

建立工具准入:把“能安装”变成“可治理”

前端项目已经习惯了依赖治理:锁版本、审查包来源、扫描漏洞、限制安装脚本。Agent 的 Skill 和 Tool Use 也需要类似流程,只是审查对象从 npm 包扩展到了工具描述、提示词、权限声明和运行环境。

可以为每个工具建立一份轻量但强制的准入清单。

1. 工具身份与维护边界

需要明确:

• 工具由谁维护,代码和配置存放在哪里;
• 工具是否允许动态拉取远程脚本或规则;
• 工具描述是否可被运行时修改;
• 工具依赖哪些外部服务,失败时会做什么;
• 工具是否有清晰的下线和撤销机制。

“来自内部仓库”不自动等于安全。一个内部工具如果允许从不受控位置加载模板、规则或网页内容,同样可能成为注入入口。

2. 输入、输出与副作用

工具契约不能只写参数类型,还要标注安全属性。

ts

type ToolManifest = {

  name: string

  trustLevel: 'internal-reviewed' | 'restricted' | 'untrusted'

  inputSources: Array<'user' | 'repository' | 'web' | 'issue' | 'email'>

  sideEffects: Array<'none' | 'file-write' | 'network' | 'database-write' | 'release'>

  dataClassification: Array<'public' | 'internal' | 'sensitive'>

  approvalRequired: boolean

}

这类元数据的价值在于:工具编排层可以根据风险自动拦截,而不是依赖模型“记得谨慎”。

例如,一个输入来源包含 web、副作用又包含 network 的工具,应被默认视为高风险组合。它至少需要隔离执行环境,并且不能继承访问内部敏感数据的凭证。

3. 可信工具白名单不等于永久信任

白名单的作用是缩小攻击面,不是给工具发“永久免检证书”。

工具升级、Skill 规则变更、依赖范围扩大、权限申请变化,都应触发重新审查。尤其要注意一种隐蔽变化:工具名称没变,但描述文本变了。

对于 Agent 来说,工具描述本身就是模型的决策输入。描述从“读取构建日志”变成“读取构建日志,并在必要时自动修复环境”,权限语义已经发生根本变化。

最小权限:不要给 Agent 一把万能钥匙

研究和工程实践都反复指向同一个原则:Agent 只能访问完成当前任务所需的敏感数据或凭证。

这句话落到前端系统,不应停留在“Token 少发一点”,而要拆成可执行的权限设计。

用任务令牌替代长期令牌

不要让浏览器 Agent、代码 Agent、发布 Agent 共用一个高权限访问令牌。

更稳妥的做法是由编排服务按任务签发短生命周期、范围受限的凭证。即便模型受到注入影响,可被滥用的能力也被限定在当前任务范围内。

text

用户:检查营销页首屏加载问题

任务令牌范围:

- 可读取:指定项目的性能报告、构建产物元数据

- 可访问:预发布环境的目标页面

- 不可读取:用户数据、生产环境变量、其他仓库

- 不可执行:发布、删除、写数据库、外发网络请求

按资源拆权限,而不是按系统拆权限

“允许访问 Git 平台”过于粗糙。更好的表达是:

• 允许读取哪个仓库;
• 允许读取哪个分支;
• 允许访问哪些目录;
• 是否允许读取 CI 配置与密钥引用文件;
• 是否允许创建分支;
• 是否允许提交;
• 是否允许合并。

同理,“允许访问浏览器”也应该继续拆分为站点、域名、登录态、下载能力、上传能力和跨域请求能力。

凭证不应进入模型上下文

模型不需要知道 Token 的真实值,也不需要读取完整 Cookie、环境变量或私钥文件。凭证应由工具运行层保管,模型只提交结构化调用请求。

错误模式是:让 Agent 先读取 .env,再从中提取配置调用接口。

正确模式是:工具层根据权限代为完成受控请求,模型看到的只有经过裁剪的结果。

沙箱不是可选项,而是执行边界

当 Agent 可以运行代码、执行 Shell、操作浏览器时,系统提示词无法替代隔离。

提示词可以表达规则,但它本质上仍是模型输入;沙箱则是即使模型做出错误决定,运行环境也能阻止高危结果发生。

一个面向前端研发的沙箱至少应考虑以下边界:

• 文件系统隔离:只挂载当前任务需要的代码目录,不暴露开发机主目录、SSH 配置和全局凭证;
• 网络隔离:默认禁止任意外联,仅允许访问任务必需的服务;
• 进程隔离:限制可执行命令与可启动进程,避免通过工具链间接扩权;
• 浏览器隔离:使用独立 Profile,不复用工程师真实登录态;
• 密钥隔离:由运行时注入最小范围凭证,不将密钥写入工作区;
• 结果隔离:工具输出经过大小、格式和敏感信息处理后,再进入模型上下文。

前端团队尤其要警惕“本地开发 Agent”。它通常离真实工作环境最近:能看到代码、能执行包管理命令、可能继承 IDE 登录态,还可能访问浏览器 Cookie。便捷性越高,越不能把它当成普通代码补全工具。

人类确认要卡在“不可逆动作”之前

不少产品把人工确认设计成一个泛化弹窗:Agent 每调用一次工具就问用户是否继续。结果是确认疲劳,用户习惯性点击通过,安全机制反而失效。

确认应该针对高影响、不可逆、跨边界的动作。

操作
是否建议自动执行
原因
搜索公开文档
可以,在受控域名范围内
副作用低,数据敏感度通常较低
读取指定仓库普通源码
可以,按目录限制
仍需避免读取密钥和部署配置
创建本地补丁或草稿 PR
可以,但需可审查
变更可回滚,尚未进入主干
合并 PR
应人工确认
会影响共享代码基线
修改 CI、权限配置、Feature Flag
应人工确认
可能扩大后续权限或影响发布路径
访问生产敏感数据
应人工确认
涉及数据边界和合规风险
发布、回滚、删除资源
必须人工确认
影响范围大,通常不可静默执行

确认弹窗也不能只显示“是否允许调用工具”。用户需要看见足够的决策上下文:目标资源、操作类型、影响环境、将发送或修改的内容摘要,以及工具为何需要这项权限。

让 Agent 的行为可追溯,而不是只看最终回答

Agent 出问题后,仅靠聊天记录往往无法还原事故:模型读到了什么、选择了哪个工具、工具真实请求是什么、是否被规则拦截、返回结果是否包含异常指令,这些都需要审计事件。

建议把一次工具调用拆成可关联的事件链:

日志至少应覆盖:

• 任务标识与发起身份;
• Agent 申请了什么工具和权限;
• 策略引擎允许、拒绝或要求确认的原因;
• 工具调用的目标资源与参数摘要;
• 实际副作用,例如创建文件、提交变更或请求外部服务;
• 工具返回内容是否来自不可信数据源;
• 异常模式,例如短时间内连续访问无关资源、反复申请更高权限。

需要避免把“完整 Prompt、完整响应、完整 Token”无差别写入日志。审计系统本身也可能变成敏感数据汇集点。日志应保留足够的可追溯性,同时对密钥、用户数据和内部内容做脱敏与访问控制。

把不可信内容当数据,而不是指令

这是降低间接提示词注入成功率的关键工程策略。

当网页、Issue、邮件、文档进入上下文时,Agent 的运行框架应明确标识它们的来源和信任级别。不要把抓取到的文本直接拼到系统指令之后,也不要让工具返回的自由文本天然获得“命令优先级”。

可以采用结构化封装:

json

{

"source""external_issue",

"trust""untrusted",

"content""这里是 Issue 的原始内容",

"instructionPolicy""content_is_data_only"

}

模型仍可能受文本影响,因此结构化标记不是万能药。但它能帮助编排层和策略层识别风险来源,并为后续规则提供依据:

• 来自不可信网页的文本不能触发凭证读取;
• 来自邮件或 Issue 的操作建议不能直接升级权限;
• 外部内容请求调用高风险工具时,必须进入人工确认;
• 工具输出中出现要求忽略既有规则、要求导出敏感信息等模式时,进入拦截或降级流程。

防注入的核心不是证明模型永远不会被诱导,而是确保它即使被诱导,也拿不到足以造成严重后果的权限。

一个适合前端团队的落地顺序

不必一开始就建设复杂的安全平台。可以从最容易产生副作用的环节开始收敛。

1. 盘点现有 Agent 能力
2. 列出所有 Skill、MCP Server、浏览器自动化脚本和内部 API 工具。
3. 标记每个工具能读什么、能写什么、能访问哪里、是否能外发数据。
4. 切断万能权限
5. 停止让多个 Agent 共用高权限 Token。
6. 将读取、修改、发布拆成独立工具与独立授权。
7. 为代码执行和浏览器执行建立隔离环境
8. 不复用开发者本机身份。
9. 默认限制网络、目录挂载和密钥访问。
10. 定义必须确认的动作清单
11. 合并、发布、回滚、修改权限、读取生产敏感数据,默认不能静默执行。
12. 增加策略拦截与审计事件
13. 记录工具调用链。
14. 对权限升级、异常资源访问、外发请求建立告警和复盘机制。
15. 把 Skill 纳入代码审查范围
16. 审查提示词、工具描述和权限声明。
17. 将“规则文本变更”视为与代码变更同等重要的风险信号。

结语:能力设计决定了 Agent 的事故半径

前端 Agent 最容易落入一个误区:把 Skill 当作知识包,把 Tool Use 当作接口封装。实际上,前者会影响模型如何理解和规划,后者会决定模型能够对真实世界做什么。

一个可靠的 Agent 不依赖“模型永远听话”。它应该假设外部内容可能恶意、工具可能有缺陷、规则可能被绕过,并在这些前提下控制事故半径:

• 不可信输入不能直接驱动高危操作;
• 高危工具不能获得默认权限;
• 代码与浏览器执行必须处于隔离环境;
• 关键动作必须由人确认;
• 每一次跨边界调用都应可追溯、可撤销、可复盘。

当团队把这些边界设计清楚,Skill 和 Tool Use 才会从“不可控的能力扩张”,变成真正能进入研发流程的工程能力。