Prevent adding known invalid URL-based rules

Related discussion:
- https://github.com/uBlockOrigin/uBlock-issues/issues/662#issuecomment-509220702

Currently, `doc` (aka `main_frame`) rules are not
evaluated to decide whether a network request must
be blocked or not, by design. This commits adjust
uBO's UI to account for this.
This commit is contained in:
Raymond Hill 2019-07-08 10:49:53 -04:00
parent e55cae6232
commit 2f63fb3fd4
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
3 changed files with 74 additions and 61 deletions

View File

@ -38,7 +38,7 @@ CodeMirror.defineMode('ubo-dynamic-filtering', ( ) => {
'true', 'true',
'false', 'false',
]); ]);
const validTypes = new Set([ const validHnRuleTypes = new Set([
'*', '*',
'3p', '3p',
'image', 'image',
@ -47,6 +47,10 @@ CodeMirror.defineMode('ubo-dynamic-filtering', ( ) => {
'3p-script', '3p-script',
'3p-frame', '3p-frame',
]); ]);
const invalidURLRuleTypes = new Set([
'doc',
'main_frame',
]);
const validActions = new Set([ const validActions = new Set([
'block', 'block',
'allow', 'allow',
@ -110,20 +114,26 @@ CodeMirror.defineMode('ubo-dynamic-filtering', ( ) => {
} }
// Field 3 // Field 3
if ( tokens.length === 3 ) { if ( tokens.length === 3 ) {
// Switch rule
if ( isSwitchRule(tokens[0]) ) { if ( isSwitchRule(tokens[0]) ) {
if ( validSwitcheStates.has(token) === false ) { if ( validSwitcheStates.has(token) === false ) {
return skipToEnd(stream, 'error'); return skipToEnd(stream, 'error');
} }
return null; return null;
} }
// Hostname rule
if ( isURLRule(tokens[1]) === false ) { if ( isURLRule(tokens[1]) === false ) {
if ( if (
tokens[1] !== '*' && token !== '*' || tokens[1] !== '*' && token !== '*' ||
tokens[1] === '*' && validTypes.has(token) === false tokens[1] === '*' && validHnRuleTypes.has(token) === false
) { ) {
return skipToEnd(stream, 'error'); return skipToEnd(stream, 'error');
} }
} }
// URL rule
if ( /[^a-z_-]+/.test(token) || invalidURLRuleTypes.has(token) ) {
return skipToEnd(stream, 'error');
}
return null; return null;
} }
// Field 4 // Field 4

View File

@ -1610,6 +1610,9 @@ const reloadTab = function(ev) {
const fillDynamicPane = function() { const fillDynamicPane = function() {
if ( targetRow.classList.contains('cosmeticRealm') ) { return; } if ( targetRow.classList.contains('cosmeticRealm') ) { return; }
// https://github.com/uBlockOrigin/uBlock-issues/issues/662#issuecomment-509220702
if ( targetType === 'doc' ) { return; }
// https://github.com/gorhill/uBlock/issues/2469 // https://github.com/gorhill/uBlock/issues/2469
if ( targetURLs.length === 0 || reSchemeOnly.test(targetURLs[0]) ) { if ( targetURLs.length === 0 || reSchemeOnly.test(targetURLs[0]) ) {
return; return;

View File

@ -1,7 +1,7 @@
/******************************************************************************* /*******************************************************************************
uBlock Origin - a browser extension to black/white list requests. uBlock Origin - a browser extension to black/white list requests.
Copyright (C) 2015-2018 Raymond Hill Copyright (C) 2015-present Raymond Hill
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -26,7 +26,7 @@
// The purpose of log filtering is to create ad hoc filtering rules, to // The purpose of log filtering is to create ad hoc filtering rules, to
// diagnose and assist in the creation of custom filters. // diagnose and assist in the creation of custom filters.
µBlock.URLNetFiltering = (function() { µBlock.URLNetFiltering = (( ) => {
/******************************************************************************* /*******************************************************************************
@ -38,49 +38,49 @@ rule entry: { url, action }
/******************************************************************************/ /******************************************************************************/
var actionToNameMap = { const actionToNameMap = {
1: 'block', 1: 'block',
2: 'allow', 2: 'allow',
3: 'noop' 3: 'noop'
}; };
var nameToActionMap = { const nameToActionMap = {
'block': 1, 'block': 1,
'allow': 2, 'allow': 2,
'noop': 3 'noop': 3
}; };
const knownInvalidTypes = new Set([
'doc',
'main_frame',
]);
/******************************************************************************/ /******************************************************************************/
var RuleEntry = function(url, action) { const RuleEntry = function(url, action) {
this.url = url; this.url = url;
this.action = action; this.action = action;
}; };
/******************************************************************************/ /******************************************************************************/
var indexOfURL = function(entries, url) { const indexOfURL = function(entries, url) {
// TODO: binary search -- maybe, depends on common use cases // TODO: binary search -- maybe, depends on common use cases
var urlLen = url.length, const urlLen = url.length;
entry;
// URLs must be ordered by increasing length. // URLs must be ordered by increasing length.
for ( var i = 0; i < entries.length; i++ ) { for ( let i = 0; i < entries.length; i++ ) {
entry = entries[i]; const entry = entries[i];
if ( entry.url.length > urlLen ) { if ( entry.url.length > urlLen ) { break; }
break; if ( entry.url === url ) { return i; }
}
if ( entry.url === url ) {
return i;
}
} }
return -1; return -1;
}; };
/******************************************************************************/ /******************************************************************************/
var indexOfMatch = function(entries, url) { const indexOfMatch = function(entries, url) {
var urlLen = url.length, const urlLen = url.length;
i = entries.length; let i = entries.length;
while ( i-- ) { while ( i-- ) {
if ( entries[i].url.length <= urlLen ) { if ( entries[i].url.length <= urlLen ) {
break; break;
@ -98,22 +98,20 @@ var indexOfMatch = function(entries, url) {
/******************************************************************************/ /******************************************************************************/
var indexFromLength = function(entries, len) { const indexFromLength = function(entries, len) {
// TODO: binary search -- maybe, depends on common use cases // TODO: binary search -- maybe, depends on common use cases
// URLs must be ordered by increasing length. // URLs must be ordered by increasing length.
for ( var i = 0; i < entries.length; i++ ) { for ( let i = 0; i < entries.length; i++ ) {
if ( entries[i].url.length > len ) { if ( entries[i].url.length > len ) { return i; }
return i;
}
} }
return -1; return -1;
}; };
/******************************************************************************/ /******************************************************************************/
var addRuleEntry = function(entries, url, action) { const addRuleEntry = function(entries, url, action) {
var entry = new RuleEntry(url, action), const entry = new RuleEntry(url, action);
i = indexFromLength(entries, url.length); const i = indexFromLength(entries, url.length);
if ( i === -1 ) { if ( i === -1 ) {
entries.push(entry); entries.push(entry);
} else { } else {
@ -123,7 +121,7 @@ var addRuleEntry = function(entries, url, action) {
/******************************************************************************/ /******************************************************************************/
var URLNetFiltering = function() { const URLNetFiltering = function() {
this.reset(); this.reset();
}; };
@ -144,13 +142,13 @@ URLNetFiltering.prototype.reset = function() {
URLNetFiltering.prototype.assign = function(other) { URLNetFiltering.prototype.assign = function(other) {
// Remove rules not in other // Remove rules not in other
for ( var key of this.rules.keys() ) { for ( const key of this.rules.keys() ) {
if ( other.rules.has(key) === false ) { if ( other.rules.has(key) === false ) {
this.rules.delete(key); this.rules.delete(key);
} }
} }
// Add/change rules in other // Add/change rules in other
for ( var entry of other.rules ) { for ( const entry of other.rules ) {
this.rules.set(entry[0], entry[1].slice()); this.rules.set(entry[0], entry[1].slice());
} }
this.changed = true; this.changed = true;
@ -162,16 +160,15 @@ URLNetFiltering.prototype.setRule = function(srcHostname, url, type, action) {
if ( action === 0 ) { if ( action === 0 ) {
return this.removeRule(srcHostname, url, type); return this.removeRule(srcHostname, url, type);
} }
var bucketKey = srcHostname + ' ' + type, const bucketKey = srcHostname + ' ' + type;
entries = this.rules.get(bucketKey); let entries = this.rules.get(bucketKey);
if ( entries === undefined ) { if ( entries === undefined ) {
entries = []; entries = [];
this.rules.set(bucketKey, entries); this.rules.set(bucketKey, entries);
} }
var i = indexOfURL(entries, url), const i = indexOfURL(entries, url);
entry;
if ( i !== -1 ) { if ( i !== -1 ) {
entry = entries[i]; const entry = entries[i];
if ( entry.action === action ) { return false; } if ( entry.action === action ) { return false; }
entry.action = action; entry.action = action;
} else { } else {
@ -184,15 +181,11 @@ URLNetFiltering.prototype.setRule = function(srcHostname, url, type, action) {
/******************************************************************************/ /******************************************************************************/
URLNetFiltering.prototype.removeRule = function(srcHostname, url, type) { URLNetFiltering.prototype.removeRule = function(srcHostname, url, type) {
var bucketKey = srcHostname + ' ' + type, const bucketKey = srcHostname + ' ' + type;
entries = this.rules.get(bucketKey); const entries = this.rules.get(bucketKey);
if ( entries === undefined ) { if ( entries === undefined ) { return false; }
return false; const i = indexOfURL(entries, url);
} if ( i === -1 ) { return false; }
var i = indexOfURL(entries, url);
if ( i === -1 ) {
return false;
}
entries.splice(i, 1); entries.splice(i, 1);
if ( entries.length === 0 ) { if ( entries.length === 0 ) {
this.rules.delete(bucketKey); this.rules.delete(bucketKey);
@ -278,14 +271,19 @@ URLNetFiltering.prototype.intToActionMap = new Map([
/******************************************************************************/ /******************************************************************************/
URLNetFiltering.prototype.copyRules = function(other, context, urls, type) { URLNetFiltering.prototype.copyRules = function(other, context, urls, type) {
var url, otherOwn, thisOwn; let i = urls.length;
var i = urls.length;
while ( i-- ) { while ( i-- ) {
url = urls[i]; const url = urls[i];
other.evaluateZ(context, url, type); other.evaluateZ(context, url, type);
otherOwn = other.r !== 0 && other.context === context && other.url === url && other.type === type; const otherOwn = other.r !== 0 &&
other.context === context &&
other.url === url &&
other.type === type;
this.evaluateZ(context, url, type); this.evaluateZ(context, url, type);
thisOwn = this.r !== 0 && this.context === context && this.url === url && this.type === type; const thisOwn = this.r !== 0 &&
this.context === context &&
this.url === url &&
this.type === type;
if ( otherOwn && !thisOwn ) { if ( otherOwn && !thisOwn ) {
this.setRule(context, url, type, other.r); this.setRule(context, url, type, other.r);
this.changed = true; this.changed = true;
@ -303,17 +301,16 @@ URLNetFiltering.prototype.copyRules = function(other, context, urls, type) {
// "url-filtering:" hostname url type action // "url-filtering:" hostname url type action
URLNetFiltering.prototype.toArray = function() { URLNetFiltering.prototype.toArray = function() {
var out = [], const out = [];
key, pos, hn, type, entries, i, entry;
for ( var item of this.rules ) { for ( var item of this.rules ) {
key = item[0]; const key = item[0];
pos = key.indexOf(' '); let pos = key.indexOf(' ');
hn = key.slice(0, pos); const hn = key.slice(0, pos);
pos = key.lastIndexOf(' '); pos = key.lastIndexOf(' ');
type = key.slice(pos + 1); const type = key.slice(pos + 1);
entries = item[1]; const entries = item[1];
for ( i = 0; i < entries.length; i++ ) { for ( let i = 0; i < entries.length; i++ ) {
entry = entries[i]; const entry = entries[i];
out.push( out.push(
hn + ' ' + hn + ' ' +
entry.url + ' ' + entry.url + ' ' +
@ -333,7 +330,7 @@ URLNetFiltering.prototype.toString = function() {
URLNetFiltering.prototype.fromString = function(text) { URLNetFiltering.prototype.fromString = function(text) {
this.reset(); this.reset();
var lineIter = new µBlock.LineIterator(text); const lineIter = new µBlock.LineIterator(text);
while ( lineIter.eot() === false ) { while ( lineIter.eot() === false ) {
this.addFromRuleParts(lineIter.next().trim().split(/\s+/)); this.addFromRuleParts(lineIter.next().trim().split(/\s+/));
} }
@ -344,6 +341,9 @@ URLNetFiltering.prototype.fromString = function(text) {
URLNetFiltering.prototype.validateRuleParts = function(parts) { URLNetFiltering.prototype.validateRuleParts = function(parts) {
if ( parts.length !== 4 ) { return; } if ( parts.length !== 4 ) { return; }
if ( parts[1].indexOf('://') === -1 ) { return; } if ( parts[1].indexOf('://') === -1 ) { return; }
if ( /[^a-z_-]+/.test(parts[2]) || knownInvalidTypes.has(parts[2]) ) {
return;
}
if ( nameToActionMap.hasOwnProperty(parts[3]) === false ) { return; } if ( nameToActionMap.hasOwnProperty(parts[3]) === false ) { return; }
return parts; return parts;
}; };