Skip to content

🔒 全面加固跨世界 GM RPC、用户脚本、Sandbox 与 Agent 边界 - #1747

Draft
cyfung1031 wants to merge 242 commits into
scriptscat:mainfrom
cyfung1031:codex/main-world-security-refactor
Draft

cyfung1031 wants to merge 242 commits into
scriptscat:mainfrom
cyfung1031:codex/main-world-security-refactor

Conversation

@cyfung1031

@cyfung1031 cyfung1031 commented Sep 16, 2026 •

Copy link
Copy Markdown
Collaborator

Checklist / 检查清单

  • Fixes mentioned issues / 修复已提及的问题
  • Code reviewed by human / 代码通过人工检查
  • Changes tested / 已完成测试

背景

本 PR 的说明仅以当前完整代码树为准:scriptscat:main 80854540dd1759e55ce79f541fcf582ba9c0a499 对比 PR head cc195781d048b7961e6af7d14029ce739998c73c。以下内容描述这两个最终状态之间的净差异,不引用 PR 中间 commits 曾经采用、随后修改或回退的方案。

相较 main,本 PR 主要加固 MAIN world、USER_SCRIPT、Service Worker、Offscreen/Sandbox 与 Agent/CAT API 的跨上下文身份、传输、数据和生命周期边界;同时补齐 early-start 同步状态、GM storage 持久化兼容语义,以及异步 GM/RPC 收尾行为。

变更总览

