xiMatrix

filter net requests according to source, destination and type  https://addons.mozilla.org/firefox/addon/ximatrix/
git clone https://git.ce9e.org/xiMatrix.git

commit
900fced1b4d7bf350aedef3caaae94d08f54a774
parent
f238c98c2caac2dc63eb0b1138073aa36997460a
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2022-10-15 11:43
avoid state in background page

Diffstat

M manifest.json 3 ++-
M src/bg.js 183 +++++++++++++++++++++++++++++++++----------------------------

2 files changed, 102 insertions, 84 deletions


diff --git a/manifest.json b/manifest.json

@@ -14,7 +14,8 @@
   14    14     "48": "icon.svg"
   15    15   },
   16    16   "background": {
   17    -1     "scripts": ["src/shared.js", "src/bg.js"]
   -1    17     "scripts": ["src/shared.js", "src/bg.js"],
   -1    18     "persistent": false
   18    19   },
   19    20   "content_scripts": [{
   20    21     "js": ["src/content.js"],

diff --git a/src/bg.js b/src/bg.js

@@ -1,38 +1,45 @@
    1     1 /* global browser shared */
    2     2 
    3    -1 var rules = {};
    4    -1 var requests = {};
    5    -1 
    6     3 var getHostname = function(url) {
    7     4     var u = new URL(url);
    8     5     return u.hostname;
    9     6 };
   10     7 
   -1     8 var getRules = function() {
   -1     9     return browser.storage.local.get('rules').then(data => data.rules || {});
   -1    10 };
   -1    11 
   -1    12 var getRequests = function() {
   -1    13     return browser.storage.local.get('requests').then(data => data.requests || {});
   -1    14 };
   -1    15 
   11    16 var setRule = function(context, hostname, type, rule) {
   12    -1     if (hostname === 'first-party') {
   13    -1         context = '*';
   14    -1     }
   15    -1     if (!rules[context]) {
   16    -1         rules[context] = {};
   17    -1     }
   18    -1     if (!rules[context][hostname]) {
   19    -1         rules[context][hostname] = {};
   20    -1     }
   21    -1     if (rule) {
   22    -1         rules[context][hostname][type] = rule;
   23    -1     } else {
   24    -1         delete rules[context][hostname][type];
   25    -1         if (Object.keys(rules[context][hostname]).length === 0) {
   26    -1             delete rules[context][hostname];
   -1    17     return getRules().then(rules => {
   -1    18         if (hostname === 'first-party') {
   -1    19             context = '*';
   27    20         }
   28    -1         if (Object.keys(rules[context]).length === 0) {
   29    -1             delete rules[context];
   -1    21         if (!rules[context]) {
   -1    22             rules[context] = {};
   30    23         }
   31    -1     }
   32    -1     browser.storage.local.set({'rules': rules});
   -1    24         if (!rules[context][hostname]) {
   -1    25             rules[context][hostname] = {};
   -1    26         }
   -1    27         if (rule) {
   -1    28             rules[context][hostname][type] = rule;
   -1    29         } else {
   -1    30             delete rules[context][hostname][type];
   -1    31             if (Object.keys(rules[context][hostname]).length === 0) {
   -1    32                 delete rules[context][hostname];
   -1    33             }
   -1    34             if (Object.keys(rules[context]).length === 0) {
   -1    35                 delete rules[context];
   -1    36             }
   -1    37         }
   -1    38         return browser.storage.local.set({'rules': rules});
   -1    39     });
   33    40 };
   34    41 
   35    -1 var restrictRules = function(context) {
   -1    42 var restrictRules = function(rules, context) {
   36    43     var restricted = {};
   37    44     restricted['*'] = rules['*'] || {};
   38    45     restricted[context] = rules[context] || {};
@@ -40,22 +47,28 @@ var restrictRules = function(context) {
   40    47 };
   41    48 
   42    49 var pushRequest = function(tabId, hostname, type) {
   43    -1     if (!requests[tabId]) {
   44    -1         requests[tabId] = {};
   45    -1     }
   46    -1     if (!requests[tabId][hostname]) {
   47    -1         requests[tabId][hostname] = {};
   48    -1     }
   49    -1     if (!requests[tabId][hostname][type]) {
   50    -1         requests[tabId][hostname][type] = 0;
   51    -1     }
   52    -1     requests[tabId][hostname][type] += 1;
   -1    50     return getRequests().then(requests => {
   -1    51         if (!requests[tabId]) {
   -1    52             requests[tabId] = {};
   -1    53         }
   -1    54         if (!requests[tabId][hostname]) {
   -1    55             requests[tabId][hostname] = {};
   -1    56         }
   -1    57         if (!requests[tabId][hostname][type]) {
   -1    58             requests[tabId][hostname][type] = 0;
   -1    59         }
   -1    60         requests[tabId][hostname][type] += 1;
   -1    61         return browser.storage.local.set({'requests': requests});
   -1    62     });
   53    63 };
   54    64 
   55    65 var clearRequests = function(tabId) {
   56    -1     if (requests[tabId]) {
   57    -1         delete requests[tabId];
   58    -1     }
   -1    66     return getRequests().then(requests => {
   -1    67         if (requests[tabId]) {
   -1    68             delete requests[tabId];
   -1    69         }
   -1    70         return browser.storage.local.set({'requests': requests});
   -1    71     });
   59    72 };
   60    73 
   61    74 var getCurrentTab = function() {
@@ -67,36 +80,39 @@ var getCurrentTab = function() {
   67    80 
   68    81 browser.runtime.onMessage.addListener((msg, sender) => {
   69    82     if (msg.type === 'get') {
   70    -1         return getCurrentTab().then(tab => {
   -1    83         return Promise.all([
   -1    84             getCurrentTab(),
   -1    85             getRules(),
   -1    86             getRequests(),
   -1    87         ]).then(([tab, rules, requests]) => {
   71    88             var context = msg.data || getHostname(tab.url);
   72    89             return {
   73    90                 context: context,
   74    -1                 rules: restrictRules(context),
   -1    91                 rules: restrictRules(rules, context),
   75    92                 requests: requests[tab.id] || {},
   76    93             };
   77    94         });
   78    95     } else if (msg.type === 'setRule') {
   79    -1         setRule(
   -1    96         return setRule(
   80    97             msg.data.context,
   81    98             msg.data.hostname,
   82    99             msg.data.type,
   83   100             msg.data.value,
   84    -1         );
   85    -1         return Promise.resolve(restrictRules(msg.data.context));
   -1   101         ).then(getRules).then(rules => {
   -1   102             return restrictRules(rules, msg.data.context);
   -1   103         });
   86   104     } else if (msg.type === 'getAllRules') {
   87    -1         return Promise.resolve(rules);
   -1   105         return getRules();
   88   106     } else if (msg.type === 'setAllRules') {
   89    -1         rules = msg.data;
   90    -1         browser.storage.local.set({'rules': rules});
   91    -1         return Promise.resolve();
   -1   107         return browser.storage.local.set({'rules': msg.data});
   92   108     } else if (msg.type === 'securitypolicyviolation') {
   93    -1         pushRequest(sender.tab.id, 'inline', msg.data);
   -1   109         return pushRequest(sender.tab.id, 'inline', msg.data);
   94   110     }
   95   111 });
   96   112 
   97   113 browser.tabs.onRemoved.addListener(clearRequests);
   98   114 browser.webNavigation.onBeforeNavigate.addListener(details => {
   99    -1     clearRequests(details.tabId);
   -1   115     return clearRequests(details.tabId);
  100   116 });
  101   117 
  102   118 browser.webRequest.onBeforeRequest.addListener(details => {
@@ -112,49 +128,50 @@ browser.webRequest.onBeforeRequest.addListener(details => {
  112   128     var hostname = getHostname(details.url);
  113   129     var type = shared.TYPE_MAP[details.type] || 'other';
  114   130 
  115    -1     pushRequest(details.tabId, hostname, type);
  116    -1 
  117    -1     if (!shared.shouldAllow(rules, context, hostname, type)) {
  118    -1         if (details.type === 'sub_frame') {
  119    -1             return {redirectUrl: 'data:,' + encodeURIComponent(details.url)};
  120    -1         } else {
  121    -1             return {cancel: true};
   -1   131     return Promise.all([
   -1   132         pushRequest(details.tabId, hostname, type),
   -1   133         getRules(),
   -1   134     ]).then(([_, rules]) => {
   -1   135         if (!shared.shouldAllow(rules, context, hostname, type)) {
   -1   136             if (details.type === 'sub_frame') {
   -1   137                 return {redirectUrl: 'data:,' + encodeURIComponent(details.url)};
   -1   138             } else {
   -1   139                 return {cancel: true};
   -1   140             }
  122   141         }
  123    -1     }
   -1   142     });
  124   143 }, {urls: ['<all_urls>']}, ['blocking']);
  125   144 
  126   145 browser.webRequest.onHeadersReceived.addListener(function(details) {
  127    -1     var context = getHostname(details.url);
  128    -1 
  129    -1     var header = type => {
  130    -1         if (shared.shouldAllow(rules, context, 'inline', type)) {
  131    -1             return 'Content-Security-Policy-Report-Only';
  132    -1         } else {
  133    -1             return 'Content-Security-Policy';
  134    -1         }
  135    -1     };
   -1   146     return getRules().then(rules => {
   -1   147         var context = getHostname(details.url);
   -1   148 
   -1   149         var header = type => {
   -1   150             if (shared.shouldAllow(rules, context, 'inline', type)) {
   -1   151                 return 'Content-Security-Policy-Report-Only';
   -1   152             } else {
   -1   153                 return 'Content-Security-Policy';
   -1   154             }
   -1   155         };
   -1   156 
   -1   157         details.responseHeaders.push({
   -1   158             name: header('css'),
   -1   159             value: "style-src 'self' *",
   -1   160         });
   -1   161         details.responseHeaders.push({
   -1   162             name: header('script'),
   -1   163             value: "script-src 'self' *",
   -1   164         });
   -1   165         details.responseHeaders.push({
   -1   166             name: header('media'),
   -1   167             value: "img-src 'self' *",
   -1   168         });
  136   169 
  137    -1     details.responseHeaders.push({
  138    -1         name: header('css'),
  139    -1         value: "style-src 'self' *",
  140    -1     });
  141    -1     details.responseHeaders.push({
  142    -1         name: header('script'),
  143    -1         value: "script-src 'self' *",
   -1   170         return {
   -1   171             responseHeaders: details.responseHeaders,
   -1   172         };
  144   173     });
  145    -1     details.responseHeaders.push({
  146    -1         name: header('media'),
  147    -1         value: "img-src 'self' *",
  148    -1     });
  149    -1 
  150    -1     return {
  151    -1         responseHeaders: details.responseHeaders,
  152    -1     };
  153   174 }, {
  154   175     urls: ['<all_urls>'],
  155   176     types: ['main_frame'],
  156   177 }, ['blocking', 'responseHeaders']);
  157    -1 
  158    -1 browser.storage.local.get('rules').then(stored => {
  159    -1     rules = stored.rules || {};
  160    -1 });