mirror of
https://github.com/gorhill/uBlock.git
synced 2026-03-11 09:04:36 +00:00
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.
This commit is contained in:
parent
25d9964b1e
commit
aaf35d9d71
5 changed files with 50 additions and 26 deletions
|
|
@ -183,7 +183,7 @@ export async function benchmarkStaticNetFiltering(options = {}) {
|
||||||
if ( r === 1 ) { blockCount += 1; }
|
if ( r === 1 ) { blockCount += 1; }
|
||||||
else if ( r === 2 ) { allowCount += 1; }
|
else if ( r === 2 ) { allowCount += 1; }
|
||||||
if ( r !== 1 ) {
|
if ( r !== 1 ) {
|
||||||
if ( sfne.transformRequest(fctxt) ) {
|
if ( sfne.transformURL(fctxt) ) {
|
||||||
redirectCount += 1;
|
redirectCount += 1;
|
||||||
}
|
}
|
||||||
if ( sfne.hasQuery(fctxt) ) {
|
if ( sfne.hasQuery(fctxt) ) {
|
||||||
|
|
|
||||||
|
|
@ -1886,7 +1886,9 @@ const onMessage = function(request, sender, callback) {
|
||||||
return {
|
return {
|
||||||
name: assetKey,
|
name: assetKey,
|
||||||
text: details.content,
|
text: details.content,
|
||||||
trustedSource: assetKey.startsWith('ublock-'),
|
trustedSource: assetKey.startsWith('ublock-') ||
|
||||||
|
assetKey === µb.userFiltersPath &&
|
||||||
|
µb.userSettings.userFiltersTrusted,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -956,7 +956,7 @@ const PageStore = class {
|
||||||
|
|
||||||
redirectNonBlockedRequest(fctxt) {
|
redirectNonBlockedRequest(fctxt) {
|
||||||
const directives = [];
|
const directives = [];
|
||||||
staticNetFilteringEngine.transformRequest(fctxt, directives);
|
staticNetFilteringEngine.transformURL(fctxt, directives);
|
||||||
if ( staticNetFilteringEngine.hasQuery(fctxt) ) {
|
if ( staticNetFilteringEngine.hasQuery(fctxt) ) {
|
||||||
staticNetFilteringEngine.filterQuery(fctxt, directives);
|
staticNetFilteringEngine.filterQuery(fctxt, directives);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -841,6 +841,11 @@ export class AstFilterParser {
|
||||||
this.netOptionValueParser = new ArglistParser(',');
|
this.netOptionValueParser = new ArglistParser(',');
|
||||||
this.scriptletArgListParser = new ArglistParser(',');
|
this.scriptletArgListParser = new ArglistParser(',');
|
||||||
this.domainRegexValueParser = new ArglistParser('/');
|
this.domainRegexValueParser = new ArglistParser('/');
|
||||||
|
this.reNetOptionTokens = new RegExp(
|
||||||
|
`^(${Array.from(netOptionTokenDescriptors.keys())
|
||||||
|
.map(s => escapeForRegex(s))
|
||||||
|
.join('|')})\\b`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
finish() {
|
finish() {
|
||||||
|
|
@ -1512,12 +1517,7 @@ export class AstFilterParser {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
const before = s.charAt(j-1);
|
const before = s.charAt(j-1);
|
||||||
if ( before === '$' ) { return -1; }
|
if ( before === '$' ) { return -1; }
|
||||||
const after = s.charAt(j+1);
|
if ( this.reNetOptionTokens.test(s.slice(j+1)) ) { return j; }
|
||||||
if ( ')/|'.includes(after) === false ) {
|
|
||||||
if ( before === '' || '"\'\\`'.includes(before) === false ) {
|
|
||||||
return j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( j === start ) { break; }
|
if ( j === start ) { break; }
|
||||||
j = s.lastIndexOf('$', j-1);
|
j = s.lastIndexOf('$', j-1);
|
||||||
if ( j === -1 ) { break; }
|
if ( j === -1 ) { break; }
|
||||||
|
|
@ -3080,9 +3080,10 @@ export function parseReplaceByRegexValue(s) {
|
||||||
if ( parser.transform ) {
|
if ( parser.transform ) {
|
||||||
pattern = parser.normalizeArg(pattern);
|
pattern = parser.normalizeArg(pattern);
|
||||||
}
|
}
|
||||||
if ( pattern === '' ) { return; }
|
if ( pattern !== '' ) {
|
||||||
pattern = parser.normalizeArg(pattern, '$');
|
pattern = parser.normalizeArg(pattern, '$');
|
||||||
pattern = parser.normalizeArg(pattern, ',');
|
pattern = parser.normalizeArg(pattern, ',');
|
||||||
|
}
|
||||||
parser.nextArg(s, parser.separatorEnd);
|
parser.nextArg(s, parser.separatorEnd);
|
||||||
let replacement = s.slice(parser.argBeg, parser.argEnd);
|
let replacement = s.slice(parser.argBeg, parser.argEnd);
|
||||||
if ( parser.separatorEnd === parser.separatorBeg ) { return; }
|
if ( parser.separatorEnd === parser.separatorBeg ) { return; }
|
||||||
|
|
@ -3092,6 +3093,9 @@ export function parseReplaceByRegexValue(s) {
|
||||||
replacement = parser.normalizeArg(replacement, '$');
|
replacement = parser.normalizeArg(replacement, '$');
|
||||||
replacement = parser.normalizeArg(replacement, ',');
|
replacement = parser.normalizeArg(replacement, ',');
|
||||||
const flags = s.slice(parser.separatorEnd);
|
const flags = s.slice(parser.separatorEnd);
|
||||||
|
if ( pattern === '' ) {
|
||||||
|
return { flags, replacement }
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
return { re: new RegExp(pattern, flags), replacement };
|
return { re: new RegExp(pattern, flags), replacement };
|
||||||
} catch {
|
} catch {
|
||||||
|
|
@ -3101,7 +3105,10 @@ export function parseReplaceByRegexValue(s) {
|
||||||
export function parseReplaceValue(s) {
|
export function parseReplaceValue(s) {
|
||||||
if ( s.startsWith('/') ) {
|
if ( s.startsWith('/') ) {
|
||||||
const r = parseReplaceByRegexValue(s);
|
const r = parseReplaceByRegexValue(s);
|
||||||
if ( r ) { r.type = 'text'; }
|
if ( r ) {
|
||||||
|
if ( r.re === undefined ) { return; }
|
||||||
|
r.type = 'text';
|
||||||
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
const pos = s.indexOf(':');
|
const pos = s.indexOf(':');
|
||||||
|
|
|
||||||
|
|
@ -1231,7 +1231,6 @@ class FilterRegex {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if ( refs.$re.test($requestURLRaw) === false ) { return false; }
|
if ( refs.$re.test($requestURLRaw) === false ) { return false; }
|
||||||
$patternMatchLeft = $requestURLRaw.search(refs.$re);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4783,7 +4782,19 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'uritransform': {
|
case 'uritransform': {
|
||||||
dnrAddRuleError(rule, `Incompatible with DNR: uritransform=${rule.__modifierValue}`);
|
const parsed = sfp.parseReplaceByRegexValue(rule.__modifierValue);
|
||||||
|
if ( parsed.re !== undefined ) {
|
||||||
|
dnrAddRuleError(rule, `Incompatible with DNR: uritransform=${rule.__modifierValue}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ( rule.condition.regexFilter === undefined ) {
|
||||||
|
dnrAddRuleError(rule, `Incompatible with DNR (need regexFilter): uritransform=${rule.__modifierValue}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rule.action.type = 'redirect';
|
||||||
|
rule.action.redirect = {
|
||||||
|
regexSubstitution: parsed.replacement.replace(/\$(\d+)/g, '\\$1')
|
||||||
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'urlskip': {
|
case 'urlskip': {
|
||||||
|
|
@ -5500,7 +5511,7 @@ function compareRedirectRequests(redirectEngine, a, b) {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
StaticNetFilteringEngine.prototype.transformRequest = function(fctxt, out = []) {
|
StaticNetFilteringEngine.prototype.transformURL = function(fctxt, out = []) {
|
||||||
const directives = this.matchAndFetchModifiers(fctxt, 'uritransform');
|
const directives = this.matchAndFetchModifiers(fctxt, 'uritransform');
|
||||||
if ( directives === undefined ) { return; }
|
if ( directives === undefined ) { return; }
|
||||||
const redirectURL = new URL(fctxt.url);
|
const redirectURL = new URL(fctxt.url);
|
||||||
|
|
@ -5514,17 +5525,21 @@ StaticNetFilteringEngine.prototype.transformRequest = function(fctxt, out = [])
|
||||||
}
|
}
|
||||||
const cache = directive.cache;
|
const cache = directive.cache;
|
||||||
if ( cache === undefined ) { continue; }
|
if ( cache === undefined ) { continue; }
|
||||||
const before = `${redirectURL.pathname}${redirectURL.search}${redirectURL.hash}`;
|
let { re } = cache;
|
||||||
if ( cache.re.test(before) !== true ) { continue; }
|
const before = redirectURL.href;
|
||||||
const after = before.replace(cache.re, cache.replacement);
|
if ( re === undefined ) {
|
||||||
|
const logdata = directive.logData();
|
||||||
|
if ( logdata === undefined ) { continue; }
|
||||||
|
try { re = new RegExp(logdata.regex, cache.flags); }
|
||||||
|
catch { continue; }
|
||||||
|
}
|
||||||
|
if ( re.test(before) !== true ) { continue; }
|
||||||
|
const after = before.replace(re, cache.replacement);
|
||||||
|
try { void new URL(after); } catch { continue; }
|
||||||
if ( after === before ) { continue; }
|
if ( after === before ) { continue; }
|
||||||
const hashPos = after.indexOf('#');
|
redirectURL.href = after;
|
||||||
redirectURL.hash = hashPos !== -1 ? after.slice(hashPos) : '';
|
|
||||||
const afterMinusHash = hashPos !== -1 ? after.slice(0, hashPos) : after;
|
|
||||||
const searchPos = afterMinusHash.indexOf('?');
|
|
||||||
redirectURL.search = searchPos !== -1 ? afterMinusHash.slice(searchPos) : '';
|
|
||||||
redirectURL.pathname = searchPos !== -1 ? after.slice(0, searchPos) : after;
|
|
||||||
out.push(directive);
|
out.push(directive);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if ( out.length === 0 ) { return; }
|
if ( out.length === 0 ) { return; }
|
||||||
if ( redirectURL.href !== fctxt.url ) {
|
if ( redirectURL.href !== fctxt.url ) {
|
||||||
|
|
@ -5724,7 +5739,7 @@ StaticNetFilteringEngine.prototype.test = function(details) {
|
||||||
out.push('not blocked');
|
out.push('not blocked');
|
||||||
}
|
}
|
||||||
if ( r !== 1 ) {
|
if ( r !== 1 ) {
|
||||||
const entries = this.transformRequest(fctxt);
|
const entries = this.transformURL(fctxt);
|
||||||
if ( entries ) {
|
if ( entries ) {
|
||||||
for ( const entry of entries ) {
|
for ( const entry of entries ) {
|
||||||
out.push(`modified: ${entry.logData().raw}`);
|
out.push(`modified: ${entry.logData().raw}`);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue