From 65e18bbec354f94d7bf7e4b2117b89d05bcce2df Mon Sep 17 00:00:00 2001 From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> Date: Tue, 17 Feb 2026 13:01:12 +0900 Subject: [PATCH 1/3] test --- src/app/service/service_worker/script.ts | 48 ++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/app/service/service_worker/script.ts b/src/app/service/service_worker/script.ts index 227be28d1..34c7a623b 100644 --- a/src/app/service/service_worker/script.ts +++ b/src/app/service/service_worker/script.ts @@ -300,6 +300,54 @@ export class ScriptService { } } ); + + { + type Config = Record; + const REMOVE_HEADERS = [ + `content-security-policy`, + `content-security-policy-report-only`, + `x-webkit-csp`, + `x-content-security-policy`, + `x-frame-options`, + ]; + + const { RuleActionType, HeaderOperation, ResourceType } = chrome.declarativeNetRequest; + + const rules: chrome.declarativeNetRequest.Rule[] = [ + { + id: 2001, + action: { + type: RuleActionType.MODIFY_HEADERS, + responseHeaders: REMOVE_HEADERS.map((header) => ({ + operation: HeaderOperation.REMOVE, + header, + })), + }, + condition: { + urlFilter: `|http*`, + resourceTypes: [ResourceType.MAIN_FRAME, ResourceType.SUB_FRAME], + }, + }, + ]; + + const updateRules = (newConfig: Config, oldConfig?: Config) => { + if (oldConfig && newConfig.csp_http_disabled === oldConfig?.csp_http_disabled) { + return; + } + if (newConfig.csp_http_disabled) { + chrome.declarativeNetRequest.updateDynamicRules({ + removeRuleIds: rules.map((rule) => rule.id), + addRules: rules, + }); + } else { + chrome.declarativeNetRequest.updateDynamicRules({ + removeRuleIds: rules.map((rule) => rule.id), + }); + } + }; + + updateRules({ csp_http_disabled: true }); + } } public async openInstallPageByUrl( From 18953c3419496a8673ea30fd851ba9dea79981f1 Mon Sep 17 00:00:00 2001 From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> Date: Wed, 18 Feb 2026 09:13:21 +0900 Subject: [PATCH 2/3] Create dnr.ts --- src/pkg/utils/dnr.ts | 46 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/pkg/utils/dnr.ts diff --git a/src/pkg/utils/dnr.ts b/src/pkg/utils/dnr.ts new file mode 100644 index 000000000..32f011b54 --- /dev/null +++ b/src/pkg/utils/dnr.ts @@ -0,0 +1,46 @@ +export const isValidDNRUrlFilter = (text: string) => { + // https://developer.chrome.com/docs/extensions/reference/api/declarativeNetRequest?hl=en#property-RuleCondition-urlFilter + + const domainNameAnchor = text.startsWith("||"); + + const leftAnchor = !domainNameAnchor && text.startsWith("|"); + + const rightAnchor = text.endsWith("|"); + + try { + let s = text; + + if (domainNameAnchor) s = s.slice(2); + if (leftAnchor) s = s.slice(1); + if (rightAnchor) s = s.slice(0, -1); + + const t = s.replace(/\*/g, "").replace(/^/g, "_"); + + // eslint-disable-next-line no-control-regex + if (/[^\x00-\xFF]/.test(t)) return false; + + new URL(t); + + return true; + } catch { + return false; + } +}; + +export const convertDomainToDNRUrlFilter = (text: string) => { + // will match its domain and subdomain + let ret; + text = text.toLowerCase(); + try { + if (text.startsWith("http") && /^http[s*]?:\/\//.test(text)) { + const u = new URL(`${text.replace("http*://", "http-wildcard://")}`); + ret = `|${u.origin.replace("http-wildcard://", "http://")}`; + } else { + const u = new URL(`https://${text}/`); + ret = `||${u.hostname}`; + } + } catch { + throw new Error("invalid domain"); + } + return ret; +}; From f1d2c4e05e1a8adfbb67d70b3575c28cd1407f21 Mon Sep 17 00:00:00 2001 From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> Date: Sun, 22 Mar 2026 04:54:21 +0900 Subject: [PATCH 3/3] demo code --- src/app/service/service_worker/script.ts | 28 ++------------------- src/pkg/utils/dnr.ts | 31 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/src/app/service/service_worker/script.ts b/src/app/service/service_worker/script.ts index 406ee96dd..0c0cf7b35 100644 --- a/src/app/service/service_worker/script.ts +++ b/src/app/service/service_worker/script.ts @@ -47,6 +47,7 @@ import { getSimilarityScore, ScriptUpdateCheck } from "./script_update_check"; import { LocalStorageDAO } from "@App/app/repo/localStorage"; import { CompiledResourceDAO } from "@App/app/repo/resource"; import { initRegularUpdateCheck } from "./regular_updatecheck"; +import { createNoCSPRules } from "@App/pkg/utils/dnr"; export type TCheckScriptUpdateOption = Partial< { checkType: "user"; noUpdateCheck?: number } | ({ checkType: "system" } & Record) @@ -316,32 +317,7 @@ export class ScriptService { { type Config = Record; - const REMOVE_HEADERS = [ - `content-security-policy`, - `content-security-policy-report-only`, - `x-webkit-csp`, - `x-content-security-policy`, - `x-frame-options`, - ]; - - const { RuleActionType, HeaderOperation, ResourceType } = chrome.declarativeNetRequest; - - const rules: chrome.declarativeNetRequest.Rule[] = [ - { - id: 2001, - action: { - type: RuleActionType.MODIFY_HEADERS, - responseHeaders: REMOVE_HEADERS.map((header) => ({ - operation: HeaderOperation.REMOVE, - header, - })), - }, - condition: { - urlFilter: `|http*`, - resourceTypes: [ResourceType.MAIN_FRAME, ResourceType.SUB_FRAME], - }, - }, - ]; + const rules = createNoCSPRules([`|http*`]); const updateRules = (newConfig: Config, oldConfig?: Config) => { if (oldConfig && newConfig.csp_http_disabled === oldConfig?.csp_http_disabled) { diff --git a/src/pkg/utils/dnr.ts b/src/pkg/utils/dnr.ts index 32f011b54..c6dd6b13e 100644 --- a/src/pkg/utils/dnr.ts +++ b/src/pkg/utils/dnr.ts @@ -44,3 +44,34 @@ export const convertDomainToDNRUrlFilter = (text: string) => { } return ret; }; + +export const createNoCSPRules = (urlFilters: string[]) => { + const REMOVE_HEADERS = [ + `content-security-policy`, + `content-security-policy-report-only`, + `x-webkit-csp`, + `x-content-security-policy`, + `x-frame-options`, + ]; + const { RuleActionType, HeaderOperation, ResourceType } = chrome.declarativeNetRequest; + if (urlFilters.length > 512) { + throw new Error(`Too many URL patterns (${urlFilters.length}). Max is 512.`); + } + const rules: chrome.declarativeNetRequest.Rule[] = urlFilters.map((urlFilter, index) => { + return { + id: 2001 + index, + action: { + type: RuleActionType.MODIFY_HEADERS, + responseHeaders: REMOVE_HEADERS.map((header) => ({ + operation: HeaderOperation.REMOVE, + header, + })), + }, + condition: { + urlFilter: urlFilter, + resourceTypes: [ResourceType.MAIN_FRAME, ResourceType.SUB_FRAME], + }, + } satisfies chrome.declarativeNetRequest.Rule; + }); + return rules; +};