Repository navigation
Conversation
chrome.userScripts 与 Firefox(134+)对 matches 和 includeGlobs 取并集:网址命中任一即注入。 getApiMatchesAndGlobs 原先按「matches 之后再用 includeGlobs 过滤」设计,遇到 @include glob 或 正则就把 matches 放宽成 *://*/* 再指望 includeGlobs 收回,结果所有带 @include 的脚本代码都挂到了 每个 http(s) 页面(执行仍由 service worker 的匹配把关,但源码对页面可见)。 - matches 只放脚本真正的 match pattern;只有 glob 时用 *://scriptcat.invalid/* 占位(API 要求 matches 非空) - glob 不以 * 结尾时补一条 #* 变体:浏览器拿带 hash 的完整网址匹配 glob,脚本猫匹配前会去掉 hash - 能抽出 scheme://网域 的正则转成小写的网域级 glob;抽不出的仍全站注册,并以 regexUnbounded 标记 - 修改 CompiledResourceNamespace,让已保存的旧注册信息失效重建
能抽出 scheme://网域 的正则已转成网域 glob 精确注册;抽不出的(如没有 scheme 的局部正则)只能全站注册, 脚本代码仍会挂到所有页面。这类脚本改为不预注册:service worker 在 pageLoad 匹配命中后,把挂载 window[flag] 的注入代码放进 TScriptInfo.code 下发,scripting 转交给 content 环境,content 沿用旧版 createElementNS 插入 <script> 的写法注入页面,执行环境照常通过 flag 接手。 content 运行在 USER_SCRIPT world,其 CSP 放行内联脚本,插入页面的 <script> 不受页面 CSP 与 Trusted Types 限制(github.com、youtube.com 实测);scripting 所在的 ISOLATED world 做不到,所以不能由它注入。 仍全站预注册的例外:early-start 需在页面脚本前同步挂载、unwrap 注入即执行,都等不了 pageLoad; Firefox 的 USER_SCRIPT world 插入的 <script> 受页面 CSP 限制。 新增 e2e/include-injection-scope.spec.ts:glob(含带 #hash 的网址)与收不窄的正则脚本在各自目标页执行 (正则目标页下发 script-src 'none'),且不在无关页面留下 window[flag]。该用例在 origin/main 上失败。
c2ef01a to
91f93aa
Compare
|
SC机制不一样 SC用 API 先在浏览器层面注册 还是会在 DevTools 看到的 |
auto remove flag 也可以,不过感觉这样也行,原来就是这种模式 而且现在这样会退化成 :///* ,感觉有点脏 |
最初搞 urlmatcher 有提出把 regex 那些转换成glob 规则,这样就可以提取网域之类 scriptcat/src/pkg/utils/url_matcher.ts Lines 475 to 507 in 6c0459b |
不太记得了😰,但是那样转换,也总有兜不住的规则吧 |
我上面講法有點錯。 scriptcat/src/pkg/utils/url_matcher.ts Lines 475 to 507 in 6c0459b |
|
好像是说过😄,做成现在这种模式,回退交给pageLoad去注入,也是MV2时候的做法 |
|
除了
改进 url_matcher.ts 把那些常见写法一一列出做仔细点应该能改善,但不保证。 |
「回退交给pageLoad去注入」这之前浏览器API就会执行,DevTools就会有显示脚本 |
|
当时好像是因为
不会啊,没有匹配到的脚本,就不会注入,pageLoad匹配url,匹配了才下发脚本信息,交给content/inject注入了,就不用 chrome.userScripts 去匹配注入了 |
这样的话安装了 300个脚本,就要跑300个配对测试了 为了不要让 DevTools 看起来一大堆无关代码就弃掉 API 注入,我觉得不好 我个人认为是 nofix 不用改 |
本来就要匹配脚本,代码也本来就有缓存,不过消息传递确实是又加重了,要把代码发给content 也不是弃掉 API 注入,是那些解析不了的退化成这种模式交给content注入,量应该很小 再思考一下 |
Checklist / 检查清单
背景
#1803 中,在不匹配的页面(如起点)的 DevTools 里能看到只该在抖音/B站运行的脚本源码,
window['#-<uuid>']上挂着完整代码。脚本没有执行(执行由 service worker 的匹配把关),但任何网站都能枚举出用户装了哪些脚本并读取源码,每个页面也要多解析这些代码。根因:
chrome.userScripts(Firefox 134+ 同样)对matches与includeGlobs取并集,网址命中任一即注入。getApiMatchesAndGlobs原先按「先 matches 再用 includeGlobs 过滤」设计,遇到@includeglob 或正则就把matches放宽成*://*/*,再指望includeGlobs收回范围,结果所有带@include的脚本都被注入到了每个 http(s) 页面。在 Chrome 148 上实测:
matches: ["https://nomatch.invalid/*"]加includeGlobs: ["*example*"]会在 example.com 注入;matches: ["*://*/*"]加includeGlobs: ["*douyin*"]同样会在 example.com 注入;excludeMatches/excludeGlobs正常生效;matches不能为空。本次改动
1. 按并集语义注册(
01d121d7)matches只放脚本真正的 match pattern。只有 glob 时用*://scriptcat.invalid/*占位(.invalid是保留域名,API 要求matches非空),注入范围完全由includeGlobs决定。scheme://网域的正则@include转成小写的网域级 glob。正则默认忽略大小写,glob 区分大小写。*的 glob 补一条<glob>#*:实测 Chrome 拿带#hash的完整网址匹配 glob,而 SW 匹配前会先去掉 hash。CompiledResourceNamespace,旧的注册信息在升级后失效并重建。addMatchesToGlobs、extractMatchPatternsFromGlobs。2. 正则收不窄的脚本改由 pageLoad 下发代码(
c2ef01a9)/foo\.com/),getUserScriptRegister标记deliverOnPageLoad,这类脚本不预注册。window[flag]的注入代码放进TScriptInfo.code。scripting把它转交给 content,content 沿用旧版createElementNS插入<script>的写法注入页面,inject 照常通过 flag 接手。scripting:content 运行在 USER_SCRIPT world,其configureWorldCSP 放行内联脚本,插入页面的<script>不受页面 CSP 和 Trusted Types 限制(在 github.com、youtube.com 实测)。scripting所在的 ISOLATED world 插入的脚本会被拦。@early-start(需在页面脚本之前同步挂载)、@unwrap(注入即执行),以及 Firefox(其 USER_SCRIPT world 插入的<script>受页面 CSP 限制)。实现考虑
chrome.userScripts.execute:它需要 Chrome 135+,而 content 注入在 Chrome 120+ 都能用,也不需要剥页面 CSP。已知限制
@early-start/@unwrap脚本,以及 Firefox 上的这类脚本,代码仍会挂到所有页面(维持原状)。?结尾的网址(如/page?)不会注入:SW 视其为匹配,Chrome 不视为匹配,属罕见情况。src/app/service/agent/service_worker/dom.test.ts改写chrome.permissions后未还原,在isolate: false下导致后续文件的 setup 失败;e2e/drive.mjs的sw命令在页面注册了自己的 Service Worker 时会拿错 worker。建议审查重点
src/pkg/utils/url_matcher.ts的getApiMatchesAndGlobs:确认注册范围始终不比 SW 匹配窄,尤其是网域 glob 转换和#*变体。src/app/service/service_worker/runtime.ts:deliverOnPageLoad的脚本在批量注册、单脚本变更(需撤销旧注册)和 pageLoad 三条路径上的处理是否一致。url_matcher.test.ts大部分期望值按新契约重写(旧期望写死了 AND 语义下的*://*/*),可对照新旧 diff 确认没有遗漏原有分支。关联
Close #1803
验证
以下结果绑定 base
e88d0eb6(含 #1815 的 Playwright 1.64.1)、head91f93aa9。本 PR 先基于eecd34a2开出,#1815 合并后 rebase,无冲突。91f93aa9):Lint、单元测试两个分片、Run tests、Runtime smoke、E2E 四个分片全部通过。rebase 前 Runtime smoke 的失败,是因为 ubuntu 26.04 runner 不支持 Playwright 1.60,由 ⬆️ 升级 @playwright/test 到 1.64.1,修复 ubuntu 26.04 runner 上的 CI 失败 #1815 修复。@include的脚本在 example.com 上挂着window['#-<uuid>'];只有纯@match的对照脚本没有。e2e/include-injection-scope.spec.ts:在origin/main(eecd34a2)的构建上 1 failed / 1 passed,无关页面泄漏了 2 个#-uuid;在本分支上 2 passed。pnpm exec playwright test e2e/runtime-bootstrap.spec.ts e2e/popup-matching-regressions.spec.ts e2e/gm-api.spec.ts e2e/include-injection-scope.spec.ts:18 passed,其中 gm-api 页内自测 138 项通过、0 失败(含严格 CSP 页面)。pnpm exec vitest run --no-coverage src/pkg/utils src/app/service/service_worker src/app/repo src/app/service/content:79 个文件、1805 条全部通过。pnpm run typecheck通过。pnpm test不可靠,origin/main原样运行也有 79–140 个文件失败,且每次数量不同。原因是高负载下超时,以及dom.test.ts的 mock 泄漏。全量结果以上面 CI 的两个分片为准。git grep核对。git diff e88d0eb6...91f93aa9共 12 个文件,均属于两项改动、对应测试和执行路径文档。