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
6042b58adcc1bce50d5fe52bfa060f53752a9436
parent
24c9746fcdc47ba078f0831bdeeb2dd69d41eafd
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2023-02-25 07:25
Merge branch 'saved-rules-2'

Diffstat

M README.md 1 -
M src/bg.js 111 ++++++++++++++++++++++++++++++++++++-------------------------
M src/popup.css 8 ++++++--
M src/popup.html 13 ++++++++-----
M src/popup.js 13 +++++++++++--
M src/settings.css 2 ++
M src/settings.html 3 ++-
M src/settings.js 17 ++++++++++++-----

8 files changed, 107 insertions, 61 deletions


diff --git a/README.md b/README.md

@@ -27,7 +27,6 @@ to allow only those requests you want. Definitely for advanced users.
   27    27 			tried to produce something that works for me rather than copying every
   28    28 			detail.
   29    29 	-	it is not currently possible to block cookies
   30    -1 	-	there is no distinction between temporary and permanent rules
   31    30 	-	the popup is not updated while it is open. You have to close and open it
   32    31 		again to refresh the data.
   33    32 	-	the icon does not show the amount of blocked requests

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

@@ -4,6 +4,7 @@ var lock = Promise.resolve();
    4     4 
    5     5 var STORAGE_DEFAULTS = {
    6     6     'rules': {},
   -1     7     'savedRules': {},
    7     8     'requests': {},
    8     9     'recording': true,
    9    10 };
