Skip to content

Commit 8af18b7

Browse files
committed
refactor: extract GM cookie handler
1 parent 6d75041 commit 8af18b7

15 files changed

Lines changed: 775 additions & 267 deletions

‎background.core.js‎

Lines changed: 5 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -8666,92 +8666,11 @@ async function handleMessage(message, sender) {
86668666
// a clear error instead of leaking the raw Chrome error (and to make sure
86678667
// we never pass an attacker-controlled URL into a future Chrome API that
86688668
// is more permissive about schemes).
8669-
case 'GM_cookie_list': {
8670-
try {
8671-
const cookieScriptId = sender.userScriptId || data.scriptId;
8672-
if (!cookieScriptId) return { error: 'Missing script context' };
8673-
const cookieScript = await ScriptStorage.get(cookieScriptId);
8674-
if (!cookieScript) return { error: 'Script context not found' };
8675-
const details = {};
8676-
if (data.url) {
8677-
if (!isHttpCookieUrl(data.url)) return { error: 'url must be http(s)://' };
8678-
details.url = data.url;
8679-
}
8680-
if (data.domain) details.domain = data.domain;
8681-
if (data.name) details.name = data.name;
8682-
if (data.path) details.path = data.path;
8683-
const partition = normalizeCookiePartitionKey(data.partitionKey);
8684-
if (partition.error) return { error: partition.error };
8685-
if (partition.partitionKey) details.partitionKey = partition.partitionKey;
8686-
const cookieTargetUrl = resolveCookiePolicyTarget(data, sender);
8687-
if (!cookieTargetUrl) return { error: 'url or domain is required for cookie list' };
8688-
if (!isHttpCookieUrl(cookieTargetUrl)) return { error: 'url must be http(s)://' };
8689-
const cookieSettings = await SettingsManager.get();
8690-
const cookiePolicy = evaluateScriptHostScopePolicy(cookieScript, cookieTargetUrl, 'Cookie access', cookieSettings);
8691-
if (!cookiePolicy.allowed) return { error: cookiePolicy.error };
8692-
if (!details.url && !details.domain) details.url = cookieTargetUrl;
8693-
const cookies = await chrome.cookies.getAll(details);
8694-
return { success: true, cookies };
8695-
} catch (e) {
8696-
return { error: e.message };
8697-
}
8698-
}
8699-
8700-
case 'GM_cookie_set': {
8701-
try {
8702-
if (!data.url) return { error: 'url is required for cookie set' };
8703-
if (!data.name) return { error: 'name is required for cookie set' };
8704-
if (!isHttpCookieUrl(data.url)) return { error: 'url must be http(s)://' };
8705-
const cookieScriptId = sender.userScriptId || data.scriptId;
8706-
if (!cookieScriptId) return { error: 'Missing script context' };
8707-
const cookieScript = await ScriptStorage.get(cookieScriptId);
8708-
if (!cookieScript) return { error: 'Script context not found' };
8709-
const cookieSettings = await SettingsManager.get();
8710-
const cookiePolicy = evaluateScriptHostScopePolicy(cookieScript, data.url, 'Cookie access', cookieSettings);
8711-
if (!cookiePolicy.allowed) return { error: cookiePolicy.error };
8712-
const partition = normalizeCookiePartitionKey(data.partitionKey);
8713-
if (partition.error) return { error: partition.error };
8714-
const cookie = await chrome.cookies.set({
8715-
url: data.url,
8716-
name: data.name,
8717-
value: data.value || '',
8718-
domain: data.domain,
8719-
path: data.path || '/',
8720-
secure: data.secure || false,
8721-
httpOnly: data.httpOnly || false,
8722-
expirationDate: data.expirationDate,
8723-
sameSite: data.sameSite || 'unspecified',
8724-
...(partition.partitionKey ? { partitionKey: partition.partitionKey } : {})
8725-
});
8726-
return { success: true, cookie };
8727-
} catch (e) {
8728-
return { error: e.message };
8729-
}
8730-
}
8731-
8732-
case 'GM_cookie_delete': {
8733-
try {
8734-
if (!data.url || !data.name) return { error: 'url and name are required for cookie delete' };
8735-
if (!isHttpCookieUrl(data.url)) return { error: 'url must be http(s)://' };
8736-
const cookieScriptId = sender.userScriptId || data.scriptId;
8737-
if (!cookieScriptId) return { error: 'Missing script context' };
8738-
const cookieScript = await ScriptStorage.get(cookieScriptId);
8739-
if (!cookieScript) return { error: 'Script context not found' };
8740-
const cookieSettings = await SettingsManager.get();
8741-
const cookiePolicy = evaluateScriptHostScopePolicy(cookieScript, data.url, 'Cookie access', cookieSettings);
8742-
if (!cookiePolicy.allowed) return { error: cookiePolicy.error };
8743-
const partition = normalizeCookiePartitionKey(data.partitionKey);
8744-
if (partition.error) return { error: partition.error };
8745-
await chrome.cookies.remove({
8746-
url: data.url,
8747-
name: data.name,
8748-
...(partition.partitionKey ? { partitionKey: partition.partitionKey } : {})
8749-
});
8750-
return { success: true };
8751-
} catch (e) {
8752-
return { error: e.message };
8753-
}
8754-
}
8669+
case 'GM_cookie_list':
8670+
case 'GM_cookie_set':
8671+
case 'GM_cookie_delete':
8672+
if (typeof GMCookieHandler === 'undefined') return { error: 'GMCookieHandler not available' };
8673+
return await GMCookieHandler.handleGMCookieMessage(action, data, sender);
87558674

87568675
// GM_webRequest — runtime rule update from script
87578676
case 'GM_webRequest': {

‎background.js‎

Lines changed: 173 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -18804,6 +18804,174 @@ if (typeof self !== 'undefined') {
1880418804
self.GMWebRequestHandler = GMWebRequestHandler;
1880518805
}
1880618806

18807+
// ============================================================================
18808+
// Generated from src/background/gm-cookie-handler.ts; do not edit by hand.
18809+
// Run `node scripts/generate-ts-runtime-modules.mjs` or `npm run build:bg`.
18810+
// ============================================================================
18811+
18812+
const GMCookieHandler = (() => {
18813+
const module = { exports: {} };
18814+
const exports = module.exports;
18815+
"use strict";
18816+
var __defProp = Object.defineProperty;
18817+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
18818+
var __getOwnPropNames = Object.getOwnPropertyNames;
18819+
var __hasOwnProp = Object.prototype.hasOwnProperty;
18820+
var __export = (target, all) => {
18821+
for (var name in all)
18822+
__defProp(target, name, { get: all[name], enumerable: true });
18823+
};
18824+
var __copyProps = (to, from, except, desc) => {
18825+
if (from && typeof from === "object" || typeof from === "function") {
18826+
for (let key of __getOwnPropNames(from))
18827+
if (!__hasOwnProp.call(to, key) && key !== except)
18828+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18829+
}
18830+
return to;
18831+
};
18832+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18833+
18834+
// src/background/gm-cookie-handler.ts
18835+
var gm_cookie_handler_exports = {};
18836+
__export(gm_cookie_handler_exports, {
18837+
GMCookieHandler: () => GMCookieHandler,
18838+
GM_COOKIE_ACTIONS: () => GM_COOKIE_ACTIONS,
18839+
default: () => gm_cookie_handler_default,
18840+
handleGMCookieMessage: () => handleGMCookieMessage,
18841+
isGMCookieAction: () => isGMCookieAction
18842+
});
18843+
module.exports = __toCommonJS(gm_cookie_handler_exports);
18844+
var GM_COOKIE_ACTIONS = [
18845+
"GM_cookie_delete",
18846+
"GM_cookie_list",
18847+
"GM_cookie_set"
18848+
];
18849+
var GM_COOKIE_ACTION_SET = new Set(GM_COOKIE_ACTIONS);
18850+
function cookieGetAll() {
18851+
return chrome.cookies.getAll;
18852+
}
18853+
function cookieSet() {
18854+
return chrome.cookies.set;
18855+
}
18856+
function cookieRemove() {
18857+
return chrome.cookies.remove;
18858+
}
18859+
function errorMessage(error) {
18860+
if (error && typeof error === "object" && "message" in error) {
18861+
const message = error.message;
18862+
if (typeof message === "string") return message;
18863+
}
18864+
return String(error);
18865+
}
18866+
async function getCookieScript(data, sender) {
18867+
const scriptId = sender.userScriptId || data.scriptId;
18868+
if (!scriptId) return { error: "Missing script context" };
18869+
const script = await ScriptStorage.get(scriptId);
18870+
if (!script) return { error: "Script context not found" };
18871+
return { script, scriptId };
18872+
}
18873+
async function enforceCookiePolicy(script, url) {
18874+
const settings = await SettingsManager.get();
18875+
const policy = evaluateScriptHostScopePolicy(script, url, "Cookie access", settings);
18876+
return policy.allowed ? null : policy.error || "Cookie access denied";
18877+
}
18878+
function isGMCookieAction(action) {
18879+
return typeof action === "string" && GM_COOKIE_ACTION_SET.has(action);
18880+
}
18881+
async function handleGMCookieMessage(action, data = {}, sender = {}) {
18882+
switch (action) {
18883+
case "GM_cookie_list": {
18884+
try {
18885+
const context = await getCookieScript(data, sender);
18886+
if (context.error) return { error: context.error };
18887+
const details = {};
18888+
if (data.url) {
18889+
if (!isHttpCookieUrl(data.url)) return { error: "url must be http(s)://" };
18890+
details.url = data.url;
18891+
}
18892+
if (data.domain) details.domain = data.domain;
18893+
if (data.name) details.name = data.name;
18894+
if (data.path) details.path = data.path;
18895+
const partition = normalizeCookiePartitionKey(data.partitionKey);
18896+
if (partition.error) return { error: partition.error };
18897+
if (partition.partitionKey) details.partitionKey = partition.partitionKey;
18898+
const cookieTargetUrl = resolveCookiePolicyTarget(data, sender);
18899+
if (!cookieTargetUrl) return { error: "url or domain is required for cookie list" };
18900+
if (!isHttpCookieUrl(cookieTargetUrl)) return { error: "url must be http(s)://" };
18901+
const policyError = await enforceCookiePolicy(context.script, cookieTargetUrl);
18902+
if (policyError) return { error: policyError };
18903+
if (!details.url && !details.domain) details.url = cookieTargetUrl;
18904+
const cookies = await cookieGetAll()(details);
18905+
return { success: true, cookies };
18906+
} catch (error) {
18907+
return { error: errorMessage(error) };
18908+
}
18909+
}
18910+
case "GM_cookie_set": {
18911+
try {
18912+
if (!data.url) return { error: "url is required for cookie set" };
18913+
if (!data.name) return { error: "name is required for cookie set" };
18914+
if (!isHttpCookieUrl(data.url)) return { error: "url must be http(s)://" };
18915+
const context = await getCookieScript(data, sender);
18916+
if (context.error) return { error: context.error };
18917+
const policyError = await enforceCookiePolicy(context.script, data.url);
18918+
if (policyError) return { error: policyError };
18919+
const partition = normalizeCookiePartitionKey(data.partitionKey);
18920+
if (partition.error) return { error: partition.error };
18921+
const cookie = await cookieSet()({
18922+
url: data.url,
18923+
name: data.name,
18924+
value: data.value || "",
18925+
domain: data.domain,
18926+
path: data.path || "/",
18927+
secure: data.secure || false,
18928+
httpOnly: data.httpOnly || false,
18929+
expirationDate: data.expirationDate,
18930+
sameSite: data.sameSite || "unspecified",
18931+
...partition.partitionKey ? { partitionKey: partition.partitionKey } : {}
18932+
});
18933+
return { success: true, cookie };
18934+
} catch (error) {
18935+
return { error: errorMessage(error) };
18936+
}
18937+
}
18938+
case "GM_cookie_delete": {
18939+
try {
18940+
if (!data.url || !data.name) return { error: "url and name are required for cookie delete" };
18941+
if (!isHttpCookieUrl(data.url)) return { error: "url must be http(s)://" };
18942+
const context = await getCookieScript(data, sender);
18943+
if (context.error) return { error: context.error };
18944+
const policyError = await enforceCookiePolicy(context.script, data.url);
18945+
if (policyError) return { error: policyError };
18946+
const partition = normalizeCookiePartitionKey(data.partitionKey);
18947+
if (partition.error) return { error: partition.error };
18948+
await cookieRemove()({
18949+
url: data.url,
18950+
name: data.name,
18951+
...partition.partitionKey ? { partitionKey: partition.partitionKey } : {}
18952+
});
18953+
return { success: true };
18954+
} catch (error) {
18955+
return { error: errorMessage(error) };
18956+
}
18957+
}
18958+
default:
18959+
return { error: `Unsupported GM_cookie action: ${action}` };
18960+
}
18961+
}
18962+
var GMCookieHandler = Object.freeze({
18963+
GM_COOKIE_ACTIONS,
18964+
handleGMCookieMessage,
18965+
isGMCookieAction
18966+
});
18967+
var gm_cookie_handler_default = GMCookieHandler;
18968+
return module.exports.default || module.exports.GMCookieHandler || module.exports;
18969+
})();
18970+
18971+
if (typeof self !== 'undefined') {
18972+
self.GMCookieHandler = GMCookieHandler;
18973+
}
18974+
1880718975
// ============================================================================
1880818976
// Generated from src/background/connect-policy.ts; do not edit by hand.
1880918977
// Run `node scripts/generate-ts-runtime-modules.mjs` or `npm run build:bg`.
@@ -38261,92 +38429,11 @@ async function handleMessage(message, sender) {
3826138429
// a clear error instead of leaking the raw Chrome error (and to make sure
3826238430
// we never pass an attacker-controlled URL into a future Chrome API that
3826338431
// is more permissive about schemes).
38264-
case 'GM_cookie_list': {
38265-
try {
38266-
const cookieScriptId = sender.userScriptId || data.scriptId;
38267-
if (!cookieScriptId) return { error: 'Missing script context' };
38268-
const cookieScript = await ScriptStorage.get(cookieScriptId);
38269-
if (!cookieScript) return { error: 'Script context not found' };
38270-
const details = {};
38271-
if (data.url) {
38272-
if (!isHttpCookieUrl(data.url)) return { error: 'url must be http(s)://' };
38273-
details.url = data.url;
38274-
}
38275-
if (data.domain) details.domain = data.domain;
38276-
if (data.name) details.name = data.name;
38277-
if (data.path) details.path = data.path;
38278-
const partition = normalizeCookiePartitionKey(data.partitionKey);
38279-
if (partition.error) return { error: partition.error };
38280-
if (partition.partitionKey) details.partitionKey = partition.partitionKey;
38281-
const cookieTargetUrl = resolveCookiePolicyTarget(data, sender);
38282-
if (!cookieTargetUrl) return { error: 'url or domain is required for cookie list' };
38283-
if (!isHttpCookieUrl(cookieTargetUrl)) return { error: 'url must be http(s)://' };
38284-
const cookieSettings = await SettingsManager.get();
38285-
const cookiePolicy = evaluateScriptHostScopePolicy(cookieScript, cookieTargetUrl, 'Cookie access', cookieSettings);
38286-
if (!cookiePolicy.allowed) return { error: cookiePolicy.error };
38287-
if (!details.url && !details.domain) details.url = cookieTargetUrl;
38288-
const cookies = await chrome.cookies.getAll(details);
38289-
return { success: true, cookies };
38290-
} catch (e) {
38291-
return { error: e.message };
38292-
}
38293-
}
38294-
38295-
case 'GM_cookie_set': {
38296-
try {
38297-
if (!data.url) return { error: 'url is required for cookie set' };
38298-
if (!data.name) return { error: 'name is required for cookie set' };
38299-
if (!isHttpCookieUrl(data.url)) return { error: 'url must be http(s)://' };
38300-
const cookieScriptId = sender.userScriptId || data.scriptId;
38301-
if (!cookieScriptId) return { error: 'Missing script context' };
38302-
const cookieScript = await ScriptStorage.get(cookieScriptId);
38303-
if (!cookieScript) return { error: 'Script context not found' };
38304-
const cookieSettings = await SettingsManager.get();
38305-
const cookiePolicy = evaluateScriptHostScopePolicy(cookieScript, data.url, 'Cookie access', cookieSettings);
38306-
if (!cookiePolicy.allowed) return { error: cookiePolicy.error };
38307-
const partition = normalizeCookiePartitionKey(data.partitionKey);
38308-
if (partition.error) return { error: partition.error };
38309-
const cookie = await chrome.cookies.set({
38310-
url: data.url,
38311-
name: data.name,
38312-
value: data.value || '',
38313-
domain: data.domain,
38314-
path: data.path || '/',
38315-
secure: data.secure || false,
38316-
httpOnly: data.httpOnly || false,
38317-
expirationDate: data.expirationDate,
38318-
sameSite: data.sameSite || 'unspecified',
38319-
...(partition.partitionKey ? { partitionKey: partition.partitionKey } : {})
38320-
});
38321-
return { success: true, cookie };
38322-
} catch (e) {
38323-
return { error: e.message };
38324-
}
38325-
}
38326-
38327-
case 'GM_cookie_delete': {
38328-
try {
38329-
if (!data.url || !data.name) return { error: 'url and name are required for cookie delete' };
38330-
if (!isHttpCookieUrl(data.url)) return { error: 'url must be http(s)://' };
38331-
const cookieScriptId = sender.userScriptId || data.scriptId;
38332-
if (!cookieScriptId) return { error: 'Missing script context' };
38333-
const cookieScript = await ScriptStorage.get(cookieScriptId);
38334-
if (!cookieScript) return { error: 'Script context not found' };
38335-
const cookieSettings = await SettingsManager.get();
38336-
const cookiePolicy = evaluateScriptHostScopePolicy(cookieScript, data.url, 'Cookie access', cookieSettings);
38337-
if (!cookiePolicy.allowed) return { error: cookiePolicy.error };
38338-
const partition = normalizeCookiePartitionKey(data.partitionKey);
38339-
if (partition.error) return { error: partition.error };
38340-
await chrome.cookies.remove({
38341-
url: data.url,
38342-
name: data.name,
38343-
...(partition.partitionKey ? { partitionKey: partition.partitionKey } : {})
38344-
});
38345-
return { success: true };
38346-
} catch (e) {
38347-
return { error: e.message };
38348-
}
38349-
}
38432+
case 'GM_cookie_list':
38433+
case 'GM_cookie_set':
38434+
case 'GM_cookie_delete':
38435+
if (typeof GMCookieHandler === 'undefined') return { error: 'GMCookieHandler not available' };
38436+
return await GMCookieHandler.handleGMCookieMessage(action, data, sender);
3835038437

3835138438
// GM_webRequest — runtime rule update from script
3835238439
case 'GM_webRequest': {

‎esbuild.config.mjs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ async function buildBackground() {
114114
readFile("modules/gm-notification-handler.js"),
115115
readFile("modules/gm-resource-handler.js"),
116116
readFile("modules/gm-webrequest-handler.js"),
117+
readFile("modules/gm-cookie-handler.js"),
117118
readFile("modules/connect-policy.js"),
118119
readFile("modules/resources.js"),
119120

0 commit comments

Comments
 (0)