范围 HEAD vs main 的净变化 作用
MAIN world 通道 MAIN ↔ scripting 使用新增的 PageEventMessage,以每个 document 的随机 eventFlag 派生 performance CustomEvent 名称;pageLoad、value/event、external API 与 GM RPC 共用该 keyed bridge 避免把完整 MAIN traffic 放在任意页面脚本都能无条件订阅的全局 window.message 总线上;随机 key 只降低被动可观察性,不承担授权
页面 GM RPC 身份 新增 PageRpcRegistry 与 Service Worker execution binding;Page RPC v2 wire 使用 handle + sequence + api + params,canonical UUID/run flag/envTag/grant 由 Service Worker 根据 handle 与真实 sender 解析 页面不能用自报 UUID、run flag、envTag 或 grant 决定身份/权限;handle 只作为已签发 binding 的索引
Page RPC 重放 新增固定大小 RequestSequenceWindow;重复序列和窗口外旧序列拒绝,合法的大幅前向 gap 可按固定成本推进窗口 在防重放的同时避免无界 requestId 集合;broker-only sequence gap 也不会永久卡死长期 binding
USER_SCRIPT 传输 content 的普通 GM RPC、bootstrap、value update 与 event callback 使用浏览器原生 ExtensionMessage / user-script connection;同步 DOM 节点辅助仍单独使用 CustomEvent 路径;专用 user-script listener 不可用时可走 document token 绑定的 extension-port compatibility path 将普通特权 traffic 从 page-observable DOM bridge 分离,同时保留节点引用和浏览器兼容所需的窄通道
USER_SCRIPT 生命周期 新增 document/frame 定向的 connection/session、reconnect token、断线后的 pending value update 合并,以及 binding/session 撤销清理 防止旧文档 connection 残留、重连期间 value update 丢失和 callback 错投
Offscreen / Sandbox main 的 Offscreen/EventPage ↔ Sandbox WindowMessage 数据通道改为一次性 Window bootstrap + private MessagePort;parent 校验 exact iframe source、固定 marker 与 exactly one transferred port,随后移除 Window listener;port attachment 同时作为 readiness background/crontab userscript 即使监听 window.message,也不能被动看到后续 sandbox 内部 GM/lifecycle/value/event/skill payload
Sandbox same-realm hardening private-port transport 捕获 MessagePort 方法和原生 MessageEvent.data getter,wire envelope 继续做 data-property/shape 校验 避免 background userscript 后续篡改同 realm DOM prototype 后观察或干扰 trusted transport 的读取/发送
Wrapper provenance normal/early wrapper 使用统一 canonical wrapper source,closure 绑定 build token、创建时 Document、metadata,并记录 UUID/flag/scriptRevision;执行前用捕获的原生 Function.prototype.toString 和当前 Document 读取 metadata,再与 authoritative script revision 对齐 页面替换的函数、其他 Document 的 wrapper、旧 compiled revision 都不能直接取得当前 ScriptInfo/权限继续执行
Trusted runtime state trusted ScriptInfo / GM state 更新使用 own data-property 安装与安全刷新,不依赖继承 setter 或 Object.assign 的普通赋值语义 避免 Object.prototype setter、__proto__ 等页面可控原型行为影响内部权威状态更新
Early-start preload main 会把 early-start scriptInfo 放入 page-visible bootstrap event;HEAD 的 event detail 只保留 scriptFlag,同步 value/config/userConfig/resource snapshot 保留在 registered wrapper closure;execution binding 仍由 authoritative pageLoad 下发 保持 document-start 首行同步 GM 读取,同时减少 page-visible bootstrap payload,并把同步 snapshot 与 document-bound privilege 分离
Early-start reconcile authoritative pageLoad 与 bridge negotiation 并行;early context reconcile 校验 UUID/flag/scriptRevision/execution binding,并保留 bootstrap 前脚本自身已完成的 set/delete key 后到 pageLoad 不会把 early read-modify-write 回滚,也不会让 stale wrapper 继承当前文档的新权限
Early snapshot freshness 对需要同步 value-read 的 enabled early-start script,按 storageName 串行 DB commit、value delivery 与 registered snapshot refresh;shared storage 同批更新;value-only refresh 不推进 static code/resource revision,并有 dirty/repair 路径 下一次 document-start 的 wrapper snapshot 与已提交 storage 保持同 generation,避免并发 write/registration update 发生 generation 倒退
GM storage serialization 新增明确的 userscript storage normalization:array 保持数组形状;非 array object 按 own enumerable string properties 序列化;object 内 Function/Symbol/undefined 省略、array 对应项归一为 null;cycle 无效;top-level Function/Symbol/BigInt 继续使用 ScriptCat 的删除语义;getter/Proxy property read 在此 compatibility surface 可观察 将 GM storage 的兼容 serialization 与 privileged DTO 的 strict clone 分开,统一 immediate、RPC-settled 与 reload-persisted 结果
GM value wire / 字典形状 REncoded 增加 NaN、Infinity、-Infinity 显式 tag;all-values 路径返回 null-prototype dictionary,并安全保留 __proto__ 等 own key 非有限 number 可无损往返;值字典不受 prototype key 污染
Privileged DTO / message boundary Page RPC、ExtensionMessage、WindowMessage、CustomEvent、Server action、USER_SCRIPT 与 Sandbox port envelope 增加严格字段、descriptor、版本/来源与 clone 校验;Map/Set/普通对象条目在 privileged clone 前检查;关键原生方法提前捕获 hostile accessor、Proxy、Symbol、函数、不可 clone 值或被篡改内建不会直接进入 privileged service
GM facade / grant policy GM broker、transport、CAT conversation state 和受保护字段进一步收进 private/facade state;Page RPC effective grant、none grant、别名和依赖图统一使用同一 policy 页面脚本拿不到完整内部 broker 状态;不同 Page RPC consumer 不会各自解释 grant
GM XHR 生命周期 setup/connect/abort/error/timeout/load/loadend 统一 settle-once;用户 callback 抛错不会阻断内部收尾;连接建立前 abort、setup/connect failure 与缺失 broker loadend 都有明确完成路径 Promise/回调不会因错误路径悬挂,abort/error 后 connection 和引用可以收尾
GM async value write Promise-based GM.setValue / batch/delete 以 Service Worker RPC 与 freshness barrier 完成为准,不再依赖 valueUpdate broadcast 来 resolve;legacy 同步 API 仍立即返回 delivery channel 丢失不会让 Promise 永久等待;持久化完成和广播传播职责分开
Connected RPC Server connection 记录 caller disconnect;handler 在 caller 已断开后完成或 reject 时,不再向失效 connection 回包 消除 late response 对关闭端口的无效发送;业务 handler cancellation 仍由各服务自行决定
Agent/CAT canonical identity userscript Agent GM handlers 以 request.script.uuid 覆盖/注入 script identity;conversation/task/attachment/OPFS/DOM monitor 的访问检查使用该 canonical identity 调用脚本不能靠 request payload 中自报 scriptUuid 访问另一脚本资源
Agent ownership / concurrency script conversation 写入 ownerScriptUuid 并按 owner 检查;task CRUD/run/history 按 owner scope;task 使用 generation/revision;attachment read 需在调用脚本拥有的持久化 conversation 中可达 防止跨脚本读取/修改持久化 Agent 资源,并减少 stale update 覆盖
DOM/CDP monitor monitor session 记录 owner;peek/stop/start 检查 owner;同一 tab 的 monitor transition 通过串行队列执行 防止跨脚本读取/替换 monitor,以及并发 attach/detach 留下 debugger/listener
运行时内建与热路径 编译执行改用捕获的 call primitive;关键 Map/Set/Reflect/descriptor 操作使用捕获原生;null-prototype record 在安全前提下直接赋值;共享 message envelope / WindowMessageConnect 减少重复实现 降低页面原型篡改影响,并减少不必要的 bind/descriptor/adapter 开销
回归测试 新增/扩展 MAIN keyed bridge、Sandbox private port、Page RPC、wrapper revision、USER_SCRIPT reconnect、early snapshot、GM storage persistence、GM XHR、RPC disconnect、Agent owner/OPFS/DOM monitor 等 unit/E2E 把上述 HEAD vs main 的边界和兼容语义固定为可重复验证