@@ -33,36 +34,44 @@ var storageChange = function(key, fn) {
   33    34 };
   34    35 
   35    36 var setRule = function(context, hostname, type, rule) {
   36    -1     return storageChange('rules', rules => {
   37    -1         if (hostname === 'first-party') {
   38    -1             context = '*';
   39    -1         }
   40    -1         if (!rules[context]) {
   41    -1             rules[context] = {};
   42    -1         }
   43    -1         if (!rules[context][hostname]) {
   44    -1             rules[context][hostname] = {};
   45    -1         }
   46    -1         if (rule) {
   47    -1             rules[context][hostname][type] = rule;
   48    -1         } else {
   49    -1             delete rules[context][hostname][type];
   50    -1             if (Object.keys(rules[context][hostname]).length === 0) {
   51    -1                 delete rules[context][hostname];
   -1    37     return storageGet('savedRules').then(savedRules => {
   -1    38         return storageChange('rules', rules => {
   -1    39             if (hostname === 'first-party') {
   -1    40                 context = '*';
   52    41             }
   53    -1             if (Object.keys(rules[context]).length === 0) {
   54    -1                 delete rules[context];
   -1    42             if (!rules[context]) {
   -1    43                 rules[context] = savedRules[context] || {};
   55    44             }
   56    -1         }
   57    -1         return rules;
   -1    45             if (!rules[context][hostname]) {
   -1    46                 rules[context][hostname] = {};
   -1    47             }
   -1    48             if (rule) {
   -1    49                 rules[context][hostname][type] = rule;
   -1    50             } else {
   -1    51                 delete rules[context][hostname][type];
   -1    52                 if (Object.keys(rules[context][hostname]).length === 0) {
   -1    53                     delete rules[context][hostname];
   -1    54                 }
   -1    55                 if (Object.keys(rules[context]).length === 0 && !savedRules[context]) {
   -1    56                     delete rules[context];
   -1    57                 }
   -1    58             }
   -1    59             return rules;
   -1    60         });
   58    61     });
   59    62 };
   60    63 
   61    -1 var restrictRules = function(rules, context) {
   62    -1     var restricted = {};
   63    -1     restricted['*'] = rules['*'] || {};
   64    -1     restricted[context] = rules[context] || {};
   65    -1     return restricted;
   -1    64 var getRules = function(context) {
   -1    65     return Promise.all([
   -1    66         storageGet('rules'),
   -1    67         storageGet('savedRules'),
   -1    68     ]).then(([rules, savedRules]) => {
   -1    69         var restricted = {};
   -1    70         restricted['*'] = rules['*'] || savedRules['*'] || {};
   -1    71         restricted[context] = rules[context] || savedRules[context] || {};
   -1    72         restricted.dirty = !!rules[context];
   -1    73         return restricted;
   -1    74     });
   66    75 };
   67    76 
   68    77 var pushRequest = function(tabId, hostname, type) {
@@ -103,19 +112,20 @@ var getCurrentTab = function() {
  103   112 
  104   113 browser.runtime.onMessage.addListener((msg, sender) => {
  105   114     if (msg.type === 'get') {
  106    -1         return Promise.all([
  107    -1             getCurrentTab(),
  108    -1             storageGet('rules'),
  109    -1             storageGet('requests'),
  110    -1             storageGet('recording'),
  111    -1         ]).then(([tab, rules, requests, recording]) => {
  112    -1             var context = msg.data || getHostname(tab.url);
  113    -1             return {
  114    -1                 context: context,
  115    -1                 rules: restrictRules(rules, context),
  116    -1                 requests: requests[tab.id] || {},
  117    -1                 recording: recording,
  118    -1             };
   -1   115         return getCurrentTab().then(tab => {
   -1   116             var context = getHostname(tab.url);
   -1   117             return Promise.all([
   -1   118                 getRules(context),
   -1   119                 storageGet('requests'),
   -1   120                 storageGet('recording'),
   -1   121             ]).then(([rules, requests, recording]) => {
   -1   122                 return {
   -1   123                     context: context,
   -1   124                     rules: rules,
   -1   125                     requests: requests[tab.id] || {},
   -1   126                     recording: recording,
   -1   127                 };
   -1   128             });
  119   129         });
  120   130     } else if (msg.type === 'setRule') {
  121   131         return setRule(
@@ -123,9 +133,21 @@ browser.runtime.onMessage.addListener((msg, sender) => {
  123   133             msg.data.hostname,
  124   134             msg.data.type,
  125   135             msg.data.value,
  126    -1         ).then(() => storageGet('rules')).then(rules => {
  127    -1             return restrictRules(rules, msg.data.context);
  128    -1         });
   -1   136         ).then(() => getRules(msg.data.context));
   -1   137     } else if (msg.type === 'commit') {
   -1   138         var r;
   -1   139         return storageChange('rules', rules => {
   -1   140             r = rules[msg.data];
   -1   141             delete rules[msg.data];
   -1   142             return rules;
   -1   143         }).then(() => storageChange('savedRules', savedRules => {
   -1   144             if (Object.keys(r).length === 0) {
   -1   145                 delete savedRules[msg.data];
   -1   146             } else {
   -1   147                 savedRules[msg.data] = r;
   -1   148             }
   -1   149             return savedRules;
   -1   150         }));
  129   151     } else if (msg.type === 'securitypolicyviolation') {
  130   152         return pushRequest(sender.tab.id, 'inline', msg.data);
  131   153     } else if (msg.type === 'toggleRecording') {
@@ -155,7 +177,7 @@ browser.webRequest.onBeforeRequest.addListener(details => {
  155   177 
  156   178     return Promise.all([
  157   179         pushRequest(details.tabId, hostname, type),
  158    -1         storageGet('rules'),
   -1   180         getRules(context),
  159   181     ]).then(([_, rules]) => {
  160   182         if (!shared.shouldAllow(rules, context, hostname, type)) {
  161   183             if (details.type === 'sub_frame') {
@@ -169,12 +191,11 @@ browser.webRequest.onBeforeRequest.addListener(details => {
  169   191 }, {urls: ['<all_urls>']}, ['blocking']);
  170   192 
  171   193 browser.webRequest.onHeadersReceived.addListener(function(details) {
   -1   194     var context = getHostname(details.url);
  172   195     return Promise.all([
  173    -1         storageGet('rules'),
   -1   196         getRules(context),
  174   197         storageGet('recording'),
  175   198     ]).then(([rules, recording]) => {
  176    -1         var context = getHostname(details.url);
  177    -1 
  178   199         var csp = (type, value) => {
  179   200             var name = 'Content-Security-Policy';
  180   201             if (shared.shouldAllow(rules, context, 'inline', type)) {

diff --git a/src/popup.css b/src/popup.css

@@ -6,8 +6,12 @@
    6     6 	--text-dark: #fff;
    7     7 }
    8     8 
    9    -1 [name="settings"] {
   10    -1 	float: right;
   -1     9 .toolbar {
   -1    10 	display: flex;
   -1    11 	gap: 0.5em;
   -1    12 }
   -1    13 .toolbar label {
   -1    14 	flex-grow: 1;
   11    15 }
   12    16 
   13    17 table {

diff --git a/src/popup.html b/src/popup.html

@@ -6,11 +6,14 @@
    6     6 </head>
    7     7 <body>
    8     8 	<table></table>
    9    -1 	<label>
   10    -1 		<input type="checkbox" name="recording">
   11    -1 		recording
   12    -1 	</label>
   13    -1 	<button type="button" name="settings">Edit rules</button>
   -1     9 	<div class="toolbar">
   -1    10 		<label>
   -1    11 			<input type="checkbox" name="recording">
   -1    12 			recording
   -1    13 		</label>
   -1    14 		<button type="button" name="commit" disabled>Commit</button>
   -1    15 		<button type="button" name="settings">Edit rules</button>
   -1    16 	</div>
   14    17 	<details>
   15    18 		<summary>Help</summary>
   16    19 		<p>In the table above, the columns represent different types of requests. The rows represent domains. Numbers (if recording is enabled) show how many requests of a given type the current tab tries to make to the given domain. Red cells are blocked, green cells are allowed. Light green cells are allowed indirectly, e.g. because they represent a sub-domain of an allowed domain. Grey cells are disabled.</p>

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

@@ -6,6 +6,7 @@ var rules;
    6     6 
    7     7 var table = document.querySelector('table');
    8     8 var recording = document.querySelector('[name="recording"]')
   -1     9 var commitButton = document.querySelector('[name="commit"]');
    9    10 
   10    11 var sendMessage = function(type, data) {
   11    12     return browser.runtime.sendMessage({type: type, data: data});
@@ -81,6 +82,7 @@ var createCheckbox = function(hostname, type) {
   81    82             value: input.checked,
   82    83         }).then(newRules => {
   83    84             rules = newRules;
   -1    85             commitButton.disabled = !rules.dirty;
   84    86             updateInherit(type);
   85    87         });
   86    88     };
@@ -129,12 +131,13 @@ var createRow = function(hostname) {
  129   131     return tr;
  130   132 };
  131   133 
  132    -1 var loadContext = function(c) {
  133    -1     sendMessage('get', c).then(data => {
   -1   134 var loadContext = function() {
   -1   135     sendMessage('get').then(data => {
  134   136         context = data.context;
  135   137         requests = data.requests;
  136   138         rules = data.rules;
  137   139         recording.checked = data.recording;
   -1   140         commitButton.disabled = !rules.dirty;
  138   141 
  139   142         table.innerHTML = '';
  140   143         table.append(createHeader());
@@ -162,3 +165,9 @@ document.addEventListener('DOMContentLoaded', () => {
  162   165 recording.addEventListener('change', event => {
  163   166     sendMessage('toggleRecording');
  164   167 });
   -1   168 
   -1   169 commitButton.addEventListener('click', event => {
   -1   170     sendMessage('commit', context).then(() => {
   -1   171         commitButton.disabled = true;
   -1   172     });
   -1   173 });

diff --git a/src/settings.css b/src/settings.css

@@ -12,6 +12,7 @@ form {
   12    12 	height: 100vh;
   13    13 	display: grid;
   14    14 	grid-template-rows: 1fr min-content;
   -1    15 	grid-template-columns: 1fr 1fr;
   15    16 	grid-gap: 0.5em;
   16    17 	padding: 0.5em;
   17    18 }
@@ -24,4 +25,5 @@ textarea {
   24    25 button {
   25    26 	padding: 0.5em 2em;
   26    27 	justify-self: end;
   -1    28 	grid-column: 1 / 3;
   27    29 }

diff --git a/src/settings.html b/src/settings.html

@@ -6,7 +6,8 @@
    6     6 </head>
    7     7 <body>
    8     8 	<form>
    9    -1 		<textarea></textarea>
   -1     9 		<textarea class="rules"></textarea>
   -1    10 		<textarea class="savedRules"></textarea>
   10    11 		<button>Save</button>
   11    12 	</form>
   12    13 	<script src="shared.js"></script>

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

@@ -1,15 +1,22 @@
    1     1 var form = document.querySelector('form');
    2    -1 var textarea = document.querySelector('textarea');
   -1     2 var textarea1 = document.querySelector('textarea.rules');
   -1     3 var textarea2 = document.querySelector('textarea.savedRules');
    3     4 
    4    -1 browser.storage.local.get('rules').then(data => {
   -1     5 browser.storage.local.get(['rules', 'savedRules']).then(data => {
    5     6 	var rules = data.rules || {};
    6    -1 	textarea.value = JSON.stringify(rules, null, 2)
   -1     7 	var savedRules = data.savedRules || {};
   -1     8 	textarea1.value = JSON.stringify(rules, null, 2)
   -1     9 	textarea2.value = JSON.stringify(savedRules, null, 2)
    7    10 });
    8    11 
    9    12 form.addEventListener('submit', event => {
   10    13 	event.preventDefault();
   11    -1 	var rules = JSON.parse(textarea.value);
   12    -1 	browser.storage.local.set({'rules': rules}).then(() => {
   -1    14 	var rules = JSON.parse(textarea1.value);
   -1    15 	var savedRules = JSON.parse(textarea2.value);
   -1    16 	browser.storage.local.set({
   -1    17 		'rules': rules,
   -1    18 		'savedRules': savedRules,
   -1    19 	}).then(() => {
   13    20 		location.reload();
   14    21 	});
   15    22 });