From 2f63fb3fd451bd3ca5795cfd1be46335ce6d3da7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 8 Jul 2019 10:49:53 -0400 Subject: [PATCH] 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. --- src/js/codemirror/ubo-dynamic-filtering.js | 14 ++- src/js/logger-ui.js | 3 + src/js/url-net-filtering.js | 118 ++++++++++----------- 3 files changed, 74 insertions(+), 61 deletions(-) diff --git a/src/js/codemirror/ubo-dynamic-filtering.js b/src/js/codemirror/ubo-dynamic-filtering.js index cc493e484..b2507edb2 100644 --- a/src/js/codemirror/ubo-dynamic-filtering.js +++ b/src/js/codemirror/ubo-dynamic-filtering.js @@ -38,7 +38,7 @@ CodeMirror.defineMode('ubo-dynamic-filtering', ( ) => { 'true', 'false', ]); - const validTypes = new Set([ + const validHnRuleTypes = new Set([ '*', '3p', 'image', @@ -47,6 +47,10 @@ CodeMirror.defineMode('ubo-dynamic-filtering', ( ) => { '3p-script', '3p-frame', ]); + const invalidURLRuleTypes = new Set([ + 'doc', + 'main_frame', + ]); const validActions = new Set([ 'block', 'allow', @@ -110,20 +114,26 @@ CodeMirror.defineMode('ubo-dynamic-filtering', ( ) => { } // Field 3 if ( tokens.length === 3 ) { + // Switch rule if ( isSwitchRule(tokens[0]) ) { if ( validSwitcheStates.has(token) === false ) { return skipToEnd(stream, 'error'); } return null; } + // Hostname rule if ( isURLRule(tokens[1]) === false ) { if ( tokens[1] !== '*' && token !== '*' || - tokens[1] === '*' && validTypes.has(token) === false + tokens[1] === '*' && validHnRuleTypes.has(token) === false ) { return skipToEnd(stream, 'error'); } } + // URL rule + if ( /[^a-z_-]+/.test(token) || invalidURLRuleTypes.has(token) ) { + return skipToEnd(stream, 'error'); + } return null; } // Field 4 diff --git a/src/js/logger-ui.js b/src/js/logger-ui.js index 6b8d620ff..a69e21d0a 100644 --- a/src/js/logger-ui.js +++ b/src/js/logger-ui.js @@ -1610,6 +1610,9 @@ const reloadTab = function(ev) { const fillDynamicPane = function() { 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 if ( targetURLs.length === 0 || reSchemeOnly.test(targetURLs[0]) ) { return; diff --git a/src/js/url-net-filtering.js b/src/js/url-net-filtering.js index c7da817c4..d6d44111b 100644 --- a/src/js/url-net-filtering.js +++ b/src/js/url-net-filtering.js @@ -1,7 +1,7 @@ /******************************************************************************* 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 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 // 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', 2: 'allow', 3: 'noop' }; -var nameToActionMap = { +const nameToActionMap = { 'block': 1, 'allow': 2, 'noop': 3 }; +const knownInvalidTypes = new Set([ + 'doc', + 'main_frame', +]); + /******************************************************************************/ -var RuleEntry = function(url, action) { +const RuleEntry = function(url, action) { this.url = url; this.action = action; }; /******************************************************************************/ -var indexOfURL = function(entries, url) { +const indexOfURL = function(entries, url) { // TODO: binary search -- maybe, depends on common use cases - var urlLen = url.length, - entry; + const urlLen = url.length; // URLs must be ordered by increasing length. - for ( var i = 0; i < entries.length; i++ ) { - entry = entries[i]; - if ( entry.url.length > urlLen ) { - break; - } - if ( entry.url === url ) { - return i; - } + for ( let i = 0; i < entries.length; i++ ) { + const entry = entries[i]; + if ( entry.url.length > urlLen ) { break; } + if ( entry.url === url ) { return i; } } return -1; }; /******************************************************************************/ -var indexOfMatch = function(entries, url) { - var urlLen = url.length, - i = entries.length; +const indexOfMatch = function(entries, url) { + const urlLen = url.length; + let i = entries.length; while ( i-- ) { if ( entries[i].url.length <= urlLen ) { 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 // URLs must be ordered by increasing length. - for ( var i = 0; i < entries.length; i++ ) { - if ( entries[i].url.length > len ) { - return i; - } + for ( let i = 0; i < entries.length; i++ ) { + if ( entries[i].url.length > len ) { return i; } } return -1; }; /******************************************************************************/ -var addRuleEntry = function(entries, url, action) { - var entry = new RuleEntry(url, action), - i = indexFromLength(entries, url.length); +const addRuleEntry = function(entries, url, action) { + const entry = new RuleEntry(url, action); + const i = indexFromLength(entries, url.length); if ( i === -1 ) { entries.push(entry); } else { @@ -123,7 +121,7 @@ var addRuleEntry = function(entries, url, action) { /******************************************************************************/ -var URLNetFiltering = function() { +const URLNetFiltering = function() { this.reset(); }; @@ -144,13 +142,13 @@ URLNetFiltering.prototype.reset = function() { URLNetFiltering.prototype.assign = function(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 ) { this.rules.delete(key); } } // 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.changed = true; @@ -162,16 +160,15 @@ URLNetFiltering.prototype.setRule = function(srcHostname, url, type, action) { if ( action === 0 ) { return this.removeRule(srcHostname, url, type); } - var bucketKey = srcHostname + ' ' + type, - entries = this.rules.get(bucketKey); + const bucketKey = srcHostname + ' ' + type; + let entries = this.rules.get(bucketKey); if ( entries === undefined ) { entries = []; this.rules.set(bucketKey, entries); } - var i = indexOfURL(entries, url), - entry; + const i = indexOfURL(entries, url); if ( i !== -1 ) { - entry = entries[i]; + const entry = entries[i]; if ( entry.action === action ) { return false; } entry.action = action; } else { @@ -184,15 +181,11 @@ URLNetFiltering.prototype.setRule = function(srcHostname, url, type, action) { /******************************************************************************/ URLNetFiltering.prototype.removeRule = function(srcHostname, url, type) { - var bucketKey = srcHostname + ' ' + type, - entries = this.rules.get(bucketKey); - if ( entries === undefined ) { - return false; - } - var i = indexOfURL(entries, url); - if ( i === -1 ) { - return false; - } + const bucketKey = srcHostname + ' ' + type; + const entries = this.rules.get(bucketKey); + if ( entries === undefined ) { return false; } + const i = indexOfURL(entries, url); + if ( i === -1 ) { return false; } entries.splice(i, 1); if ( entries.length === 0 ) { this.rules.delete(bucketKey); @@ -278,14 +271,19 @@ URLNetFiltering.prototype.intToActionMap = new Map([ /******************************************************************************/ URLNetFiltering.prototype.copyRules = function(other, context, urls, type) { - var url, otherOwn, thisOwn; - var i = urls.length; + let i = urls.length; while ( i-- ) { - url = urls[i]; + const url = urls[i]; 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); - 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 ) { this.setRule(context, url, type, other.r); this.changed = true; @@ -303,17 +301,16 @@ URLNetFiltering.prototype.copyRules = function(other, context, urls, type) { // "url-filtering:" hostname url type action URLNetFiltering.prototype.toArray = function() { - var out = [], - key, pos, hn, type, entries, i, entry; + const out = []; for ( var item of this.rules ) { - key = item[0]; - pos = key.indexOf(' '); - hn = key.slice(0, pos); + const key = item[0]; + let pos = key.indexOf(' '); + const hn = key.slice(0, pos); pos = key.lastIndexOf(' '); - type = key.slice(pos + 1); - entries = item[1]; - for ( i = 0; i < entries.length; i++ ) { - entry = entries[i]; + 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 + ' ' + @@ -333,7 +330,7 @@ URLNetFiltering.prototype.toString = function() { URLNetFiltering.prototype.fromString = function(text) { this.reset(); - var lineIter = new µBlock.LineIterator(text); + const lineIter = new µBlock.LineIterator(text); while ( lineIter.eot() === false ) { this.addFromRuleParts(lineIter.next().trim().split(/\s+/)); } @@ -344,6 +341,9 @@ URLNetFiltering.prototype.fromString = function(text) { URLNetFiltering.prototype.validateRuleParts = function(parts) { if ( parts.length !== 4 ) { 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; } return parts; };