PR 范围

项目 当前值
比较基准 scriptscat:main @ 80854540dd1759e55ce79f541fcf582ba9c0a499
PR Head codex/main-world-security-refactor @ cc195781d048b7961e6af7d14029ce739998c73c
Ahead / behind 239 / 0 commits
Changed files 143
Diff +16,266 / -2,000

实现考虑

设计约束 当前 HEAD 的 invariant
句柄不是秘密 execution handle 是 Service Worker 签发 binding 的索引;最终授权仍由真实 sender 的 tab/frame/document、脚本状态、grant 与 permission verification 决定
随机 event key 不是认证 MAIN keyed event 只减少普通页面的被动全量可观察性;一旦页面获知 event key,该输入面仍是 page-visible,因此所有 privileged consumer 仍需独立校验
MessagePort 是传输隔离能力 Sandbox private port 将内部 payload 移出共享 Window bus,但不是最终 GM authorization;脚本 context/grant/SW 校验继续成立
Wrapper artifact / instance 分离 UUID/flag/scriptRevision 表示 compiled artifact;closure captured Document 与 SW execution binding 表示具体运行实例/transport identity,三者不互相替代
GM storage 与 privileged DTO 分层 storage compatibility 可以观察调用脚本自己的 enumerable getter/Proxy trap;Page RPC、bridge DTO 与内部状态仍使用 descriptor-first strict validation/clone
Snapshot 是派生状态 early registered snapshot 用于同步 API compatibility,不是授权 secret;storage commit 成功后,registration maintenance failure 只进入 dirty/repair,不反向撤销 DB commit
Async completion 与传播分离 Promise GM write 等待 RPC/freshness barrier;valueUpdate channel 负责传播。RPC 完成不保证所有观察 context 已在同一时刻收到广播
BFCache / navigation same-document/BFCache 可继续使用当前 document 的有效 context;新 document 必须重新取得匹配的 binding/session,导航、脚本撤销和 tab 关闭会清理旧状态
Agent owner scope userscript service 入口以 canonical request.script.uuid 决定 owner;UI/extension-internal 调用与 script-scoped 调用继续走各自现有权限模型
DOM monitor 生命周期 同一 tab 的 start/stop 串行;owner 不匹配的 script caller 不能 peek/stop/replace 现有 monitor

已知限制

  • PR 仍为 Draft,尚未有人类审查;对应 checklist 保持未勾选。
  • MAIN PageEventMessage 仍是 page-visible transport。随机 eventFlag 不是 bearer secret,也不能替代 broker/Service Worker authorization。
  • PageEventMessage 没有通用 ready buffer;当前入口在 eventFlag negotiation 前预取 authoritative pageLoad,但只在 bridge 建立后消费并发送页面 payload。未来新增更早发送的 caller 时仍需单独证明 ordering。
  • Sandbox channel 采用 fail-closed readiness:若 iframe 没有 transfer 合法 MessagePort,parent 不会报告 verified readiness,也不会 replay background/scheduled script;当前没有 timeout-ready 路径。
  • early-start registered snapshot 是派生状态;极端 chrome.userScripts.update/registration failure 下,storage 已成功但下一次 wrapper snapshot 可暂时 dirty,后续 repair/registration mutation 会再尝试恢复。
  • GM storage 是 serialization compatibility surface,不保证 graph identity、prototype、cycle 或大多数 special-object internal slot;ScriptCat top-level unsupported value 继续使用既有 delete 语义,和其他 manager 的瞬时 own-undefined 表现可不同。
  • Connected RPC 的 disconnect 只禁止 late response,不自动取消已经进入业务 handler 的工作;需要真正 cancellation 的服务仍需自己的 AbortSignal/取消协议。
  • Firefox 未完成本轮手动浏览器验证;当前新增浏览器 E2E 主要覆盖 Chromium 路径。

