Skip to content

🔒 加固 MAIN 世界脚本 GM RPC 权限边界 - #1747

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

cyfung1031 wants to merge 71 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 / 已完成测试

背景

MAIN world 脚本与隔离环境共用 GM RPC 通道时,页面可以伪造脚本 UUID、run flag、API 名称或参数,service worker 也缺少与具体页面文档绑定的能力证明。部分内部 Map、绑定函数和监听器状态还会把可变的页面对象作为接收方或跨脚本全局状态保存。

本次改动

  • 为每个脚本、环境、标签页、frame 和 document 签发并校验执行绑定句柄与 canonical run flag;脚本启用、安装、删除、注销和标签页关闭时撤销绑定。
  • 在 scripting broker 增加严格的 page GM RPC DTO、grant/依赖图 allowlist、自有数据字段校验和 structured-clone 参数边界,再转发到 service worker;请求 ID 使用有界重放窗口。
  • 让早期启动脚本在收到 authoritative binding 后同步 live GM context,保证异步 GM.setValue 等待能收到正确确认,并修正 inject/content 两个 pageLoad 调用的绑定轮换竞态。
  • 将关键能力函数改为捕获的 Reflect.apply wrapper,并把页面可变接收方相关的对话、任务监听器、通知和值确认状态改为实例或上下文关联存储;XHR 在所有终止路径完成 resolve、loadend 和清理。
  • USER_SCRIPT content bootstrap、GM RPC、值更新和事件回调迁移到原生 extension 消息;bootstrap 使用 service worker 按文档签发的一次性 token 绑定脚本句柄,DOM CustomEventMessage 仅保留同步节点引用及 bootstrap 触发。
  • 修正 USER_SCRIPT 执行器的 GM RPC 前缀,使原生 content 脚本的异步值 API 直接到达 service worker,而 isolated page 脚本继续使用 scripting broker。
  • 对绑定、页面 RPC、早期启动确认、URL 精确匹配、跨环境状态、绑定轮换竞态和 XHR 生命周期补充回归测试,并同步架构文档。

实现考虑

请求中的句柄是范围标识,不是秘密;service worker 仍以真实消息发送方的 tab/frame/document 身份、脚本存在状态和既有 permission verification 作为授权依据。BFCache 恢复不会重新执行 content script,因此旧 document binding 会保留到标签页关闭或全局/脚本撤销;解析时仍严格拒绝其他 document 使用该句柄。

USER_SCRIPT 与 isolated scripting bundle 分别请求 ctit 脚本列表,避免并发启动时一方撤销另一方刚签发的 binding。USER_SCRIPT handshake 不接受页面提供的句柄列表;service worker 只接受与当前 sender 文档匹配的一次性 bootstrap token,并从已保存的脚本清单派生句柄集合。

已知限制

MAIN inject 路径仍保留现有 page-observable DOM CustomEvent 传输和页面可观察的 GM 返回值、句柄及能力结果;完整的 MAIN 原生消息迁移、USER_SCRIPT 早期预加载 companion 和更细的跨 realm facade 不在本次范围内。绑定表的旧 document 项会在 BFCache 语义要求下保留,service worker 重启、脚本撤销或标签页关闭时清理。Firefox 未在本次手动浏览器会话中验证。

建议审查重点

  • 伪造/重放 page RPC 的 UUID、句柄、环境标签、run flag、grant API、grant 依赖、accessor 和不可 structured-clone 参数是否均在 broker 或 service worker 边界拒绝。
  • USER_SCRIPT 原生端口与 DOM 节点辅助通道是否保持边界,以及值更新和事件回调是否只到达匹配 document。
  • 早期启动脚本在 authoritative binding 到达前后的 GM.setValueconnect 和页面值广播时序。
  • BFCache 的 pageShow 路径是否继续复用原 document 绑定,新 document 的 sender 校验是否隔离旧句柄。

验证

  • pnpm exec vitest run ... — 15 个安全路径 suite、352 tests passed(commit 9f98995e)。
  • pnpm run typecheck — passed(commit 9f98995e)。
  • pnpm run lint — Prettier, TypeScript, i18n, issue-template checks and ESLint passed(commit 9f98995e)。
  • pnpm run build — production build passed; existing Rspack asset-size and Monaco dynamic-require warnings remain(commit 9f98995e)。
  • Commit hook — typecheck, Prettier and issue-template checks passed.
  • pnpm test (commit d65bcf3e) — 373 files / 4,790 tests passed, with the existing tests/verification-tools.test.mjs console-collector case timing out in the sandbox and two unhandled listen EPERM 127.0.0.1 errors; no changed security-path test failed.
  • GitHub Actions test run 35205972023 on 25714ff6 — lint, both Vitest shards, all four E2E shards, and the aggregate test job passed. test run 35238597251 on 9f98995e — lint, both Vitest shards, all four E2E shards, and the aggregate test job passed.
  • pnpm exec playwright test e2e/gm-api.spec.ts -g 'Content inject tests|early-start content world' --workers=1 --retries=0 — 2 passed after rebuilding the extension; both suites completed their asynchronous GM value cases and emitted valid sctest/v1 summaries.
  • Manual Chrome smoke on the built extension observed both content and MAIN script markers, prior=seen after reload, and the DOM helper marker; after rebuilding/restarting, no new service-worker Document is not defined crash appeared. The browser XHR smoke did not produce a callback and is not claimed as browser-verified.
  • Static security re-review of the final tree found no remaining blocker; this is not a human approval.

@cyfung1031
cyfung1031 marked this pull request as draft September 16, 2026 23:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant