Skip to content

Commit aaf35d9

Browse files
committed
Improve compatibility of uritransform= with DNR syntax
The `uritransform=` option will now be converted to a proper DNR rule when the following condition are fulfilled: - The value of the `uritransform` option matches `//[replacement]/`, i.e. the pattern to match is empty, and only the replacement part is provided. - The filter pattern is a regex. Is such case, the DNR rule will be a `redirect` making use of the `regexSubstitution` property. In case the above conditions are not fulfilled, the filter will be discarded as incompatible with DNR syntax (as was the case before). This is potentially a breaking change, in cases where a filter assumed that the part to match was the start of the path part of a URL. A reminder that `uritransform` is an option which requires a trusted source, otherwise it is rejected.
1 parent 25d9964 commit aaf35d9

File tree

5 files changed

+50
-26
lines changed

5 files changed

+50
-26
lines changed

src/js/benchmarks.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ export async function benchmarkStaticNetFiltering(options = {}) {
183183
if ( r === 1 ) { blockCount += 1; }
184184
else if ( r === 2 ) { allowCount += 1; }
185185
if ( r !== 1 ) {
186-
if ( sfne.transformRequest(fctxt) ) {
186+
if ( sfne.transformURL(fctxt) ) {
187187
redirectCount += 1;
188188
}
189189
if ( sfne.hasQuery(fctxt) ) {

src/js/messaging.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1886,7 +1886,9 @@ const onMessage = function(request, sender, callback) {
18861886
return {
18871887
name: assetKey,
18881888
text: details.content,
1889-
trustedSource: assetKey.startsWith('ublock-'),
1889+
trustedSource: assetKey.startsWith('ublock-') ||
1890+
assetKey === µb.userFiltersPath &&
1891+
µb.userSettings.userFiltersTrusted,
18901892
};
18911893
})
18921894
);

src/js/pagestore.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -956,7 +956,7 @@ const PageStore = class {
956956

957957
redirectNonBlockedRequest(fctxt) {
958958
const directives = [];
959-
staticNetFilteringEngine.transformRequest(fctxt, directives);
959+
staticNetFilteringEngine.transformURL(fctxt, directives);
960960
if ( staticNetFilteringEngine.hasQuery(fctxt) ) {
961961
staticNetFilteringEngine.filterQuery(fctxt, directives);
962962
}

src/js/static-filtering-parser.js

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,11 @@ export class AstFilterParser {
841841
this.netOptionValueParser = new ArglistParser(',');
842842
this.scriptletArgListParser = new ArglistParser(',');
843843
this.domainRegexValueParser = new ArglistParser('/');
844+
this.reNetOptionTokens = new RegExp(
845+
`^(${Array.from(netOptionTokenDescriptors.keys())
846+
.map(s => escapeForRegex(s))
847+
.join('|')})\\b`
848+
);
844849
}
845850

846851
finish() {
@@ -1512,12 +1517,7 @@ export class AstFilterParser {
15121517
for (;;) {
15131518
const before = s.charAt(j-1);
15141519
if ( before === '$' ) { return -1; }
1515-
const after = s.charAt(j+1);
1516-
if ( ')/|'.includes(after) === false ) {
1517-
if ( before === '' || '"\'\\`'.includes(before) === false ) {
1518-
return j;
1519-
}
1520-
}
1520+
if ( this.reNetOptionTokens.test(s.slice(j+1)) ) { return j; }
15211521
if ( j === start ) { break; }
15221522
j = s.lastIndexOf('$', j-1);
15231523
if ( j === -1 ) { break; }
@@ -3080,9 +3080,10 @@ export function parseReplaceByRegexValue(s) {
30803080
if ( parser.transform ) {
30813081
pattern = parser.normalizeArg(pattern);
30823082
}
3083-
if ( pattern === '' ) { return; }
3084-
pattern = parser.normalizeArg(pattern, '$');
3085-
pattern = parser.normalizeArg(pattern, ',');
3083+
if ( pattern !== '' ) {
3084+
pattern = parser.normalizeArg(pattern, '$');
3085+
pattern = parser.normalizeArg(pattern, ',');
3086+
}
30863087
parser.nextArg(s, parser.separatorEnd);
30873088
let replacement = s.slice(parser.argBeg, parser.argEnd);
30883089
if ( parser.separatorEnd === parser.separatorBeg ) { return; }
@@ -3092,6 +3093,9 @@ export function parseReplaceByRegexValue(s) {
30923093
replacement = parser.normalizeArg(replacement, '$');
30933094
replacement = parser.normalizeArg(replacement, ',');
30943095
const flags = s.slice(parser.separatorEnd);
3096+
if ( pattern === '' ) {
3097+
return { flags, replacement }
3098+
}
30953099
try {
30963100
return { re: new RegExp(pattern, flags), replacement };
30973101
} catch {
@@ -3101,7 +3105,10 @@ export function parseReplaceByRegexValue(s) {
31013105
export function parseReplaceValue(s) {
31023106
if ( s.startsWith('/') ) {
31033107
const r = parseReplaceByRegexValue(s);
3104-
if ( r ) { r.type = 'text'; }
3108+
if ( r ) {
3109+
if ( r.re === undefined ) { return; }
3110+
r.type = 'text';
3111+
}
31053112
return r;
31063113
}
31073114
const pos = s.indexOf(':');

src/js/static-net-filtering.js

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,7 +1231,6 @@ class FilterRegex {
12311231
);
12321232
}
12331233
if ( refs.$re.test($requestURLRaw) === false ) { return false; }
1234-
$patternMatchLeft = $requestURLRaw.search(refs.$re);
12351234
return true;
12361235
}
12371236

@@ -4783,7 +4782,19 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar
47834782
break;
47844783
}
47854784
case 'uritransform': {
4786-
dnrAddRuleError(rule, `Incompatible with DNR: uritransform=${rule.__modifierValue}`);
4785+
const parsed = sfp.parseReplaceByRegexValue(rule.__modifierValue);
4786+
if ( parsed.re !== undefined ) {
4787+
dnrAddRuleError(rule, `Incompatible with DNR: uritransform=${rule.__modifierValue}`);
4788+
break;
4789+
}
4790+
if ( rule.condition.regexFilter === undefined ) {
4791+
dnrAddRuleError(rule, `Incompatible with DNR (need regexFilter): uritransform=${rule.__modifierValue}`);
4792+
break;
4793+
}
4794+
rule.action.type = 'redirect';
4795+
rule.action.redirect = {
4796+
regexSubstitution: parsed.replacement.replace(/\$(\d+)/g, '\\$1')
4797+
};
47874798
break;
47884799
}
47894800
case 'urlskip': {
@@ -5500,7 +5511,7 @@ function compareRedirectRequests(redirectEngine, a, b) {
55005511

55015512
/******************************************************************************/
55025513

5503-
StaticNetFilteringEngine.prototype.transformRequest = function(fctxt, out = []) {
5514+
StaticNetFilteringEngine.prototype.transformURL = function(fctxt, out = []) {
55045515
const directives = this.matchAndFetchModifiers(fctxt, 'uritransform');
55055516
if ( directives === undefined ) { return; }
55065517
const redirectURL = new URL(fctxt.url);
@@ -5514,17 +5525,21 @@ StaticNetFilteringEngine.prototype.transformRequest = function(fctxt, out = [])
55145525
}
55155526
const cache = directive.cache;
55165527
if ( cache === undefined ) { continue; }
5517-
const before = `${redirectURL.pathname}${redirectURL.search}${redirectURL.hash}`;
5518-
if ( cache.re.test(before) !== true ) { continue; }
5519-
const after = before.replace(cache.re, cache.replacement);
5528+
let { re } = cache;
5529+
const before = redirectURL.href;
5530+
if ( re === undefined ) {
5531+
const logdata = directive.logData();
5532+
if ( logdata === undefined ) { continue; }
5533+
try { re = new RegExp(logdata.regex, cache.flags); }
5534+
catch { continue; }
5535+
}
5536+
if ( re.test(before) !== true ) { continue; }
5537+
const after = before.replace(re, cache.replacement);
5538+
try { void new URL(after); } catch { continue; }
55205539
if ( after === before ) { continue; }
5521-
const hashPos = after.indexOf('#');
5522-
redirectURL.hash = hashPos !== -1 ? after.slice(hashPos) : '';
5523-
const afterMinusHash = hashPos !== -1 ? after.slice(0, hashPos) : after;
5524-
const searchPos = afterMinusHash.indexOf('?');
5525-
redirectURL.search = searchPos !== -1 ? afterMinusHash.slice(searchPos) : '';
5526-
redirectURL.pathname = searchPos !== -1 ? after.slice(0, searchPos) : after;
5540+
redirectURL.href = after;
55275541
out.push(directive);
5542+
break;
55285543
}
55295544
if ( out.length === 0 ) { return; }
55305545
if ( redirectURL.href !== fctxt.url ) {
@@ -5724,7 +5739,7 @@ StaticNetFilteringEngine.prototype.test = function(details) {
57245739
out.push('not blocked');
57255740
}
57265741
if ( r !== 1 ) {
5727-
const entries = this.transformRequest(fctxt);
5742+
const entries = this.transformURL(fctxt);
57285743
if ( entries ) {
57295744
for ( const entry of entries ) {
57305745
out.push(`modified: ${entry.logData().raw}`);

0 commit comments

Comments
 (0)