建议审查重点

审查面 重点
MAIN / Page RPC keyed PageEventMessage 上所有 privileged request 是否都经过 shape/clone、handle、grant、sequence 与真实 sender 校验
USER_SCRIPT native connection、bootstrap/reconnect token、pending value delivery 与 DOM-only CustomEvent path 是否严格分离;新 document 是否会清除旧 session/binding
Sandbox Window 只是否用于 one-shot port transfer;exact source/marker/port count、listener removal 与 same-realm prototype hardening 是否完整
Wrapper / early-start canonical source、build token、Document identity、UUID/flag/scriptRevision、authoritative reconcile 和 bootstrap 前本地写入是否组成一致生命周期
GM storage compatibility serialization 与 privileged strict clone 是否保持两个边界;unsupported value、special object、non-finite number 与 prototype key 是否符合测试固定的语义
生命周期 GM XHR、connected RPC、USER_SCRIPT reconnect、snapshot refresh、Sandbox readiness 与 CDP monitor 在 error/abort/disconnect/retry 下是否都能收尾
Agent/CAT userscript request 中自报 identity 是否都被 canonical caller identity 覆盖;conversation/task/attachment/OPFS/monitor 的 owner scope 是否没有漏口
性能 / 可维护性 captured-native 与 null-prototype fast path 是否只用于已证明安全的内部对象;shared message primitives 是否没有扩大 trust boundary
兼容性 document-start 首行同步 GM state、BFCache/navigation、USER_SCRIPT fallback、GM storage immediate/settled/reload 三阶段,以及 Chrome/Firefox 差异

验证

检查 结果
最终 compare scriptscat:main 80854540dd1759e55ce79f541fcf582ba9c0a499 → PR head cc195781d048b7961e6af7d14029ce739998c73c;ahead 239 / behind 0;143 files;+16,266 / -2,000
GitHub Actions 当前 head 的 run 36573773382 为 success;Lint、2 个 unit test shard、4 个 E2E shard 与聚合测试全部 success
MAIN bridge e2e/main-world-keyed-bridge.spec.ts、page_event_message.test.ts、page_rpc.test.ts 覆盖 keyed bridge、message envelope 与 handle/grant/sequence policy
Sandbox e2e/sandbox-message-port.spec.ts、sandbox_message_channel.test.ts、message_port_message.ts 相关测试覆盖 source/marker/one-port bootstrap、listener removal、private traffic 与 prototype poisoning
USER_SCRIPT connection/runtime 单元测试覆盖 native bootstrap/reconnect、document/frame 约束、pending value update 与 callback delivery
Wrapper / early-start executor/runtime/utils 测试覆盖 canonical wrapper metadata、Document/revision mismatch、首行同步 snapshot、authoritative reconcile、bootstrap 前 write/delete preservation 与 snapshot refresh/repair
GM storage example/tests/gm_storage_test.js 与 e2e/gm-api.spec.ts 覆盖 clone/normalization、unsupported value、special object、non-finite number,以及 immediate / RPC-settled / reload-persisted 三阶段
GM XHR / RPC GM XHR unit/E2E 覆盖 setup/connect failure、abort、error/timeout/loadend settle;Server regression 覆盖 caller disconnect 后不发送 late response
Agent / DOM conversation/task/OPFS/DOM monitor 单元测试覆盖 canonical owner、attachment reachability、task scope/CAS 与同 tab monitor serialization

@cyfung1031
cyfung1031 marked this pull request as draft September 16, 2026 23:03
@cyfung1031 cyfung1031 changed the title 🔒 全面加固跨世界 GM RPC、用户脚本与 Agent 边界 🔒 全面加固跨世界 GM RPC、用户脚本、Sandbox 与 Agent 边界 Sep 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P1 🔥 重要但是不紧急的内容

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants