From 507d1b1a52b7242da10b69616329bbc2ad31d3f4 Mon Sep 17 00:00:00 2001 From: Dominik Hebeler <dominik@suma-ev.de> Date: Wed, 13 Mar 2024 17:15:27 +0100 Subject: [PATCH] handle request/response headers --- _locales/en/messages.json | 2 +- build/js/Cookie.js | 46 +++++++++++++ build/js/SettingsManager.js | 132 ++++++++++++++++++++++++++++++++++++ build/js/app.js | 15 +--- build/js/contentupdater.js | 14 ---- manifest.json | 4 +- package-lock.json | 9 +++ package.json | 3 +- web/settings/index.js | 2 +- 9 files changed, 197 insertions(+), 30 deletions(-) create mode 100644 build/js/Cookie.js create mode 100644 build/js/SettingsManager.js delete mode 100644 build/js/contentupdater.js diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 05207e6..a0a764d 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -20,7 +20,7 @@ "description": "Heading for Setting storage by plugin" }, "settings_store_settings_description": { - "message": "MetaGer search settings are typically stored in your browser with the help of cookies which leads to problems with settings being deleted when clearing the browser history. If you use this feature all settings will be stored in this extension instead and Cookies are not required anymore. Allow this extension to run in private browsing sessions to also apply your settings there.", + "message": "MetaGer search settings are typically stored in your browser with the help of cookies which leads to problems with settings being deleted when clearing the browser history. If you enable this feature all settings will be stored in this extension instead and Cookies are not required anymore. Allow this extension to run in private browsing sessions in order to also apply your settings there.", "description": "Heading for Setting storage by plugin" }, "settings_store_settings_sync": { diff --git a/build/js/Cookie.js b/build/js/Cookie.js new file mode 100644 index 0000000..f0e9b02 --- /dev/null +++ b/build/js/Cookie.js @@ -0,0 +1,46 @@ +export class Cookie { + key = null; + value = null; + expired = false; + constructor(cookieString) { + // Return an empty object if cookieString + // is empty + if (cookieString === "") + throw new Error("Cannot parse cookie string " + cookieString); + + // Get each individual key-value pairs + // from the cookie string + // This returns a new array + let pairs = cookieString.split(";"); + + // Separate keys from values in each pair string + // Returns a new array which looks like + // [[key1,value1], [key2,value2], ...] + let splittedPairs = pairs.map(cookie => cookie.split("=")); + + + let expiration = null; + for (const [index, pair] of splittedPairs.entries()) { + if (index == 0) { + this.key = pair[0].trim(); + this.value = pair[1].trim(); + } else { + let key = pair[0].trim(); + if (key.match(/^expires$/i) && this.expiration == null) { + expiration = new Date(pair[1].trim()); + } else if (key.match(/^max-age$/i)) { + let maxAge = parseInt(pair[1].trim()); + if (maxAge == 0) { + this.expired = true; + } else { + expiration = new Date((new Date()).getTime() + maxAge * 1000); + } + } + } + }; + + if (expiration != null && Date.now() > expiration.getTime()) { + this.expired = true; + } + } +} \ No newline at end of file diff --git a/build/js/SettingsManager.js b/build/js/SettingsManager.js new file mode 100644 index 0000000..5bf522d --- /dev/null +++ b/build/js/SettingsManager.js @@ -0,0 +1,132 @@ +import { Cookie } from "./Cookie"; + +export class SettingsManager { + + _responseListener = this._handleResponseHeaders.bind(this); + _requestListener = this._handleRequestHeaders.bind(this); + + settings = {}; + + constructor() { + // Register event handler for response headers on our domains + browser.storage.sync.get({ settings_store: false }).then(stored_settings => { + if (stored_settings.settings_store == true) { + this._toggleRequestListener(true); + this._toggleResponseListener(true); + } + }) + + // Listen for changes to the setting storage setting + browser.storage.onChanged.addListener((changes, areaName) => { + if (changes.hasOwnProperty("settings_store")) { + let new_value = changes.settings_store.newValue; + this._toggleResponseListener(new_value); + this._toggleRequestListener(new_value); + } + }); + + + } + + _toggleRequestListener(enabled = false) { + if (enabled) { + browser.webRequest.onBeforeSendHeaders.addListener( + this._requestListener, + { + urls: [ + "https://metager.org/*", + "https://metager.de/*" + ] + }, + ["blocking", "requestHeaders"], + ); + } else { + browser.webRequest.onBeforeSendHeaders.removeListener(this._requestListener); + } + } + + _toggleResponseListener(enabled = false) { + if (enabled) { + browser.webRequest.onHeadersReceived.addListener( + this._responseListener, + { + urls: [ + "https://metager.org/*", + "https://metager.de/*" + ] + }, + ["blocking", "responseHeaders"], + ); + } else { + browser.webRequest.onHeadersReceived.removeListener(this._responseListener); + } + } + + _handleRequestHeaders(details) { + let cookies = []; + + let headers = []; + // Include any possibly existing cookies + for (let header of details.requestHeaders) { + if (header.name.match(/cookie/i)) { + let existing_cookies = header.value.split(";"); + for (let cookie of existing_cookies) { + cookies.push(cookie.trim()); + } + } else { + headers.push(header); + } + } + + for (let key in this.settings) { + cookies.push(`${key}=${encodeURIComponent(this.settings[key])}`); + } + + if (cookies.length > 0) { + let cookie_header = { + name: "Cookie", + value: cookies.join("; ") + }; + headers.push(cookie_header); + + } + return { requestHeaders: headers } + } + + /** + * + * @param {*} details + */ + _handleResponseHeaders(details) { + if (details.hasOwnProperty("responseHeaders")) { + let new_headers = []; // Cookie Array without set-cookie + let settings_changed = false; + for (const header of details.responseHeaders) { + if (header.name.match(/set-cookie/i)) { + let cookie_responses = header.value.split("\n"); + let new_cookies = []; + for (let cookie of cookie_responses) { + let response_cookie = new Cookie(cookie); + if (response_cookie.expired) { + new_cookies.push(cookie); + delete this.settings[response_cookie.key]; + } else { + this.settings[response_cookie.key] = decodeURIComponent(response_cookie.value); + } + } + if (new_cookies.length > 0) { + header.value = new_cookies.join("\n"); + new_headers.push(header); + } + settings_changed = true; + } else { + new_headers.push(header); + } + } + if (settings_changed) { + browser.storage.sync.set({ settings_settings: this.settings }); + } + return { responseHeaders: new_headers }; + } + } +} \ No newline at end of file diff --git a/build/js/app.js b/build/js/app.js index f621352..a6c9060 100644 --- a/build/js/app.js +++ b/build/js/app.js @@ -1,14 +1,5 @@ +const { SettingsManager } = require("./SettingsManager"); + (() => { - /* - const permissionsToRequest = { - permissions: ["webRequest"], - origins: ["https://metager.de/*"] - }; - browser.permissions.request(permissionsToRequest).then(response => { - console.log(response); - browser.webRequest.onBeforeRequest.addListener(details => { - console.log(details.url); - }, { urls: ["https://metager.de/*"] }); - }) -*/ + let settingsManager = new SettingsManager(); })(); \ No newline at end of file diff --git a/build/js/contentupdater.js b/build/js/contentupdater.js deleted file mode 100644 index aa891ef..0000000 --- a/build/js/contentupdater.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Function to modify content of our webpages - * i.e. removes install plugin button as plugin already is installed - */ -export function updateContent() { - removePluginButton(); -} - -/** - * Remove the install plugin button from our webpages - */ -function removePluginButton() { - -} \ No newline at end of file diff --git a/manifest.json b/manifest.json index 5ade9ac..3078455 100644 --- a/manifest.json +++ b/manifest.json @@ -34,6 +34,7 @@ }, "permissions": [ "webRequest", + "webRequestBlocking", "storage" ], "content_scripts": [ @@ -52,7 +53,8 @@ "https://metager.de/*" ], "optional_permissions": [ - "webRequest" + "webRequest", + "webRequestBlocking" ], "background": { "scripts": [ diff --git a/package-lock.json b/package-lock.json index 3018cf0..0c44a63 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,6 +6,7 @@ "": { "dependencies": { "blind-signatures": "^1.0.7", + "cookie": "^0.6.0", "node-rsa": "^1.1.1", "uuid": "^9.0.0" }, @@ -1912,6 +1913,14 @@ "url": "https://github.com/yeoman/configstore?sponsor=1" } }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/core-js": { "version": "3.29.0", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.29.0.tgz", diff --git a/package.json b/package.json index 7e0d184..653d215 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "dependencies": { "blind-signatures": "^1.0.7", + "cookie": "^0.6.0", "node-rsa": "^1.1.1", "uuid": "^9.0.0" }, @@ -12,4 +13,4 @@ "scripts": { "dev": "npm i --clean && webpack --mode=development" } -} \ No newline at end of file +} diff --git a/web/settings/index.js b/web/settings/index.js index 7be0211..bd24bef 100644 --- a/web/settings/index.js +++ b/web/settings/index.js @@ -13,7 +13,7 @@ async function verifyPermissions(request = false) { let permissionsToRequest = { - permissions: ["webRequest"], + permissions: ["webRequestBlocking"], origins: [ "https://metager.org/*", "https://metager.de/*", -- GitLab