From 676c3d4a59967f9fb7e7122c1543cf64768c41c2 Mon Sep 17 00:00:00 2001 From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> Date: Thu, 30 Apr 2026 19:31:44 +0900 Subject: [PATCH 1/9] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20GM=20Execution=20Trust?= =?UTF-8?q?=20Token?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/repo/scripts.ts | 1 + src/runtime/background/gm_api.ts | 65 +++++++++++++++++++++++++------ src/runtime/background/runtime.ts | 14 ++++++- src/runtime/content/gm_api.ts | 3 ++ src/runtime/gm_api.test.ts | 43 ++++++++++++++++++++ 5 files changed, 112 insertions(+), 14 deletions(-) diff --git a/src/app/repo/scripts.ts b/src/app/repo/scripts.ts index 57dcf811a..7a745b0d1 100644 --- a/src/app/repo/scripts.ts +++ b/src/app/repo/scripts.ts @@ -84,6 +84,7 @@ export interface ScriptRunResource extends Script { grantMap: { [key: string]: string }; value: { [key: string]: Value }; flag: string; + executionToken?: string; resource: { [key: string]: Resource }; sourceCode: string; } diff --git a/src/runtime/background/gm_api.ts b/src/runtime/background/gm_api.ts index 25d5e7dc1..0a75cd485 100644 --- a/src/runtime/background/gm_api.ts +++ b/src/runtime/background/gm_api.ts @@ -39,6 +39,7 @@ export type MessageRequest = { scriptId: number; // 脚本id api: string; runFlag: string; + executionToken?: string; params: any[]; }; @@ -50,6 +51,28 @@ export type Request = MessageRequest & { export type Api = (request: Request, connect?: Channel) => Promise; export default class GMApi { + static executionMap = new Map< + string, + { scriptIds: Set; tabId: number } + >(); + + static registerScriptExecution(scriptIds: number[], tabId: number) { + const token = uuidv4(); + this.executionMap.set(token, { + scriptIds: new Set(scriptIds), + tabId, + }); + return token; + } + + static removeTabExecutions(tabId: number) { + this.executionMap.forEach((execution, token) => { + if (execution.tabId === tabId) { + this.executionMap.delete(token); + } + }); + } + message: MessageHander; script: ScriptDAO; @@ -80,18 +103,18 @@ export default class GMApi { this.message.setHandler( "gmApi", async (_action: string, data: MessageRequest, sender: MessageSender) => { - const api = PermissionVerify.apis.get(data.api); - if (!api) { - return Promise.reject(new Error("api is not found")); - } - const req = await this.parseRequest(data, sender); try { + const api = PermissionVerify.apis.get(data.api); + if (!api) { + return Promise.reject(new Error("api is not found")); + } + const req = await this.parseRequest(data, sender); await this.permissionVerify.verify(req, api); + return api.api.call(this, req); } catch (e) { this.logger.error("verify error", { api: data.api }, Logger.E(e)); return Promise.reject(e); } - return api.api.call(this, req); } ); this.message.setHandlerWithChannel( @@ -102,18 +125,18 @@ export default class GMApi { data: MessageRequest, sender: MessageSender ) => { - const api = PermissionVerify.apis.get(data.api); - if (!api) { - return connect.throw("api is not found"); - } - const req = await this.parseRequest(data, sender); try { + const api = PermissionVerify.apis.get(data.api); + if (!api) { + return connect.throw("api is not found"); + } + const req = await this.parseRequest(data, sender); await this.permissionVerify.verify(req, api); + return api.api.call(this, req, connect); } catch (e: any) { this.logger.error("verify error", { api: data.api }, Logger.E(e)); return connect.throw(e.message); } - return api.api.call(this, req, connect); } ); // 只有background页才监听web请求 @@ -150,9 +173,27 @@ export default class GMApi { const req: Request = data; req.script = script; req.sender = sender; + this.verifyExecution(req); return Promise.resolve(req); } + verifyExecution(request: Request) { + // 只适用于有 pageLoad 的 前台脚本(content), 不适用于没 pageLoad 的 后台脚本 (sandbox) + if (request.sender.targetTag === "sandbox") { + return; + } + if (request.sender.targetTag !== "content") { + throw new Error("script execution must be from content or sandbox"); + } + if (!request.executionToken) { + throw new Error("script execution is not trusted"); + } + const execution = GMApi.executionMap.get(request.executionToken); + if (!execution || !execution.scriptIds.has(request.scriptId)) { + throw new Error("script execution is not trusted"); + } + } + @PermissionVerify.API() GM_setValue(request: Request): Promise { if (!request.params || request.params.length !== 2) { diff --git a/src/runtime/background/runtime.ts b/src/runtime/background/runtime.ts index 4d22d52fa..3a9310b73 100644 --- a/src/runtime/background/runtime.ts +++ b/src/runtime/background/runtime.ts @@ -285,6 +285,7 @@ export default class Runtime extends Manager { }; chrome.tabs.onRemoved.addListener((tabId) => { runScript.delete(tabId); + GMApi.removeTabExecutions(tabId); }); // 给popup页面获取运行脚本,与菜单 this.message.setHandler( @@ -431,10 +432,19 @@ export default class Runtime extends Manager { return; } - resolve({ scripts: filter }); + const executionToken = GMApi.registerScriptExecution( + filter.map((script) => script.id), + sender.tabId! + ); + const runResources = filter.map((script) => ({ + ...script, + executionToken, + })); + + resolve({ scripts: runResources }); // 注入脚本 - filter.forEach((script) => { + runResources.forEach((script) => { let runAt = "document_idle"; if (script.metadata["run-at"]) { [runAt] = script.metadata["run-at"]; diff --git a/src/runtime/content/gm_api.ts b/src/runtime/content/gm_api.ts index 15ea65a1a..4fb002935 100644 --- a/src/runtime/content/gm_api.ts +++ b/src/runtime/content/gm_api.ts @@ -77,6 +77,7 @@ export class GM_Base { scriptId: this.scriptRes.id, params, runFlag: this.runFlag, + executionToken: this.scriptRes.executionToken, }); } @@ -90,6 +91,7 @@ export class GM_Base { scriptId: this.scriptRes.id, params, runFlag: this.runFlag, + executionToken: this.scriptRes.executionToken, }); return channel; } @@ -121,6 +123,7 @@ export class GM_Base { }); } } + } export default class GMApi extends GM_Base { diff --git a/src/runtime/gm_api.test.ts b/src/runtime/gm_api.test.ts index a3f420937..495a6002a 100644 --- a/src/runtime/gm_api.test.ts +++ b/src/runtime/gm_api.test.ts @@ -91,6 +91,49 @@ beforeAll(async () => { }); }); +describe("GM execution trust", () => { + it("rejects content messages without a registered execution token", async () => { + await expect( + backgroundApi.parseRequest( + { + api: "GM_setValue", + scriptId: scriptRes.id, + params: ["test", "test"], + runFlag: "test", + }, + { + targetTag: "content", + tabId: 1, + url: window.location.href, + } + ) + ).rejects.toThrow("script execution is not trusted"); + }); + + it("accepts content messages with a matching execution token", async () => { + const executionToken = GMApi.registerScriptExecution([scriptRes.id], 1); + await expect( + backgroundApi.parseRequest( + { + api: "GM_setValue", + scriptId: scriptRes.id, + params: ["test", "test"], + runFlag: "test", + executionToken, + }, + { + targetTag: "content", + tabId: 1, + url: window.location.href, + } + ) + ).resolves.toMatchObject({ + scriptId: scriptRes.id, + executionToken, + }); + }); +}); + describe("GM value", () => { it("get value", () => { contentApi.GM_setValue("test", "test"); From 94102c94563fda1e00fd07d091d72ce632a62302 Mon Sep 17 00:00:00 2001 From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> Date: Thu, 30 Apr 2026 19:44:55 +0900 Subject: [PATCH 2/9] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/message/message.test.ts | 2 +- src/app/message/message.ts | 2 +- src/runtime/background/gm_api.ts | 12 ++++++++---- src/runtime/gm_api.test.ts | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/app/message/message.test.ts b/src/app/message/message.test.ts index d78b873d8..a80e9ab94 100644 --- a/src/app/message/message.test.ts +++ b/src/app/message/message.test.ts @@ -21,7 +21,7 @@ global.sandbox = {}; const center = new MessageCenter(); center.start(); -const content = new MessageInternal("background"); +const content = new MessageInternal("testing"); describe("message center", () => { it("set handler", async () => { diff --git a/src/app/message/message.ts b/src/app/message/message.ts index 15fbfe02b..59728eb8f 100644 --- a/src/app/message/message.ts +++ b/src/app/message/message.ts @@ -26,7 +26,7 @@ export type HandlerWithChannel = ( ) => void; export type TargetTag = - | "background" + | "testing" | "content" | "sandbox" | "popup" diff --git a/src/runtime/background/gm_api.ts b/src/runtime/background/gm_api.ts index 0a75cd485..47d79fe20 100644 --- a/src/runtime/background/gm_api.ts +++ b/src/runtime/background/gm_api.ts @@ -179,11 +179,15 @@ export default class GMApi { verifyExecution(request: Request) { // 只适用于有 pageLoad 的 前台脚本(content), 不适用于没 pageLoad 的 后台脚本 (sandbox) - if (request.sender.targetTag === "sandbox") { + if (process.env.VI_TESTING === "true" && request.sender.targetTag === "testing") { return; - } - if (request.sender.targetTag !== "content") { - throw new Error("script execution must be from content or sandbox"); + } else { + if (request.sender.targetTag === "sandbox") { + return; + } + if (request.sender.targetTag !== "content") { + throw new Error("script execution must be from content or sandbox"); + } } if (!request.executionToken) { throw new Error("script execution is not trusted"); diff --git a/src/runtime/gm_api.test.ts b/src/runtime/gm_api.test.ts index 495a6002a..f200884fa 100644 --- a/src/runtime/gm_api.test.ts +++ b/src/runtime/gm_api.test.ts @@ -31,7 +31,7 @@ IoC.registerInstance(ValueManager, new ValueManager(center, center)); const backgroundApi = new GMApi(center, new PermissionVerify()); backgroundApi.start(); -const internal = new MessageInternal("background"); +const internal = new MessageInternal("testing"); const scriptRes = { id: 0, name: "test", From 04423f97a5f7f7fb0275b3b75d8dc573986302de Mon Sep 17 00:00:00 2001 From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> Date: Thu, 30 Apr 2026 19:45:40 +0900 Subject: [PATCH 3/9] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=86=99=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/runtime/background/gm_api.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/runtime/background/gm_api.ts b/src/runtime/background/gm_api.ts index 47d79fe20..78c29ee4a 100644 --- a/src/runtime/background/gm_api.ts +++ b/src/runtime/background/gm_api.ts @@ -181,13 +181,12 @@ export default class GMApi { // 只适用于有 pageLoad 的 前台脚本(content), 不适用于没 pageLoad 的 后台脚本 (sandbox) if (process.env.VI_TESTING === "true" && request.sender.targetTag === "testing") { return; - } else { - if (request.sender.targetTag === "sandbox") { - return; - } - if (request.sender.targetTag !== "content") { - throw new Error("script execution must be from content or sandbox"); - } + } + if (request.sender.targetTag === "sandbox") { + return; + } + if (request.sender.targetTag !== "content") { + throw new Error("script execution must be from content or sandbox"); } if (!request.executionToken) { throw new Error("script execution is not trusted"); From 98ef11cda81b39f8533efbc61bce951a7d4abe67 Mon Sep 17 00:00:00 2001 From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> Date: Thu, 30 Apr 2026 19:59:39 +0900 Subject: [PATCH 4/9] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20webpack=20=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- webpack.config.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/webpack.config.ts b/webpack.config.ts index 4befd41ba..5ad2ada22 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -1,6 +1,6 @@ /* eslint-disable import/no-extraneous-dependencies */ import path from "path"; -import { Configuration } from "webpack"; +import { Configuration, DefinePlugin } from "webpack"; import TerserPlugin from "terser-webpack-plugin"; import HtmlWebpackPlugin from "html-webpack-plugin"; import ESLintPlugin from "eslint-webpack-plugin"; @@ -131,6 +131,9 @@ const configCommon: Configuration = { }), new CleanWebpackPlugin(), new ProgressBarPlugin({}), + new DefinePlugin({ + "process.env.VI_TESTING": JSON.stringify(false), + }), new MonacoLocalesPlugin({ languages: ["en", "zh-cn"], defaultLanguage: "en", From 8c17c8c6fa6cf1b01db38f426666a4ee4a4f419f Mon Sep 17 00:00:00 2001 From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> Date: Thu, 30 Apr 2026 20:01:14 +0900 Subject: [PATCH 5/9] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E5=A4=9A=E4=BD=99?= =?UTF-8?q?=E5=86=99=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/message/common.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/message/common.ts b/src/app/message/common.ts index 5a4416c8d..527331e35 100644 --- a/src/app/message/common.ts +++ b/src/app/message/common.ts @@ -2,7 +2,7 @@ export const MouseEventClone = MouseEvent; export const CustomEventClone = CustomEvent; -const performanceClone = (typeof process !== "undefined" && process.env.VI_TESTING === "true" +const performanceClone = (process.env.VI_TESTING === "true" ? new EventTarget() : performance) as Performance; From 027a4600051e3bac465f68494aae258ac2f3f612 Mon Sep 17 00:00:00 2001 From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> Date: Fri, 1 May 2026 02:01:11 +0900 Subject: [PATCH 6/9] =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/repo/scripts.ts | 1 - src/inject.ts | 5 +++- src/runtime/background/gm_api.ts | 45 ++++++++++++++++--------------- src/runtime/background/runtime.ts | 18 +++++++------ src/runtime/content/content.ts | 4 ++- src/runtime/content/gm_api.ts | 12 +++++++-- src/runtime/gm_api.test.ts | 5 ++-- 7 files changed, 52 insertions(+), 38 deletions(-) diff --git a/src/app/repo/scripts.ts b/src/app/repo/scripts.ts index 7a745b0d1..57dcf811a 100644 --- a/src/app/repo/scripts.ts +++ b/src/app/repo/scripts.ts @@ -84,7 +84,6 @@ export interface ScriptRunResource extends Script { grantMap: { [key: string]: string }; value: { [key: string]: Value }; flag: string; - executionToken?: string; resource: { [key: string]: Resource }; sourceCode: string; } diff --git a/src/inject.ts b/src/inject.ts index 8487f039e..93072d036 100644 --- a/src/inject.ts +++ b/src/inject.ts @@ -1,6 +1,8 @@ import LoggerCore from "./app/logger/core"; import MessageWriter from "./app/logger/message_writer"; import MessageContent from "./app/message/content"; +import { type ScriptRunResource } from "./app/repo/scripts"; +import { assignExecutionToken } from "./runtime/content/gm_api"; import InjectRuntime from "./runtime/content/inject"; // 通过flag与content建立通讯,这个ScriptFlag是后端注入时候生成的 @@ -17,7 +19,8 @@ const logger = new LoggerCore({ }); -message.setHandler("pageLoad", (_action, data) => { +message.setHandler("pageLoad", (_action, data: { scripts: ScriptRunResource[], executionToken?: string }) => { + assignExecutionToken(`${data.executionToken || ""}`); logger.logger().debug("inject start"); const runtime = new InjectRuntime(message, data.scripts, flag); runtime.start(); diff --git a/src/runtime/background/gm_api.ts b/src/runtime/background/gm_api.ts index 78c29ee4a..db7384641 100644 --- a/src/runtime/background/gm_api.ts +++ b/src/runtime/background/gm_api.ts @@ -50,28 +50,29 @@ export type Request = MessageRequest & { export type Api = (request: Request, connect?: Channel) => Promise; -export default class GMApi { - static executionMap = new Map< - string, - { scriptIds: Set; tabId: number } - >(); - - static registerScriptExecution(scriptIds: number[], tabId: number) { - const token = uuidv4(); - this.executionMap.set(token, { - scriptIds: new Set(scriptIds), - tabId, - }); - return token; - } +const executionMap = new Map< + string, + { scriptIds: Set; tabId: number } +>(); + +export const registerScriptExecution = (scriptIds: number[], tabId: number): string => { + const token = uuidv4(); + executionMap.set(token, { + scriptIds: new Set(scriptIds), + tabId, + }); + return token; +}; - static removeTabExecutions(tabId: number) { - this.executionMap.forEach((execution, token) => { - if (execution.tabId === tabId) { - this.executionMap.delete(token); - } - }); - } +export const removeTabExecutions = (tabId: number) => { + executionMap.forEach((execution, token) => { + if (execution.tabId === tabId) { + executionMap.delete(token); + } + }); +}; + +export default class GMApi { message: MessageHander; @@ -191,7 +192,7 @@ export default class GMApi { if (!request.executionToken) { throw new Error("script execution is not trusted"); } - const execution = GMApi.executionMap.get(request.executionToken); + const execution = executionMap.get(request.executionToken); if (!execution || !execution.scriptIds.has(request.scriptId)) { throw new Error("script execution is not trusted"); } diff --git a/src/runtime/background/runtime.ts b/src/runtime/background/runtime.ts index 3a9310b73..8c6e55c11 100644 --- a/src/runtime/background/runtime.ts +++ b/src/runtime/background/runtime.ts @@ -29,7 +29,7 @@ import Manager from "@App/app/service/manager"; import Hook from "@App/app/service/hook"; import { i18nName } from "@App/locales/locales"; import { compileInjectScript, compileScriptCode } from "../content/utils"; -import GMApi, { type Request } from "./gm_api"; +import GMApi, { registerScriptExecution, removeTabExecutions, type Request } from "./gm_api"; import { genScriptMenu } from "./utils"; export type RuntimeEvent = "start" | "stop" | "watchRunStatus"; @@ -285,7 +285,7 @@ export default class Runtime extends Manager { }; chrome.tabs.onRemoved.addListener((tabId) => { runScript.delete(tabId); - GMApi.removeTabExecutions(tabId); + removeTabExecutions(tabId); }); // 给popup页面获取运行脚本,与菜单 this.message.setHandler( @@ -432,16 +432,18 @@ export default class Runtime extends Manager { return; } - const executionToken = GMApi.registerScriptExecution( + const executionToken = registerScriptExecution( filter.map((script) => script.id), sender.tabId! ); - const runResources = filter.map((script) => ({ - ...script, - executionToken, - })); - resolve({ scripts: runResources }); + const runResources = filter; + // const runResources = filter.map((script) => ({ + // ...script, + // executionToken, + // })); + + resolve({ scripts: runResources, executionToken }); // 注入脚本 runResources.forEach((script) => { diff --git a/src/runtime/content/content.ts b/src/runtime/content/content.ts index 856856a9e..191e630b1 100644 --- a/src/runtime/content/content.ts +++ b/src/runtime/content/content.ts @@ -3,6 +3,7 @@ import MessageContent from "@App/app/message/content"; import MessageInternal from "@App/app/message/internal"; import { MessageHander, MessageManager } from "@App/app/message/message"; import { ScriptRunResource } from "@App/app/repo/scripts"; +import { assignExecutionToken } from "./gm_api"; // content页的处理 export default class ContentRuntime { @@ -18,7 +19,8 @@ export default class ContentRuntime { this.internalMessage = internalMessage; } - start(resp: { scripts: ScriptRunResource[] }) { + start(resp: { scripts: ScriptRunResource[], executionToken?: string }) { + assignExecutionToken(`${resp.executionToken || ""}`); // 由content到background // 转发gmApi消息 this.contentMessage.setHandler("gmApi", (action, data) => { diff --git a/src/runtime/content/gm_api.ts b/src/runtime/content/gm_api.ts index 4fb002935..2a0949e25 100644 --- a/src/runtime/content/gm_api.ts +++ b/src/runtime/content/gm_api.ts @@ -11,6 +11,14 @@ import { parseUserConfig } from "@App/pkg/utils/yaml"; import { v4 as uuidv4 } from "uuid"; import { type ValueUpdateData } from "./exec_script"; +// content/page 环境的变数,不储存在任何object +let currentExecutionToken = ""; +export const assignExecutionToken = (executionToken: string) => { + // content/page 环境 pageLoad 时储存由 background 生成的 executionToken + if (currentExecutionToken) throw new Error("currentExecutionToken cannot be re-assigned"); + currentExecutionToken = executionToken; +}; + interface ApiParam { depend?: string[]; listener?: () => void; @@ -77,7 +85,7 @@ export class GM_Base { scriptId: this.scriptRes.id, params, runFlag: this.runFlag, - executionToken: this.scriptRes.executionToken, + executionToken: currentExecutionToken, }); } @@ -91,7 +99,7 @@ export class GM_Base { scriptId: this.scriptRes.id, params, runFlag: this.runFlag, - executionToken: this.scriptRes.executionToken, + executionToken: currentExecutionToken, }); return channel; } diff --git a/src/runtime/gm_api.test.ts b/src/runtime/gm_api.test.ts index f200884fa..be4aa4cf4 100644 --- a/src/runtime/gm_api.test.ts +++ b/src/runtime/gm_api.test.ts @@ -1,7 +1,7 @@ // gm api 单元测试 // 初始化runtime环境 import initTestEnv from "@App/pkg/utils/test_utils"; -import GMApi from "./background/gm_api"; +import GMApi, { registerScriptExecution } from "./background/gm_api"; import LoggerCore from "@App/app/logger/core"; import MessageCenter from "@App/app/message/center"; import { ScriptDAO, ScriptRunResource } from "@App/app/repo/scripts"; @@ -111,7 +111,7 @@ describe("GM execution trust", () => { }); it("accepts content messages with a matching execution token", async () => { - const executionToken = GMApi.registerScriptExecution([scriptRes.id], 1); + const executionToken = registerScriptExecution([scriptRes.id], 1); await expect( backgroundApi.parseRequest( { @@ -129,7 +129,6 @@ describe("GM execution trust", () => { ) ).resolves.toMatchObject({ scriptId: scriptRes.id, - executionToken, }); }); }); From 2247883433252a80c3ba6208712532ec280454e2 Mon Sep 17 00:00:00 2001 From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> Date: Fri, 1 May 2026 08:38:34 +0900 Subject: [PATCH 7/9] =?UTF-8?q?InjectRuntime=20=E4=BB=A3=E7=A0=81=E5=AF=B9?= =?UTF-8?q?=E9=BD=90=20ContentRuntime?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/content.ts | 3 ++- src/inject.ts | 8 +++----- src/runtime/content/inject.ts | 9 ++++----- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/content.ts b/src/content.ts index f0254ecf9..ab3720c0f 100644 --- a/src/content.ts +++ b/src/content.ts @@ -30,5 +30,6 @@ internalMessage.syncSend("pageLoad", null).then((resp) => { logger.logger().debug("content start"); // 通过flag与inject建立通讯 const contentMessage = new MessageContent(scriptFlag, true); - new ContentRuntime(contentMessage, internalMessage).start(resp); + const contentRuntime = new ContentRuntime(contentMessage, internalMessage); + contentRuntime.start(resp); }); diff --git a/src/inject.ts b/src/inject.ts index 93072d036..61e392e08 100644 --- a/src/inject.ts +++ b/src/inject.ts @@ -2,7 +2,6 @@ import LoggerCore from "./app/logger/core"; import MessageWriter from "./app/logger/message_writer"; import MessageContent from "./app/message/content"; import { type ScriptRunResource } from "./app/repo/scripts"; -import { assignExecutionToken } from "./runtime/content/gm_api"; import InjectRuntime from "./runtime/content/inject"; // 通过flag与content建立通讯,这个ScriptFlag是后端注入时候生成的 @@ -19,9 +18,8 @@ const logger = new LoggerCore({ }); -message.setHandler("pageLoad", (_action, data: { scripts: ScriptRunResource[], executionToken?: string }) => { - assignExecutionToken(`${data.executionToken || ""}`); +message.setHandler("pageLoad", (_action, resp: { scripts: ScriptRunResource[], executionToken?: string }) => { logger.logger().debug("inject start"); - const runtime = new InjectRuntime(message, data.scripts, flag); - runtime.start(); + const runtime = new InjectRuntime(message, flag); + runtime.start(resp); }); diff --git a/src/runtime/content/inject.ts b/src/runtime/content/inject.ts index 565e34445..20feb9293 100644 --- a/src/runtime/content/inject.ts +++ b/src/runtime/content/inject.ts @@ -3,10 +3,10 @@ import { type ScriptRunResource } from "@App/app/repo/scripts"; import ExecScript, { type ValueUpdateData } from "./exec_script"; import { addStyleSheet, type ScriptFunc } from "./utils"; import { onInjectPageLoaded } from "./external"; +import { assignExecutionToken } from "./gm_api"; // 注入脚本的沙盒环境 export default class InjectRuntime { - scripts: ScriptRunResource[]; flag: string; @@ -16,16 +16,15 @@ export default class InjectRuntime { constructor( message: MessageContent, - scripts: ScriptRunResource[], flag: string ) { this.message = message; - this.scripts = scripts; this.flag = flag; } - start() { - this.scripts.forEach((script) => { + start(resp: { scripts: ScriptRunResource[], executionToken?: string }) { + assignExecutionToken(`${resp.executionToken || ""}`); + resp.scripts.forEach((script) => { // @ts-ignore const scriptFunc = window[script.flag]; if (scriptFunc) { From e2c3fbaa8d81e7a525ff4455596ee640c46b3e6d Mon Sep 17 00:00:00 2001 From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> Date: Fri, 1 May 2026 08:56:29 +0900 Subject: [PATCH 8/9] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/content.ts | 5 +++-- src/inject.ts | 4 ++-- src/runtime/content/inject.ts | 6 +----- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/content.ts b/src/content.ts index ab3720c0f..db8187e6f 100644 --- a/src/content.ts +++ b/src/content.ts @@ -17,6 +17,9 @@ const logger = new LoggerCore({ const scriptFlag = randomString(8); +// 通过flag与inject建立通讯 +const contentMessage = new MessageContent(scriptFlag, true); + // 注入运行框架 const temp = document.createElementNS("http://www.w3.org/1999/xhtml", "script"); temp.setAttribute("type", "text/javascript"); @@ -28,8 +31,6 @@ temp.remove(); internalMessage.syncSend("pageLoad", null).then((resp) => { logger.logger().debug("content start"); - // 通过flag与inject建立通讯 - const contentMessage = new MessageContent(scriptFlag, true); const contentRuntime = new ContentRuntime(contentMessage, internalMessage); contentRuntime.start(resp); }); diff --git a/src/inject.ts b/src/inject.ts index 61e392e08..9dde14a1d 100644 --- a/src/inject.ts +++ b/src/inject.ts @@ -8,6 +8,7 @@ import InjectRuntime from "./runtime/content/inject"; // eslint-disable-next-line no-undef const flag = ScriptFlag; +// 通过flag与content建立通讯 const message = new MessageContent(flag, false); // 加载logger组件 @@ -17,9 +18,8 @@ const logger = new LoggerCore({ labels: { env: "inject", href: window.location.href }, }); - message.setHandler("pageLoad", (_action, resp: { scripts: ScriptRunResource[], executionToken?: string }) => { logger.logger().debug("inject start"); - const runtime = new InjectRuntime(message, flag); + const runtime = new InjectRuntime(message); runtime.start(resp); }); diff --git a/src/runtime/content/inject.ts b/src/runtime/content/inject.ts index 20feb9293..daf6abe6e 100644 --- a/src/runtime/content/inject.ts +++ b/src/runtime/content/inject.ts @@ -8,18 +8,14 @@ import { assignExecutionToken } from "./gm_api"; // 注入脚本的沙盒环境 export default class InjectRuntime { - flag: string; - message: MessageContent; execList: ExecScript[] = []; constructor( - message: MessageContent, - flag: string + message: MessageContent ) { this.message = message; - this.flag = flag; } start(resp: { scripts: ScriptRunResource[], executionToken?: string }) { From b8d52fc5980ae07e28b2d4bc6f0d93b84df53150 Mon Sep 17 00:00:00 2001 From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> Date: Fri, 1 May 2026 09:22:55 +0900 Subject: [PATCH 9/9] =?UTF-8?q?=E4=BF=AE=E6=94=B9=20ScriptFlag=20=E6=B3=A8?= =?UTF-8?q?=E5=85=A5=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/message/content.ts | 1 + src/content.ts | 3 ++- src/inject.ts | 3 +-- src/types/main.d.ts | 2 -- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/app/message/content.ts b/src/app/message/content.ts index 28bec38f2..8a319128e 100644 --- a/src/app/message/content.ts +++ b/src/app/message/content.ts @@ -29,6 +29,7 @@ export default class MessageContent constructor(eventId: string, isContent: boolean) { super(); + if (!eventId || eventId[0] === "{") throw new Error("eventId is missing"); this.eventId = eventId; this.isContent = isContent; this.channelManager = new WarpChannelManager((data) => { diff --git a/src/content.ts b/src/content.ts index db8187e6f..b76db6192 100644 --- a/src/content.ts +++ b/src/content.ts @@ -24,7 +24,8 @@ const contentMessage = new MessageContent(scriptFlag, true); const temp = document.createElementNS("http://www.w3.org/1999/xhtml", "script"); temp.setAttribute("type", "text/javascript"); temp.setAttribute("charset", "UTF-8"); -temp.textContent = `(function (ScriptFlag) {\n${injectJs}\n})('${scriptFlag}')${sourceMapTo("injected.js")}`; +const injectJsConv = injectJs.replace("{{__ScriptFlag__}}", () =>`${scriptFlag}`); +temp.textContent = `(function () {\n${injectJsConv}\n})()${sourceMapTo("injected.js")}`; temp.className = "injected-js"; document.documentElement.appendChild(temp); temp.remove(); diff --git a/src/inject.ts b/src/inject.ts index 9dde14a1d..6932e33de 100644 --- a/src/inject.ts +++ b/src/inject.ts @@ -5,8 +5,7 @@ import { type ScriptRunResource } from "./app/repo/scripts"; import InjectRuntime from "./runtime/content/inject"; // 通过flag与content建立通讯,这个ScriptFlag是后端注入时候生成的 -// eslint-disable-next-line no-undef -const flag = ScriptFlag; +const flag = "{{__ScriptFlag__}}"; // 通过flag与content建立通讯 const message = new MessageContent(flag, false); diff --git a/src/types/main.d.ts b/src/types/main.d.ts index c792ad38e..b37cf186a 100644 --- a/src/types/main.d.ts +++ b/src/types/main.d.ts @@ -1,7 +1,5 @@ declare let sandbox: Window; -declare let ScriptFlag: string; - declare let browser: chrome; declare let cloneInto: ((detail: any, view: any) => any) | undefined;