mirror of
https://github.com/gorhill/uBlock.git
synced 2024-10-06 09:37:12 +02:00
Export the rule-based filtering engines to the nodejs package
The code exported to nodejs package was revised to use modern JavaScript syntax. A few issues were fixed at the same time. The exported classes are: - DynamicHostRuleFiltering - DynamicURLRuleFiltering - DynamicSwitchRuleFiltering These related to the content the of "My rules" pane in the uBlock Origin extension.
This commit is contained in:
parent
cbec7397fc
commit
89c5653bc6
@ -31,7 +31,7 @@ import staticNetFilteringEngine from './static-net-filtering.js';
|
||||
import µb from './background.js';
|
||||
import { FilteringContext } from './filtering-context.js';
|
||||
import { LineIterator } from './text-utils.js';
|
||||
import { sessionFirewall } from './dynamic-net-filtering.js';
|
||||
import { sessionFirewall } from './filtering-engines.js';
|
||||
|
||||
import {
|
||||
domainFromHostname,
|
||||
|
@ -152,13 +152,9 @@ CodeMirror.defineMode('ubo-dynamic-filtering', ( ) => {
|
||||
match = reToken.exec(string);
|
||||
if ( match === null ) { return; }
|
||||
// hostname or url
|
||||
const isURLRule = isSwitchRule === false && match[0].includes('://');
|
||||
const isURLRule = isSwitchRule === false && match[0].indexOf('://') > 0;
|
||||
if ( isURLRule ) {
|
||||
if ( isSwitchRule ) {
|
||||
string = addMatchSlice(match, 'error');
|
||||
} else {
|
||||
string = addMatchSlice(match, sortType === 2 ? 'sortkey' : null);
|
||||
}
|
||||
} else if ( isValidHostname(match[0]) === false ) {
|
||||
string = addMatchSlice(match, 'error');
|
||||
} else if ( sortType === 1 && isSwitchRule || sortType === 2 ) {
|
||||
|
@ -19,14 +19,10 @@
|
||||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* jshint bitwise: false */
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
import '../lib/punycode.js';
|
||||
|
||||
import globals from './globals.js';
|
||||
import { LineIterator } from './text-utils.js';
|
||||
|
||||
@ -37,18 +33,24 @@ import {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const punycode = globals.punycode;
|
||||
const punycode = globals.punycode || undefined;
|
||||
|
||||
const supportedDynamicTypes = {
|
||||
// Object.create(null) is used below to eliminate worries about unexpected
|
||||
// property names in prototype chain -- and this way we don't have to use
|
||||
// hasOwnProperty() to avoid this.
|
||||
|
||||
const supportedDynamicTypes = Object.create(null);
|
||||
Object.assign(supportedDynamicTypes, {
|
||||
'3p': true,
|
||||
'image': true,
|
||||
'inline-script': true,
|
||||
'1p-script': true,
|
||||
'3p-script': true,
|
||||
'3p-frame': true
|
||||
};
|
||||
});
|
||||
|
||||
const typeBitOffsets = {
|
||||
const typeBitOffsets = Object.create(null);
|
||||
Object.assign(typeBitOffsets, {
|
||||
'*': 0,
|
||||
'inline-script': 2,
|
||||
'1p-script': 4,
|
||||
@ -56,27 +58,30 @@ const typeBitOffsets = {
|
||||
'3p-frame': 8,
|
||||
'image': 10,
|
||||
'3p': 12
|
||||
};
|
||||
});
|
||||
|
||||
const actionToNameMap = {
|
||||
'1': 'block',
|
||||
'2': 'allow',
|
||||
'3': 'noop'
|
||||
};
|
||||
|
||||
const nameToActionMap = {
|
||||
const nameToActionMap = Object.create(null);
|
||||
Object.assign(nameToActionMap, {
|
||||
'block': 1,
|
||||
'allow': 2,
|
||||
'noop': 3
|
||||
};
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
const intToActionMap = new Map([
|
||||
[ 1, 'block' ],
|
||||
[ 2, 'allow' ],
|
||||
[ 3, 'noop' ]
|
||||
]);
|
||||
|
||||
// For performance purpose, as simple tests as possible
|
||||
const reBadHostname = /[^0-9a-z_.\[\]:%-]/;
|
||||
const reNotASCII = /[^\x20-\x7F]/;
|
||||
const decomposedSource = [];
|
||||
const decomposedDestination = [];
|
||||
|
||||
const is3rdParty = function(srcHostname, desHostname) {
|
||||
/******************************************************************************/
|
||||
|
||||
function is3rdParty(srcHostname, desHostname) {
|
||||
// If at least one is party-less, the relation can't be labelled
|
||||
// "3rd-party"
|
||||
if ( desHostname === '*' || srcHostname === '*' || srcHostname === '' ) {
|
||||
@ -95,17 +100,16 @@ const is3rdParty = function(srcHostname, desHostname) {
|
||||
// Do not confuse 'example.com' with 'anotherexample.com'
|
||||
return desHostname.length !== srcDomain.length &&
|
||||
desHostname.charAt(desHostname.length - srcDomain.length - 1) !== '.';
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const Matrix = class {
|
||||
class DynamicHostRuleFiltering {
|
||||
|
||||
constructor() {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
|
||||
reset() {
|
||||
this.r = 0;
|
||||
this.type = '';
|
||||
@ -113,11 +117,8 @@ const Matrix = class {
|
||||
this.z = '';
|
||||
this.rules = new Map();
|
||||
this.changed = false;
|
||||
this.decomposedSource = [];
|
||||
this.decomposedDestination = [];
|
||||
}
|
||||
|
||||
|
||||
assign(other) {
|
||||
// Remove rules not in other
|
||||
for ( const k of this.rules.keys() ) {
|
||||
@ -135,7 +136,6 @@ const Matrix = class {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
copyRules(from, srcHostname, desHostnames) {
|
||||
// Specific types
|
||||
let thisBits = this.rules.get('* *');
|
||||
@ -149,7 +149,7 @@ const Matrix = class {
|
||||
this.changed = true;
|
||||
}
|
||||
|
||||
let key = srcHostname + ' *';
|
||||
let key = `${srcHostname} *`;
|
||||
thisBits = this.rules.get(key);
|
||||
fromBits = from.rules.get(key);
|
||||
if ( fromBits !== thisBits ) {
|
||||
@ -163,10 +163,7 @@ const Matrix = class {
|
||||
|
||||
// Specific destinations
|
||||
for ( const desHostname in desHostnames ) {
|
||||
if ( desHostnames.hasOwnProperty(desHostname) === false ) {
|
||||
continue;
|
||||
}
|
||||
key = '* ' + desHostname;
|
||||
key = `* ${desHostname}`;
|
||||
thisBits = this.rules.get(key);
|
||||
fromBits = from.rules.get(key);
|
||||
if ( fromBits !== thisBits ) {
|
||||
@ -177,7 +174,7 @@ const Matrix = class {
|
||||
}
|
||||
this.changed = true;
|
||||
}
|
||||
key = srcHostname + ' ' + desHostname ;
|
||||
key = `${srcHostname} ${desHostname}` ;
|
||||
thisBits = this.rules.get(key);
|
||||
fromBits = from.rules.get(key);
|
||||
if ( fromBits !== thisBits ) {
|
||||
@ -193,7 +190,6 @@ const Matrix = class {
|
||||
return this.changed;
|
||||
}
|
||||
|
||||
|
||||
// - * * type
|
||||
// - from * type
|
||||
// - * to *
|
||||
@ -202,20 +198,16 @@ const Matrix = class {
|
||||
hasSameRules(other, srcHostname, desHostnames) {
|
||||
// Specific types
|
||||
let key = '* *';
|
||||
if ( this.rules.get(key) !== other.rules.get(key) ) {
|
||||
return false;
|
||||
}
|
||||
key = srcHostname + ' *';
|
||||
if ( this.rules.get(key) !== other.rules.get(key) ) {
|
||||
return false;
|
||||
}
|
||||
if ( this.rules.get(key) !== other.rules.get(key) ) { return false; }
|
||||
key = `${srcHostname} *`;
|
||||
if ( this.rules.get(key) !== other.rules.get(key) ) { return false; }
|
||||
// Specific destinations
|
||||
for ( const desHostname in desHostnames ) {
|
||||
key = '* ' + desHostname;
|
||||
key = `* ${desHostname}`;
|
||||
if ( this.rules.get(key) !== other.rules.get(key) ) {
|
||||
return false;
|
||||
}
|
||||
key = srcHostname + ' ' + desHostname ;
|
||||
key = `${srcHostname} ${desHostname}`;
|
||||
if ( this.rules.get(key) !== other.rules.get(key) ) {
|
||||
return false;
|
||||
}
|
||||
@ -223,15 +215,12 @@ const Matrix = class {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
setCell(srcHostname, desHostname, type, state) {
|
||||
const bitOffset = typeBitOffsets[type];
|
||||
const k = srcHostname + ' ' + desHostname;
|
||||
const k = `${srcHostname} ${desHostname}`;
|
||||
const oldBitmap = this.rules.get(k) || 0;
|
||||
const newBitmap = oldBitmap & ~(3 << bitOffset) | (state << bitOffset);
|
||||
if ( newBitmap === oldBitmap ) {
|
||||
return false;
|
||||
}
|
||||
if ( newBitmap === oldBitmap ) { return false; }
|
||||
if ( newBitmap === 0 ) {
|
||||
this.rules.delete(k);
|
||||
} else {
|
||||
@ -241,54 +230,44 @@ const Matrix = class {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
unsetCell(srcHostname, desHostname, type) {
|
||||
this.evaluateCellZY(srcHostname, desHostname, type);
|
||||
if ( this.r === 0 ) {
|
||||
return false;
|
||||
}
|
||||
if ( this.r === 0 ) { return false; }
|
||||
this.setCell(srcHostname, desHostname, type, 0);
|
||||
this.changed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
evaluateCell(srcHostname, desHostname, type) {
|
||||
const key = srcHostname + ' ' + desHostname;
|
||||
const key = `${srcHostname} ${desHostname}`;
|
||||
const bitmap = this.rules.get(key);
|
||||
if ( bitmap === undefined ) { return 0; }
|
||||
return bitmap >> typeBitOffsets[type] & 3;
|
||||
}
|
||||
|
||||
|
||||
clearRegisters() {
|
||||
this.r = 0;
|
||||
this.type = this.y = this.z = '';
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
evaluateCellZ(srcHostname, desHostname, type) {
|
||||
decomposeHostname(srcHostname, this.decomposedSource);
|
||||
decomposeHostname(srcHostname, decomposedSource);
|
||||
this.type = type;
|
||||
const bitOffset = typeBitOffsets[type];
|
||||
for ( const shn of this.decomposedSource ) {
|
||||
this.z = shn;
|
||||
let v = this.rules.get(shn + ' ' + desHostname);
|
||||
if ( v !== undefined ) {
|
||||
for ( const srchn of decomposedSource ) {
|
||||
this.z = srchn;
|
||||
let v = this.rules.get(`${srchn} ${desHostname}`);
|
||||
if ( v === undefined ) { continue; }
|
||||
v = v >>> bitOffset & 3;
|
||||
if ( v !== 0 ) {
|
||||
this.r = v;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
if ( v === 0 ) { continue; }
|
||||
return (this.r = v);
|
||||
}
|
||||
// srcHostname is '*' at this point
|
||||
this.r = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
evaluateCellZY(srcHostname, desHostname, type) {
|
||||
// Pathological cases.
|
||||
if ( desHostname === '' ) {
|
||||
@ -299,11 +278,11 @@ const Matrix = class {
|
||||
// Precedence: from most specific to least specific
|
||||
|
||||
// Specific-destination, any party, any type
|
||||
decomposeHostname(desHostname, this.decomposedDestination);
|
||||
for ( const dhn of this.decomposedDestination ) {
|
||||
if ( dhn === '*' ) { break; }
|
||||
this.y = dhn;
|
||||
if ( this.evaluateCellZ(srcHostname, dhn, '*') !== 0 ) {
|
||||
decomposeHostname(desHostname, decomposedDestination);
|
||||
for ( const deshn of decomposedDestination ) {
|
||||
if ( deshn === '*' ) { break; }
|
||||
this.y = deshn;
|
||||
if ( this.evaluateCellZ(srcHostname, deshn, '*') !== 0 ) {
|
||||
return this.r;
|
||||
}
|
||||
}
|
||||
@ -338,7 +317,7 @@ const Matrix = class {
|
||||
}
|
||||
|
||||
// Any destination, any party, specific type
|
||||
if ( supportedDynamicTypes.hasOwnProperty(type) ) {
|
||||
if ( supportedDynamicTypes[type] !== undefined ) {
|
||||
if ( this.evaluateCellZ(srcHostname, '*', type) !== 0 ) {
|
||||
return this.r;
|
||||
}
|
||||
@ -353,87 +332,72 @@ const Matrix = class {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
mustAllowCellZY(srcHostname, desHostname, type) {
|
||||
return this.evaluateCellZY(srcHostname, desHostname, type) === 2;
|
||||
}
|
||||
|
||||
|
||||
mustBlockOrAllow() {
|
||||
return this.r === 1 || this.r === 2;
|
||||
}
|
||||
|
||||
|
||||
mustBlock() {
|
||||
return this.r === 1;
|
||||
}
|
||||
|
||||
|
||||
mustAbort() {
|
||||
return this.r === 3;
|
||||
}
|
||||
|
||||
|
||||
lookupRuleData(src, des, type) {
|
||||
const r = this.evaluateCellZY(src, des, type);
|
||||
if ( r === 0 ) { return; }
|
||||
return `${this.z} ${this.y} ${this.type} ${r}`;
|
||||
}
|
||||
|
||||
|
||||
toLogData() {
|
||||
if ( this.r === 0 || this.type === '' ) { return; }
|
||||
return {
|
||||
source: 'dynamicHost',
|
||||
result: this.r,
|
||||
raw: `${this.z} ${this.y} ${this.type} ${this.intToActionMap.get(this.r)}`
|
||||
raw: `${this.z} ${this.y} ${this.type} ${intToActionMap.get(this.r)}`
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
srcHostnameFromRule(rule) {
|
||||
return rule.slice(0, rule.indexOf(' '));
|
||||
}
|
||||
|
||||
|
||||
desHostnameFromRule(rule) {
|
||||
return rule.slice(rule.indexOf(' ') + 1);
|
||||
}
|
||||
|
||||
|
||||
toArray() {
|
||||
const out = [],
|
||||
toUnicode = punycode.toUnicode;
|
||||
const out = [];
|
||||
for ( const key of this.rules.keys() ) {
|
||||
let srcHostname = this.srcHostnameFromRule(key);
|
||||
let desHostname = this.desHostnameFromRule(key);
|
||||
const srchn = this.srcHostnameFromRule(key);
|
||||
const deshn = this.desHostnameFromRule(key);
|
||||
const srchnPretty = srchn.includes('xn--') && punycode
|
||||
? punycode.toUnicode(srchn)
|
||||
: srchn;
|
||||
const deshnPretty = deshn.includes('xn--') && punycode
|
||||
? punycode.toUnicode(deshn)
|
||||
: deshn;
|
||||
for ( const type in typeBitOffsets ) {
|
||||
if ( typeBitOffsets.hasOwnProperty(type) === false ) { continue; }
|
||||
const val = this.evaluateCell(srcHostname, desHostname, type);
|
||||
if ( typeBitOffsets[type] === undefined ) { continue; }
|
||||
const val = this.evaluateCell(srchn, deshn, type);
|
||||
if ( val === 0 ) { continue; }
|
||||
if ( srcHostname.indexOf('xn--') !== -1 ) {
|
||||
srcHostname = toUnicode(srcHostname);
|
||||
}
|
||||
if ( desHostname.indexOf('xn--') !== -1 ) {
|
||||
desHostname = toUnicode(desHostname);
|
||||
}
|
||||
out.push(
|
||||
srcHostname + ' ' +
|
||||
desHostname + ' ' +
|
||||
type + ' ' +
|
||||
actionToNameMap[val]
|
||||
);
|
||||
const action = intToActionMap.get(val);
|
||||
if ( action === undefined ) { continue; }
|
||||
out.push(`${srchnPretty} ${deshnPretty} ${type} ${action}`);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
toString() {
|
||||
return this.toArray().join('\n');
|
||||
}
|
||||
|
||||
|
||||
fromString(text, append) {
|
||||
const lineIter = new LineIterator(text);
|
||||
if ( append !== true ) { this.reset(); }
|
||||
@ -442,7 +406,6 @@ const Matrix = class {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
validateRuleParts(parts) {
|
||||
if ( parts.length < 4 ) { return; }
|
||||
|
||||
@ -450,19 +413,25 @@ const Matrix = class {
|
||||
if ( parts[0].endsWith(':') ) { return; }
|
||||
|
||||
// Ignore URL-based rules
|
||||
if ( parts[1].indexOf('/') !== -1 ) { return; }
|
||||
if ( parts[1].includes('/') ) { return; }
|
||||
|
||||
if ( typeBitOffsets.hasOwnProperty(parts[2]) === false ) { return; }
|
||||
if ( typeBitOffsets[parts[2]] === undefined ) { return; }
|
||||
|
||||
if ( nameToActionMap.hasOwnProperty(parts[3]) === false ) { return; }
|
||||
if ( nameToActionMap[parts[3]] === undefined ) { return; }
|
||||
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/840
|
||||
// Discard invalid rules
|
||||
if ( parts[1] !== '*' && parts[2] !== '*' ) { return; }
|
||||
|
||||
// Performance: avoid punycoding if hostnames are made only of ASCII chars.
|
||||
if ( reNotASCII.test(parts[0]) ) { parts[0] = punycode.toASCII(parts[0]); }
|
||||
if ( reNotASCII.test(parts[1]) ) { parts[1] = punycode.toASCII(parts[1]); }
|
||||
// Performance: avoid punycoding when only ASCII chars
|
||||
if ( punycode !== undefined ) {
|
||||
if ( reNotASCII.test(parts[0]) ) {
|
||||
parts[0] = punycode.toASCII(parts[0]);
|
||||
}
|
||||
if ( reNotASCII.test(parts[1]) ) {
|
||||
parts[1] = punycode.toASCII(parts[1]);
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/1082
|
||||
// Discard rules with invalid hostnames
|
||||
@ -476,7 +445,6 @@ const Matrix = class {
|
||||
return parts;
|
||||
}
|
||||
|
||||
|
||||
addFromRuleParts(parts) {
|
||||
if ( this.validateRuleParts(parts) !== undefined ) {
|
||||
this.setCell(parts[0], parts[1], parts[2], nameToActionMap[parts[3]]);
|
||||
@ -485,7 +453,6 @@ const Matrix = class {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
removeFromRuleParts(parts) {
|
||||
if ( this.validateRuleParts(parts) !== undefined ) {
|
||||
this.setCell(parts[0], parts[1], parts[2], 0);
|
||||
@ -494,7 +461,6 @@ const Matrix = class {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
toSelfie() {
|
||||
return {
|
||||
magicId: this.magicId,
|
||||
@ -502,28 +468,18 @@ const Matrix = class {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
fromSelfie(selfie) {
|
||||
if ( selfie.magicId !== this.magicId ) { return false; }
|
||||
this.rules = new Map(selfie.rules);
|
||||
this.changed = true;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Matrix.prototype.intToActionMap = new Map([
|
||||
[ 1, 'block' ],
|
||||
[ 2, 'allow' ],
|
||||
[ 3, 'noop' ]
|
||||
]);
|
||||
|
||||
Matrix.prototype.magicId = 1;
|
||||
DynamicHostRuleFiltering.prototype.magicId = 1;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const sessionFirewall = new Matrix();
|
||||
const permanentFirewall = new Matrix();
|
||||
|
||||
export { permanentFirewall, sessionFirewall };
|
||||
export default DynamicHostRuleFiltering;
|
||||
|
||||
/******************************************************************************/
|
||||
|
50
src/js/filtering-engines.js
Normal file
50
src/js/filtering-engines.js
Normal file
@ -0,0 +1,50 @@
|
||||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a browser extension to block requests.
|
||||
Copyright (C) 2014-present Raymond Hill
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
|
||||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
import DynamicHostRuleFiltering from './dynamic-net-filtering.js';
|
||||
import DynamicSwitchRuleFiltering from './hnswitches.js';
|
||||
import DynamicURLRuleFiltering from './url-net-filtering.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const permanentFirewall = new DynamicHostRuleFiltering();
|
||||
const sessionFirewall = new DynamicHostRuleFiltering();
|
||||
|
||||
const permanentURLFiltering = new DynamicURLRuleFiltering();
|
||||
const sessionURLFiltering = new DynamicURLRuleFiltering();
|
||||
|
||||
const permanentSwitches = new DynamicSwitchRuleFiltering();
|
||||
const sessionSwitches = new DynamicSwitchRuleFiltering();
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
export {
|
||||
permanentFirewall,
|
||||
sessionFirewall,
|
||||
permanentURLFiltering,
|
||||
sessionURLFiltering,
|
||||
permanentSwitches,
|
||||
sessionSwitches,
|
||||
};
|
@ -25,25 +25,21 @@
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
import '../lib/punycode.js';
|
||||
|
||||
import globals from './globals.js';
|
||||
import { LineIterator } from './text-utils.js';
|
||||
import { decomposeHostname } from './uri-utils.js';
|
||||
import { LineIterator } from './text-utils.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const punycode = globals.punycode;
|
||||
const punycode = globals.punycode || undefined;
|
||||
const decomposedSource = [];
|
||||
|
||||
/******************************************************************************/
|
||||
// Object.create(null) is used below to eliminate worries about unexpected
|
||||
// property names in prototype chain -- and this way we don't have to use
|
||||
// hasOwnProperty() to avoid this.
|
||||
|
||||
const HnSwitches = function() {
|
||||
this.reset();
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const switchBitOffsets = {
|
||||
const switchBitOffsets = Object.create(null);
|
||||
Object.assign(switchBitOffsets, {
|
||||
'no-strict-blocking': 0,
|
||||
'no-popups': 2,
|
||||
'no-cosmetic-filtering': 4,
|
||||
@ -51,23 +47,25 @@ const switchBitOffsets = {
|
||||
'no-large-media': 8,
|
||||
'no-csp-reports': 10,
|
||||
'no-scripting': 12,
|
||||
};
|
||||
});
|
||||
|
||||
const switchStateToNameMap = {
|
||||
const switchStateToNameMap = Object.create(null);
|
||||
Object.assign(switchStateToNameMap, {
|
||||
'1': 'true',
|
||||
'2': 'false'
|
||||
};
|
||||
'2': 'false',
|
||||
});
|
||||
|
||||
const nameToSwitchStateMap = {
|
||||
const nameToSwitchStateMap = Object.create(null);
|
||||
Object.assign(nameToSwitchStateMap, {
|
||||
'true': 1,
|
||||
'false': 2,
|
||||
'on': 1,
|
||||
'off': 2
|
||||
};
|
||||
'off': 2,
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// For performance purpose, as simple tests as possible
|
||||
// For performance purpose, as simple test as possible
|
||||
const reNotASCII = /[^\x20-\x7F]/;
|
||||
|
||||
// http://tools.ietf.org/html/rfc5952
|
||||
@ -76,18 +74,20 @@ const reNotASCII = /[^\x20-\x7F]/;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
HnSwitches.prototype.reset = function() {
|
||||
class DynamicSwitchRuleFiltering {
|
||||
constructor() {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.switches = new Map();
|
||||
this.n = '';
|
||||
this.z = '';
|
||||
this.r = 0;
|
||||
this.changed = true;
|
||||
this.decomposedSource = [];
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
HnSwitches.prototype.assign = function(from) {
|
||||
assign(from) {
|
||||
// Remove rules not in other
|
||||
for ( const hn of this.switches.keys() ) {
|
||||
if ( from.switches.has(hn) === false ) {
|
||||
@ -102,11 +102,9 @@ HnSwitches.prototype.assign = function(from) {
|
||||
this.changed = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
HnSwitches.prototype.copyRules = function(from, srcHostname) {
|
||||
copyRules(from, srcHostname) {
|
||||
const thisBits = this.switches.get(srcHostname);
|
||||
const fromBits = from.switches.get(srcHostname);
|
||||
if ( fromBits !== thisBits ) {
|
||||
@ -118,19 +116,13 @@ HnSwitches.prototype.copyRules = function(from, srcHostname) {
|
||||
this.changed = true;
|
||||
}
|
||||
return this.changed;
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
HnSwitches.prototype.hasSameRules = function(other, srcHostname) {
|
||||
hasSameRules(other, srcHostname) {
|
||||
return this.switches.get(srcHostname) === other.switches.get(srcHostname);
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// If value is undefined, the switch is removed
|
||||
|
||||
HnSwitches.prototype.toggle = function(switchName, hostname, newVal) {
|
||||
toggle(switchName, hostname, newVal) {
|
||||
const bitOffset = switchBitOffsets[switchName];
|
||||
if ( bitOffset === undefined ) { return false; }
|
||||
if ( newVal === this.evaluate(switchName, hostname) ) { return false; }
|
||||
@ -144,11 +136,9 @@ HnSwitches.prototype.toggle = function(switchName, hostname, newVal) {
|
||||
}
|
||||
this.changed = true;
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
HnSwitches.prototype.toggleOneZ = function(switchName, hostname, newState) {
|
||||
toggleOneZ(switchName, hostname, newState) {
|
||||
const bitOffset = switchBitOffsets[switchName];
|
||||
if ( bitOffset === undefined ) { return false; }
|
||||
let state = this.evaluateZ(switchName, hostname);
|
||||
@ -169,11 +159,9 @@ HnSwitches.prototype.toggleOneZ = function(switchName, hostname, newState) {
|
||||
}
|
||||
this.changed = true;
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
HnSwitches.prototype.toggleBranchZ = function(switchName, targetHostname, newState) {
|
||||
toggleBranchZ(switchName, targetHostname, newState) {
|
||||
this.toggleOneZ(switchName, targetHostname, newState);
|
||||
|
||||
// Turn off all descendant switches, they will inherit the state of the
|
||||
@ -190,142 +178,112 @@ HnSwitches.prototype.toggleBranchZ = function(switchName, targetHostname, newSta
|
||||
}
|
||||
|
||||
return this.changed;
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
HnSwitches.prototype.toggleZ = function(switchName, hostname, deep, newState) {
|
||||
toggleZ(switchName, hostname, deep, newState) {
|
||||
if ( deep === true ) {
|
||||
return this.toggleBranchZ(switchName, hostname, newState);
|
||||
}
|
||||
return this.toggleOneZ(switchName, hostname, newState);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
}
|
||||
|
||||
// 0 = inherit from broader scope, up to default state
|
||||
// 1 = non-default state
|
||||
// 2 = forced default state (to override a broader non-default state)
|
||||
|
||||
HnSwitches.prototype.evaluate = function(switchName, hostname) {
|
||||
evaluate(switchName, hostname) {
|
||||
const bits = this.switches.get(hostname);
|
||||
if ( bits === undefined ) {
|
||||
return 0;
|
||||
}
|
||||
if ( bits === undefined ) { return 0; }
|
||||
let bitOffset = switchBitOffsets[switchName];
|
||||
if ( bitOffset === undefined ) {
|
||||
return 0;
|
||||
}
|
||||
if ( bitOffset === undefined ) { return 0; }
|
||||
return (bits >>> bitOffset) & 3;
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
HnSwitches.prototype.evaluateZ = function(switchName, hostname) {
|
||||
evaluateZ(switchName, hostname) {
|
||||
const bitOffset = switchBitOffsets[switchName];
|
||||
if ( bitOffset === undefined ) {
|
||||
this.r = 0;
|
||||
return false;
|
||||
}
|
||||
this.n = switchName;
|
||||
decomposeHostname(hostname, this.decomposedSource);
|
||||
for ( const shn of this.decomposedSource ) {
|
||||
for ( const shn of decomposeHostname(hostname, decomposedSource) ) {
|
||||
let bits = this.switches.get(shn);
|
||||
if ( bits !== undefined ) {
|
||||
if ( bits === undefined ) { continue; }
|
||||
bits = bits >>> bitOffset & 3;
|
||||
if ( bits !== 0 ) {
|
||||
if ( bits === 0 ) { continue; }
|
||||
this.z = shn;
|
||||
this.r = bits;
|
||||
return bits === 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.r = 0;
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
HnSwitches.prototype.toLogData = function() {
|
||||
toLogData() {
|
||||
return {
|
||||
source: 'switch',
|
||||
result: this.r,
|
||||
raw: this.n + ': ' + this.z + ' true'
|
||||
raw: `${this.n}: ${this.z} true`
|
||||
};
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
HnSwitches.prototype.toArray = function() {
|
||||
const out = [];
|
||||
const toUnicode = punycode.toUnicode;
|
||||
for ( let hostname of this.switches.keys() ) {
|
||||
for ( const switchName in switchBitOffsets ) {
|
||||
if ( switchBitOffsets.hasOwnProperty(switchName) === false ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
toArray() {
|
||||
const out = [];
|
||||
for ( const hostname of this.switches.keys() ) {
|
||||
const prettyHn = hostname.includes('xn--') && punycode
|
||||
? punycode.toUnicode(hostname)
|
||||
: hostname;
|
||||
for ( const switchName in switchBitOffsets ) {
|
||||
if ( switchBitOffsets[switchName] === undefined ) { continue; }
|
||||
const val = this.evaluate(switchName, hostname);
|
||||
if ( val === 0 ) { continue; }
|
||||
if ( hostname.includes('xn--') ) {
|
||||
hostname = toUnicode(hostname);
|
||||
}
|
||||
out.push(switchName + ': ' + hostname + ' ' + switchStateToNameMap[val]);
|
||||
out.push(`${switchName}: ${prettyHn} ${switchStateToNameMap[val]}`);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
};
|
||||
}
|
||||
|
||||
HnSwitches.prototype.toString = function() {
|
||||
toString() {
|
||||
return this.toArray().join('\n');
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
HnSwitches.prototype.fromString = function(text, append) {
|
||||
fromString(text, append) {
|
||||
const lineIter = new LineIterator(text);
|
||||
if ( append !== true ) { this.reset(); }
|
||||
while ( lineIter.eot() === false ) {
|
||||
this.addFromRuleParts(lineIter.next().trim().split(/\s+/));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
HnSwitches.prototype.validateRuleParts = function(parts) {
|
||||
validateRuleParts(parts) {
|
||||
if ( parts.length < 3 ) { return; }
|
||||
if ( parts[0].endsWith(':') === false ) { return; }
|
||||
if ( nameToSwitchStateMap.hasOwnProperty(parts[2]) === false ) { return; }
|
||||
// Performance: avoid punycoding if hostname is made only of ASCII chars.
|
||||
if ( reNotASCII.test(parts[1]) ) { parts[1] = punycode.toASCII(parts[1]); }
|
||||
if ( nameToSwitchStateMap[parts[2]] === undefined ) { return; }
|
||||
if ( reNotASCII.test(parts[1]) && punycode !== undefined ) {
|
||||
parts[1] = punycode.toASCII(parts[1]);
|
||||
}
|
||||
return parts;
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
HnSwitches.prototype.addFromRuleParts = function(parts) {
|
||||
if ( this.validateRuleParts(parts) !== undefined ) {
|
||||
addFromRuleParts(parts) {
|
||||
if ( this.validateRuleParts(parts) === undefined ) { return false; }
|
||||
const switchName = parts[0].slice(0, -1);
|
||||
if ( switchBitOffsets.hasOwnProperty(switchName) ) {
|
||||
if ( switchBitOffsets[switchName] === undefined ) { return false; }
|
||||
this.toggle(switchName, parts[1], nameToSwitchStateMap[parts[2]]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
HnSwitches.prototype.removeFromRuleParts = function(parts) {
|
||||
removeFromRuleParts(parts) {
|
||||
if ( this.validateRuleParts(parts) !== undefined ) {
|
||||
this.toggle(parts[0].slice(0, -1), parts[1], 0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const sessionSwitches = new HnSwitches();
|
||||
const permanentSwitches = new HnSwitches();
|
||||
|
||||
export { permanentSwitches, sessionSwitches };
|
||||
export default DynamicSwitchRuleFiltering;
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
import logger from './logger.js';
|
||||
import µb from './background.js';
|
||||
import { sessionFirewall } from './dynamic-net-filtering.js';
|
||||
import { sessionFirewall } from './filtering-engines.js';
|
||||
|
||||
import {
|
||||
StaticExtFilteringHostnameDB,
|
||||
|
@ -26,7 +26,7 @@
|
||||
import logger from './logger.js';
|
||||
import µb from './background.js';
|
||||
import { entityFromDomain } from './uri-utils.js';
|
||||
import { sessionFirewall } from './dynamic-net-filtering.js';
|
||||
import { sessionFirewall } from './filtering-engines.js';
|
||||
|
||||
import {
|
||||
StaticExtFilteringHostnameDB,
|
||||
|
@ -44,12 +44,11 @@ import { webRequest } from './traffic.js';
|
||||
import {
|
||||
permanentFirewall,
|
||||
sessionFirewall,
|
||||
} from './dynamic-net-filtering.js';
|
||||
|
||||
import {
|
||||
permanentSwitches,
|
||||
sessionSwitches,
|
||||
} from './hnswitches.js';
|
||||
permanentURLFiltering,
|
||||
sessionURLFiltering,
|
||||
} from './filtering-engines.js';
|
||||
|
||||
import {
|
||||
domainFromHostname,
|
||||
@ -59,11 +58,6 @@ import {
|
||||
isNetworkURI,
|
||||
} from './uri-utils.js';
|
||||
|
||||
import {
|
||||
permanentURLFiltering,
|
||||
sessionURLFiltering,
|
||||
} from './url-net-filtering.js';
|
||||
|
||||
import './benchmarks.js';
|
||||
|
||||
/******************************************************************************/
|
||||
@ -488,10 +482,11 @@ const onMessage = function(request, sender, callback) {
|
||||
break;
|
||||
|
||||
case 'revertFirewallRules':
|
||||
// TODO: use Set() to message around sets of hostnames
|
||||
sessionFirewall.copyRules(
|
||||
permanentFirewall,
|
||||
request.srcHostname,
|
||||
request.desHostnames
|
||||
Object.assign(Object.create(null), request.desHostnames)
|
||||
);
|
||||
sessionSwitches.copyRules(
|
||||
permanentSwitches,
|
||||
@ -507,11 +502,12 @@ const onMessage = function(request, sender, callback) {
|
||||
break;
|
||||
|
||||
case 'saveFirewallRules':
|
||||
// TODO: use Set() to message around sets of hostnames
|
||||
if (
|
||||
permanentFirewall.copyRules(
|
||||
sessionFirewall,
|
||||
request.srcHostname,
|
||||
request.desHostnames
|
||||
Object.assign(Object.create(null), request.desHostnames)
|
||||
)
|
||||
) {
|
||||
µb.savePermanentFirewallRules();
|
||||
|
@ -29,9 +29,12 @@ import staticNetFilteringEngine from './static-net-filtering.js';
|
||||
import µb from './background.js';
|
||||
import { orphanizeString } from './text-utils.js';
|
||||
import { redirectEngine } from './redirect-engine.js';
|
||||
import { sessionFirewall } from './dynamic-net-filtering.js';
|
||||
import { sessionSwitches } from './hnswitches.js';
|
||||
import { sessionURLFiltering } from './url-net-filtering.js';
|
||||
|
||||
import {
|
||||
sessionFirewall,
|
||||
sessionSwitches,
|
||||
sessionURLFiltering,
|
||||
} from './filtering-engines.js';
|
||||
|
||||
import {
|
||||
domainFromHostname,
|
||||
|
@ -26,7 +26,7 @@
|
||||
import logger from './logger.js';
|
||||
import µb from './background.js';
|
||||
import { redirectEngine } from './redirect-engine.js';
|
||||
import { sessionFirewall } from './dynamic-net-filtering.js';
|
||||
import { sessionFirewall } from './filtering-engines.js';
|
||||
|
||||
import {
|
||||
StaticExtFilteringHostnameDB,
|
||||
|
@ -39,17 +39,11 @@ import { webRequest } from './traffic.js';
|
||||
import {
|
||||
permanentFirewall,
|
||||
sessionFirewall,
|
||||
} from './dynamic-net-filtering.js';
|
||||
|
||||
import {
|
||||
permanentSwitches,
|
||||
sessionSwitches,
|
||||
} from './hnswitches.js';
|
||||
|
||||
import {
|
||||
permanentURLFiltering,
|
||||
sessionURLFiltering,
|
||||
} from './url-net-filtering.js';
|
||||
} from './filtering-engines.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
@ -36,14 +36,17 @@ import staticFilteringReverseLookup from './reverselookup.js';
|
||||
import staticNetFilteringEngine from './static-net-filtering.js';
|
||||
import µb from './background.js';
|
||||
import { hostnameFromURI } from './uri-utils.js';
|
||||
import { permanentFirewall } from './dynamic-net-filtering.js';
|
||||
import { permanentSwitches } from './hnswitches.js';
|
||||
import { permanentURLFiltering } from './url-net-filtering.js';
|
||||
import { redirectEngine } from './redirect-engine.js';
|
||||
import { sparseBase64 } from './base64-custom.js';
|
||||
import { StaticFilteringParser } from './static-filtering-parser.js';
|
||||
import { ubolog, ubologSet } from './console.js';
|
||||
|
||||
import {
|
||||
permanentFirewall,
|
||||
permanentSwitches,
|
||||
permanentURLFiltering,
|
||||
} from './filtering-engines.js';
|
||||
|
||||
import {
|
||||
CompiledListReader,
|
||||
CompiledListWriter,
|
||||
|
@ -29,9 +29,12 @@ import scriptletFilteringEngine from './scriptlet-filtering.js';
|
||||
import staticNetFilteringEngine from './static-net-filtering.js';
|
||||
import µb from './background.js';
|
||||
import { PageStore } from './pagestore.js';
|
||||
import { sessionFirewall } from './dynamic-net-filtering.js';
|
||||
import { sessionSwitches } from './hnswitches.js';
|
||||
import { sessionURLFiltering } from './url-net-filtering.js';
|
||||
|
||||
import {
|
||||
sessionFirewall,
|
||||
sessionSwitches,
|
||||
sessionURLFiltering,
|
||||
} from './filtering-engines.js';
|
||||
|
||||
import {
|
||||
domainFromHostname,
|
||||
|
@ -30,9 +30,12 @@ import scriptletFilteringEngine from './scriptlet-filtering.js';
|
||||
import staticNetFilteringEngine from './static-net-filtering.js';
|
||||
import textEncode from './text-encode.js';
|
||||
import µb from './background.js';
|
||||
import { sessionFirewall } from './dynamic-net-filtering.js';
|
||||
import { sessionSwitches } from './hnswitches.js';
|
||||
import { sessionURLFiltering } from './url-net-filtering.js';
|
||||
|
||||
import {
|
||||
sessionFirewall,
|
||||
sessionSwitches,
|
||||
sessionURLFiltering,
|
||||
} from './filtering-engines.js';
|
||||
|
||||
import {
|
||||
entityFromDomain,
|
||||
|
@ -33,17 +33,11 @@ import { redirectEngine } from './redirect-engine.js';
|
||||
import {
|
||||
permanentFirewall,
|
||||
sessionFirewall,
|
||||
} from './dynamic-net-filtering.js';
|
||||
|
||||
import {
|
||||
permanentSwitches,
|
||||
sessionSwitches,
|
||||
} from './hnswitches.js';
|
||||
|
||||
import {
|
||||
permanentURLFiltering,
|
||||
sessionURLFiltering,
|
||||
} from './url-net-filtering.js';
|
||||
} from './filtering-engines.js';
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
@ -43,27 +43,38 @@ const actionToNameMap = {
|
||||
3: 'noop'
|
||||
};
|
||||
|
||||
const nameToActionMap = {
|
||||
const nameToActionMap = Object.create(null);
|
||||
Object.assign(nameToActionMap, {
|
||||
'block': 1,
|
||||
'allow': 2,
|
||||
'noop': 3
|
||||
};
|
||||
});
|
||||
|
||||
const knownInvalidTypes = new Set([
|
||||
'doc',
|
||||
'main_frame',
|
||||
]);
|
||||
|
||||
const intToActionMap = new Map([
|
||||
[ 1, ' block' ],
|
||||
[ 2, ' allow' ],
|
||||
[ 3, ' noop' ]
|
||||
]);
|
||||
|
||||
const decomposedSource = [];
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const RuleEntry = function(url, action) {
|
||||
class RuleEntry {
|
||||
constructor(url, action) {
|
||||
this.url = url;
|
||||
this.action = action;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const indexOfURL = function(entries, url) {
|
||||
function indexOfURL(entries, url) {
|
||||
// TODO: binary search -- maybe, depends on common use cases
|
||||
const urlLen = url.length;
|
||||
// URLs must be ordered by increasing length.
|
||||
@ -73,11 +84,11 @@ const indexOfURL = function(entries, url) {
|
||||
if ( entry.url === url ) { return i; }
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const indexOfMatch = function(entries, url) {
|
||||
function indexOfMatch(entries, url) {
|
||||
const urlLen = url.length;
|
||||
let i = entries.length;
|
||||
while ( i-- ) {
|
||||
@ -93,22 +104,22 @@ const indexOfMatch = function(entries, url) {
|
||||
} while ( i-- );
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const indexFromLength = function(entries, len) {
|
||||
function indexFromLength(entries, len) {
|
||||
// TODO: binary search -- maybe, depends on common use cases
|
||||
// URLs must be ordered by increasing length.
|
||||
for ( let i = 0; i < entries.length; i++ ) {
|
||||
if ( entries[i].url.length > len ) { return i; }
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const addRuleEntry = function(entries, url, action) {
|
||||
function addRuleEntry(entries, url, action) {
|
||||
const entry = new RuleEntry(url, action);
|
||||
const i = indexFromLength(entries, url.length);
|
||||
if ( i === -1 ) {
|
||||
@ -116,17 +127,16 @@ const addRuleEntry = function(entries, url, action) {
|
||||
} else {
|
||||
entries.splice(i, 0, entry);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const URLNetFiltering = function() {
|
||||
class DynamicURLRuleFiltering {
|
||||
constructor() {
|
||||
this.reset();
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
URLNetFiltering.prototype.reset = function() {
|
||||
reset() {
|
||||
this.rules = new Map();
|
||||
// registers, filled with result of last evaluation
|
||||
this.context = '';
|
||||
@ -134,12 +144,9 @@ URLNetFiltering.prototype.reset = function() {
|
||||
this.type = '';
|
||||
this.r = 0;
|
||||
this.changed = false;
|
||||
this.decomposedSource = [];
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
URLNetFiltering.prototype.assign = function(other) {
|
||||
assign(other) {
|
||||
// Remove rules not in other
|
||||
for ( const key of this.rules.keys() ) {
|
||||
if ( other.rules.has(key) === false ) {
|
||||
@ -151,11 +158,9 @@ URLNetFiltering.prototype.assign = function(other) {
|
||||
this.rules.set(entry[0], entry[1].slice());
|
||||
}
|
||||
this.changed = true;
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
URLNetFiltering.prototype.setRule = function(srcHostname, url, type, action) {
|
||||
setRule(srcHostname, url, type, action) {
|
||||
if ( action === 0 ) {
|
||||
return this.removeRule(srcHostname, url, type);
|
||||
}
|
||||
@ -175,11 +180,9 @@ URLNetFiltering.prototype.setRule = function(srcHostname, url, type, action) {
|
||||
}
|
||||
this.changed = true;
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
URLNetFiltering.prototype.removeRule = function(srcHostname, url, type) {
|
||||
removeRule(srcHostname, url, type) {
|
||||
const bucketKey = srcHostname + ' ' + type;
|
||||
const entries = this.rules.get(bucketKey);
|
||||
if ( entries === undefined ) { return false; }
|
||||
@ -191,34 +194,30 @@ URLNetFiltering.prototype.removeRule = function(srcHostname, url, type) {
|
||||
}
|
||||
this.changed = true;
|
||||
return true;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
URLNetFiltering.prototype.evaluateZ = function(context, target, type) {
|
||||
this.r = 0;
|
||||
if ( this.rules.size === 0 ) {
|
||||
return 0;
|
||||
}
|
||||
decomposeHostname(context, this.decomposedSource);
|
||||
for ( let shn of this.decomposedSource ) {
|
||||
this.context = shn;
|
||||
let entries = this.rules.get(shn + ' ' + type);
|
||||
|
||||
evaluateZ(context, target, type) {
|
||||
this.r = 0;
|
||||
if ( this.rules.size === 0 ) { return 0; }
|
||||
decomposeHostname(context, decomposedSource);
|
||||
for ( const srchn of decomposedSource ) {
|
||||
this.context = srchn;
|
||||
let entries = this.rules.get(`${srchn} ${type}`);
|
||||
if ( entries !== undefined ) {
|
||||
let i = indexOfMatch(entries, target);
|
||||
const i = indexOfMatch(entries, target);
|
||||
if ( i !== -1 ) {
|
||||
let entry = entries[i];
|
||||
const entry = entries[i];
|
||||
this.url = entry.url;
|
||||
this.type = type;
|
||||
this.r = entry.action;
|
||||
return this.r;
|
||||
}
|
||||
}
|
||||
entries = this.rules.get(shn + ' *');
|
||||
entries = this.rules.get(`${srchn} *`);
|
||||
if ( entries !== undefined ) {
|
||||
let i = indexOfMatch(entries, target);
|
||||
const i = indexOfMatch(entries, target);
|
||||
if ( i !== -1 ) {
|
||||
let entry = entries[i];
|
||||
const entry = entries[i];
|
||||
this.url = entry.url;
|
||||
this.type = '*';
|
||||
this.r = entry.action;
|
||||
@ -227,49 +226,28 @@ URLNetFiltering.prototype.evaluateZ = function(context, target, type) {
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
URLNetFiltering.prototype.mustAllowCellZ = function(context, target, type) {
|
||||
mustAllowCellZ(context, target, type) {
|
||||
return this.evaluateZ(context, target, type).r === 2;
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
URLNetFiltering.prototype.mustBlockOrAllow = function() {
|
||||
mustBlockOrAllow() {
|
||||
return this.r === 1 || this.r === 2;
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
URLNetFiltering.prototype.toLogData = function() {
|
||||
toLogData() {
|
||||
if ( this.r === 0 ) { return; }
|
||||
const { context, url, type } = this;
|
||||
return {
|
||||
source: 'dynamicUrl',
|
||||
result: this.r,
|
||||
rule: [
|
||||
this.context,
|
||||
this.url,
|
||||
this.type,
|
||||
this.intToActionMap.get(this.r)
|
||||
],
|
||||
raw: this.context + ' ' +
|
||||
this.url + ' ' +
|
||||
this.type + ' ' +
|
||||
this.intToActionMap.get(this.r)
|
||||
};
|
||||
rule: [ context, url, type, intToActionMap.get(this.r) ],
|
||||
raw: `${context} ${url} ${type} ${intToActionMap.get(this.r)}`,
|
||||
};
|
||||
}
|
||||
|
||||
URLNetFiltering.prototype.intToActionMap = new Map([
|
||||
[ 1, ' block' ],
|
||||
[ 2, ' allow' ],
|
||||
[ 3, ' noop' ]
|
||||
]);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
URLNetFiltering.prototype.copyRules = function(other, context, urls, type) {
|
||||
copyRules(other, context, urls, type) {
|
||||
let i = urls.length;
|
||||
while ( i-- ) {
|
||||
const url = urls[i];
|
||||
@ -293,51 +271,35 @@ URLNetFiltering.prototype.copyRules = function(other, context, urls, type) {
|
||||
}
|
||||
}
|
||||
return this.changed;
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// "url-filtering:" hostname url type action
|
||||
|
||||
URLNetFiltering.prototype.toArray = function() {
|
||||
toArray() {
|
||||
const out = [];
|
||||
for ( var item of this.rules ) {
|
||||
const key = item[0];
|
||||
for ( const [ key, entries ] of this.rules ) {
|
||||
let pos = key.indexOf(' ');
|
||||
const hn = key.slice(0, pos);
|
||||
pos = key.lastIndexOf(' ');
|
||||
const type = key.slice(pos + 1);
|
||||
const entries = item[1];
|
||||
for ( let i = 0; i < entries.length; i++ ) {
|
||||
const entry = entries[i];
|
||||
out.push(
|
||||
hn + ' ' +
|
||||
entry.url + ' ' +
|
||||
type + ' ' +
|
||||
actionToNameMap[entry.action]
|
||||
);
|
||||
for ( const { url, action } of entries ) {
|
||||
out.push(`${hn} ${url} ${type} ${actionToNameMap[action]}`);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
};
|
||||
}
|
||||
|
||||
URLNetFiltering.prototype.toString = function() {
|
||||
toString() {
|
||||
return this.toArray().sort().join('\n');
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
URLNetFiltering.prototype.fromString = function(text) {
|
||||
fromString(text) {
|
||||
this.reset();
|
||||
const lineIter = new LineIterator(text);
|
||||
while ( lineIter.eot() === false ) {
|
||||
this.addFromRuleParts(lineIter.next().trim().split(/\s+/));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
URLNetFiltering.prototype.validateRuleParts = function(parts) {
|
||||
validateRuleParts(parts) {
|
||||
if ( parts.length !== 4 ) { return; }
|
||||
if ( parts[1].indexOf('://') <= 0 ) { return; }
|
||||
if (
|
||||
@ -346,33 +308,29 @@ URLNetFiltering.prototype.validateRuleParts = function(parts) {
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if ( nameToActionMap.hasOwnProperty(parts[3]) === false ) { return; }
|
||||
if ( nameToActionMap[parts[3]] === undefined ) { return; }
|
||||
return parts;
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
URLNetFiltering.prototype.addFromRuleParts = function(parts) {
|
||||
addFromRuleParts(parts) {
|
||||
if ( this.validateRuleParts(parts) !== undefined ) {
|
||||
this.setRule(parts[0], parts[1], parts[2], nameToActionMap[parts[3]]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
URLNetFiltering.prototype.removeFromRuleParts = function(parts) {
|
||||
removeFromRuleParts(parts) {
|
||||
if ( this.validateRuleParts(parts) !== undefined ) {
|
||||
this.removeRule(parts[0], parts[1], parts[2]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const sessionURLFiltering = new URLNetFiltering();
|
||||
const permanentURLFiltering = new URLNetFiltering();
|
||||
|
||||
export { permanentURLFiltering, sessionURLFiltering };
|
||||
export default DynamicURLRuleFiltering;
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -11,14 +11,17 @@ rm -rf $DES
|
||||
mkdir -p $DES/js
|
||||
cp src/js/base64-custom.js $DES/js
|
||||
cp src/js/biditrie.js $DES/js
|
||||
cp src/js/dynamic-net-filtering.js $DES/js
|
||||
cp src/js/filtering-context.js $DES/js
|
||||
cp src/js/globals.js $DES/js
|
||||
cp src/js/hnswitches.js $DES/js
|
||||
cp src/js/hntrie.js $DES/js
|
||||
cp src/js/static-filtering-parser.js $DES/js
|
||||
cp src/js/static-net-filtering.js $DES/js
|
||||
cp src/js/static-filtering-io.js $DES/js
|
||||
cp src/js/text-utils.js $DES/js
|
||||
cp src/js/uri-utils.js $DES/js
|
||||
cp src/js/url-net-filtering.js $DES/js
|
||||
|
||||
mkdir -p $DES/lib
|
||||
cp -R src/lib/punycode.js $DES/lib/
|
||||
|
Loading…
Reference in New Issue
Block a user