From cf2c638d8e953585651d8a31f3a67f19fbc62842 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 1 Dec 2020 09:29:40 -0500 Subject: [PATCH] Improve reporting of matching `redirect=` rules in logger All matching `redirect-rule` directives will now be reported in the logger, instead of just the effective one. The highest-ranked redirect directive will be the one effectively used for redirection. This way filter list authors can see whether a lower priority redirect is being overriden by a higher priority one. The default priority has been changed to 10, so as to allow more leeway to create lower ranked redirect directives. Additonally, rendering of redirect directives with explicit priority has been fixed in the logger, they will no longer be rendered as unknown redirect tokens. --- src/js/codemirror/ubo-static-filtering.js | 5 +- src/js/pagestore.js | 6 +-- src/js/static-filtering-parser.js | 11 ++++ src/js/static-net-filtering.js | 66 +++++++++-------------- 4 files changed, 42 insertions(+), 46 deletions(-) diff --git a/src/js/codemirror/ubo-static-filtering.js b/src/js/codemirror/ubo-static-filtering.js index 79dc3618b..976554a20 100644 --- a/src/js/codemirror/ubo-static-filtering.js +++ b/src/js/codemirror/ubo-static-filtering.js @@ -192,11 +192,12 @@ CodeMirror.defineMode('ubo-static-filtering', function() { parser.commentSpan.i, parser.BITComma ); - const token = parser.strFromSlices(parserSlot, end - 3); + const raw = parser.strFromSlices(parserSlot, end - 3); + const { token } = StaticFilteringParser.parseRedirectValue(raw); if ( redirectNames.has(token) === false ) { style += ' warning'; } - stream.pos += token.length; + stream.pos += raw.length; parserSlot = end; return style; } diff --git a/src/js/pagestore.js b/src/js/pagestore.js index 505fe2a2a..037c84875 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -716,10 +716,10 @@ const PageStore = class { redirectBlockedRequest(fctxt) { if ( µb.hiddenSettings.ignoreRedirectFilters === true ) { return; } - const directive = µb.staticNetFilteringEngine.redirectRequest(fctxt); - if ( directive === undefined ) { return; } + const directives = µb.staticNetFilteringEngine.redirectRequest(fctxt); + if ( directives === undefined ) { return; } if ( µb.logger.enabled !== true ) { return; } - fctxt.pushFilter(directive.logData()); + fctxt.pushFilters(directives.map(a => a.logData())); if ( fctxt.redirectURL === undefined ) { return; } fctxt.pushFilter({ source: 'redirect', diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index dbe64875b..cd2732628 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -1162,6 +1162,17 @@ const Parser = class { ); } + static parseRedirectValue(arg) { + let token = arg.trim(); + let priority = 10; + const match = /:\d+$/.exec(token); + if ( match !== null ) { + token = token.slice(0, match.index); + priority = parseInt(token.slice(match.index + 1), 10); + } + return { token, priority }; + } + static parseQueryPruneValue(arg) { let s = arg.trim(); if ( s === '' ) { return { all: true }; } diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 8245b6d0e..b5f83382b 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -4236,57 +4236,41 @@ FilterContainer.prototype.redirectRequest = function(fctxt) { const directives = this.matchAndFetchModifiers(fctxt, 'redirect-rule'); // No directive is the most common occurrence. if ( directives === undefined ) { return; } - // A single directive should be the next most common occurrence. - if ( directives.length === 1 ) { - const directive = directives[0]; - if ( (directive.bits & AllowAction) !== 0 ) { return directive; } - const modifier = directive.modifier; - const { token } = this.parseRedirectRequestValue(modifier); + // More than a single directive means more work. + if ( directives.length !== 1 ) { + directives.sort(FilterContainer.compareRedirectRequests); + } + // Redirect to highest-ranked directive + const directive = directives[directives.length - 1]; + if ( (directive.bits & AllowAction) === 0 ) { + const { token } = + FilterContainer.parseRedirectRequestValue(directive.modifier); fctxt.redirectURL = µb.redirectEngine.tokenToURL(fctxt, token); if ( fctxt.redirectURL === undefined ) { return; } - return directive; } - // Multiple directives mean more work to do. - let winningDirective; - let winningPriority = 0; - for ( const directive of directives ) { - const modifier = directive.modifier; - const isException = (directive.bits & AllowAction) !== 0; - if ( isException && modifier.value === '' ) { - winningDirective = directive; - break; - } - const { token, priority } = this.parseRedirectRequestValue(modifier); - if ( µb.redirectEngine.hasToken(token) === false ) { continue; } - if ( winningDirective === undefined || priority > winningPriority ) { - winningDirective = directive; - winningPriority = priority; - } - } - if ( winningDirective === undefined ) { return; } - if ( (winningDirective.bits & AllowAction) === 0 ) { - fctxt.redirectURL = µb.redirectEngine.tokenToURL( - fctxt, - winningDirective.modifier.cache.token - ); - } - return winningDirective; + return directives; }; -FilterContainer.prototype.parseRedirectRequestValue = function(modifier) { +FilterContainer.parseRedirectRequestValue = function(modifier) { if ( modifier.cache === undefined ) { - let token = modifier.value; - let priority = 1; - const match = /:(\d+)$/.exec(token); - if ( match !== null ) { - token = token.slice(0, match.index); - priority = parseInt(match[1], 10); - } - modifier.cache = { token, priority }; + modifier.cache = + vAPI.StaticFilteringParser.parseRedirectValue(modifier.value); } return modifier.cache; }; +FilterContainer.compareRedirectRequests = function(a, b) { + if ( (a.bits & AllowAction) !== 0 ) { return -1; } + if ( (b.bits & AllowAction) !== 0 ) { return 1; } + const { token: atok, priority: aint } = + FilterContainer.parseRedirectRequestValue(a.modifier); + if ( µb.redirectEngine.hasToken(atok) === false ) { return -1; } + const { token: btok, priority: bint } = + FilterContainer.parseRedirectRequestValue(b.modifier); + if ( µb.redirectEngine.hasToken(btok) === false ) { return 1; } + return aint - bint; +}; + /******************************************************************************/ FilterContainer.prototype.filterQuery = function(fctxt) {