From e7ea19459e98803ad4dfb7f76516b947f22dfa11 Mon Sep 17 00:00:00 2001 From: ioj4 <69911332+ioj4@users.noreply.github.com> Date: Thu, 22 Feb 2024 15:17:09 +0100 Subject: [PATCH] [injectors/mv3] rewrite with csp workaround (#22) * [injectors/mv3] use csp workaround * [injectors/mv3] append script to documentElement because head doesn't exist yet * [injectors/mv3] bump version to 1.2 --- injectors/mv3/content.js | 5 +- .../mv3/{dnr_rules.json => dnr-rules.json} | 3 +- injectors/mv3/loader.js | 57 --------- injectors/mv3/manifest.json | 22 ++-- injectors/mv3/shelter-loader.js | 27 +++++ injectors/mv3/worker.js | 111 ------------------ 6 files changed, 43 insertions(+), 182 deletions(-) rename injectors/mv3/{dnr_rules.json => dnr-rules.json} (74%) delete mode 100644 injectors/mv3/loader.js create mode 100644 injectors/mv3/shelter-loader.js delete mode 100644 injectors/mv3/worker.js diff --git a/injectors/mv3/content.js b/injectors/mv3/content.js index 49b264a..25012d0 100644 --- a/injectors/mv3/content.js +++ b/injectors/mv3/content.js @@ -1 +1,4 @@ -chrome.runtime.sendMessage("pwnme"); +const script = document.createElement("script"); +script.src = chrome.runtime.getURL("shelter-loader.js"); + +document.documentElement.append(script); diff --git a/injectors/mv3/dnr_rules.json b/injectors/mv3/dnr-rules.json similarity index 74% rename from injectors/mv3/dnr_rules.json rename to injectors/mv3/dnr-rules.json index 92c3cc8..828fabf 100644 --- a/injectors/mv3/dnr_rules.json +++ b/injectors/mv3/dnr-rules.json @@ -12,8 +12,7 @@ ] }, "condition": { - "regexFilter": "https?://discord.com/.*", - "resourceTypes": ["main_frame"] + "resourceTypes": ["main_frame", "sub_frame"] } } ] diff --git a/injectors/mv3/loader.js b/injectors/mv3/loader.js deleted file mode 100644 index f76d902..0000000 --- a/injectors/mv3/loader.js +++ /dev/null @@ -1,57 +0,0 @@ -// shoutouts to Phorcys -// https://github.com/CumcordLoaders/Browser/blob/main/mv3/src/loader.js - -function patchFetch() { - window.fetch = async (...args) => { - const url = args[0] instanceof URL ? args[0].href : args[0]; - const result = await communicate({ type: "fetch", url, options: args[1] ?? {} }, "/extid/"); - - if (result.error) throw new (window[result.error.type] ?? Error)(result.error.text); - const headers = new Headers(result.headers ?? {}); - - let res = new Response(result.text, result.init); - Object.defineProperties(res, { - url: { - value: url, - writable: false, - }, - - headers: { - value: headers, - writable: false, - }, - }); - - return res; - }; -} - -async function communicate(data, extid, timeout = 2000) { - let port = chrome.runtime.connect(extid, { name: (Math.random() + 1).toString(36).substring(7) }); - - return new Promise((resolve, reject) => { - let listener = (msg) => { - port.onMessage.removeListener(listener); - port.disconnect(); - return resolve(msg); - }; - - port.onMessage.addListener(listener); - port.postMessage(data); - - if (timeout !== Infinity) { - setTimeout(() => { - port.onMessage.removeListener(listener); - port.disconnect(); - reject(new Error("Request timed out.")); - }, timeout); - } - }); -} - -// We remove CSP for discord.com/* via DNR -// however keep this here just to be sure ;) -patchFetch(); - -// This gets replaced by the worker at runtime -/* INJECT_SHELTER_SOURCE */ diff --git a/injectors/mv3/manifest.json b/injectors/mv3/manifest.json index 372aa63..7e3d8b0 100644 --- a/injectors/mv3/manifest.json +++ b/injectors/mv3/manifest.json @@ -1,31 +1,31 @@ { "name": "Shelter MV3 Inj", - "version": "1.1", + "version": "1.2", "homepage_url": "https://github.com/uwu/shelter", "description": "Injects shelter on discord.com/app - for MV3 browsers", "manifest_version": 3, - "permissions": ["debugger", "storage", "declarativeNetRequestWithHostAccess"], - "host_permissions": ["https://*/*"], - "background": { - "service_worker": "worker.js" - }, + "permissions": ["declarativeNetRequest"], + "host_permissions": ["*://*.discord.com/*"], "content_scripts": [ { - "matches": ["https://discord.com/*", "https://*.discord.com/*"], + "matches": ["*://*.discord.com/*"], "js": ["content.js"], "run_at": "document_start" } ], "icons": {}, - "externally_connectable": { - "matches": ["https://discord.com/*", "https://*.discord.com/*"] - }, + "web_accessible_resources": [ + { + "resources": ["shelter-loader.js"], + "matches": ["*://*.discord.com/*"] + } + ], "declarative_net_request": { "rule_resources": [ { "id": "discord_nocsp", "enabled": true, - "path": "dnr_rules.json" + "path": "dnr-rules.json" } ] } diff --git a/injectors/mv3/shelter-loader.js b/injectors/mv3/shelter-loader.js new file mode 100644 index 0000000..11350a5 --- /dev/null +++ b/injectors/mv3/shelter-loader.js @@ -0,0 +1,27 @@ +const SHELTER_URL = "https://raw.githubusercontent.com/uwu/shelter-builds/main/shelter.js"; + +// discord removes localStorage later, so hold onto it +const localStorage = window.localStorage; + +async function fetchShelter() { + const shelter = await (await fetch(SHELTER_URL)).text(); + return `${shelter}\n//# sourceMappingURL=${SHELTER_URL}.map`; +} + +async function initShelter() { + const localShelter = localStorage.getItem("shelter-bundle"); + + if (localShelter) { + const script = document.createElement("script"); + script.innerText = localShelter; + document.documentElement.append(script); + } + + const remoteShelter = await fetchShelter(); + if (remoteShelter === localShelter) return; + + localStorage.setItem("shelter-bundle", remoteShelter); + window.location.reload(); +} + +initShelter(); diff --git a/injectors/mv3/worker.js b/injectors/mv3/worker.js deleted file mode 100644 index a7a86bb..0000000 --- a/injectors/mv3/worker.js +++ /dev/null @@ -1,111 +0,0 @@ -const SHELTER_URL = "https://raw.githubusercontent.com/uwu/shelter-builds/main/shelter.js"; -async function fetchShelter() { - const shelter = await (await fetch(SHELTER_URL)).text(); - - return `${shelter} -//# sourceMappingURL=${SHELTER_URL}.map`; -} - -// why does chrome have to be like this? -// firefox does it fine, be like firefox. -const promisifiedGet = (...a) => new Promise((res) => chrome.storage.local.get(...a, res)); - -async function updateShelter(tabId) { - const [{ shelter: existingShelter }, newShelter] = await Promise.all([promisifiedGet("shelter"), fetchShelter()]); - - if (existingShelter === newShelter) return; - - await chrome.storage.local.set({ shelter: newShelter }); - - inject(tabId, `location.reload()`); -} - -// shoutouts to Phorcys -// https://github.com/CumcordLoaders/Browser/blob/main/mv3/src/worker.js - -function inject(tabId, code, detachOnFinish = true) { - chrome.debugger.attach( - { - tabId, - }, - "1.3", - () => { - chrome.debugger.sendCommand( - { - tabId, - }, - "Runtime.evaluate", - { - expression: code, - allowUnsafeEvalBlockedByCSP: true, - }, - () => { - if (detachOnFinish) chrome.debugger.detach({ tabId }); - }, - ); - }, - ); -} - -chrome.runtime.onMessage.addListener(async (msg, sender, sendResponse) => { - sendResponse(); - - if (msg === "pwnme" && new URL(sender.tab.url).pathname !== "/") { - const loader = await (await fetch(chrome.runtime.getURL("loader.js"))).text(); - const { shelter } = await promisifiedGet("shelter"); - - if (shelter) { - // replace needs a function because dist usually contains "$&" which puts doit() in random places and produces syntax errors - inject( - sender.tab.id, - loader.replace("/extid/", chrome.runtime.id).replace("/* INJECT_SHELTER_SOURCE */", () => shelter), - ); - } - - await updateShelter(sender.tab.id); - } -}); - -chrome.runtime.onConnectExternal.addListener((port) => { - function messageHandler(msg) { - if (msg.type) { - if (msg.type === "fetch" && msg.url) { - fetch(msg.url, msg.options) - .then(async (res) => { - if (port.onMessage.hasListener(messageHandler)) { - port.postMessage({ - text: await res.text(), - headers: Object.fromEntries([...res.headers]), - - init: { - status: res.status, - statusText: res.statusText, - }, - }); - } - }) - .catch((e) => { - let error = e.toString().split(": "); - - if (port.onMessage.hasListener(messageHandler)) { - port.postMessage({ - error: { - type: error[0], - text: error[1], - }, - }); - } - }); - } - } - } - - async function disconnectHandler() { - port.onMessage.removeListener(messageHandler); - port.disconnect(); - port.onDisconnect.removeListener(disconnectHandler); - } - - port.onMessage.addListener(messageHandler); - port.onDisconnect.addListener(disconnectHandler); -});