Skip to content

Commit c86ed52

Browse files
committed
Add regex extraction transformation step to urlskip= option
Related feedback: uBlockOrigin/uBlock-issues#3206 (comment) The first capture group of the regex will be used as the result of the transformation. Example: ||podtrac.com/pts/redirect.mp3/$urlskip=/podtrac\.com\/pts\/redirect\.mp3\/(.*?\.mp3\b)/ +https If the regex is invalid, or if it fails to extract a first capture group, no redirection will occur.
1 parent 913f20f commit c86ed52

File tree

1 file changed

+76
-25
lines changed

1 file changed

+76
-25
lines changed

src/js/static-net-filtering.js

Lines changed: 76 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5356,12 +5356,10 @@ StaticNetFilteringEngine.prototype.transformRequest = function(fctxt, out = [])
53565356
out.push(directive);
53575357
continue;
53585358
}
5359-
const { refs } = directive;
5360-
if ( refs instanceof Object === false ) { continue; }
5361-
if ( refs.$cache === null ) {
5362-
refs.$cache = sfp.parseReplaceValue(refs.value);
5359+
if ( directive.cache === null ) {
5360+
directive.cache = sfp.parseReplaceValue(directive.value);
53635361
}
5364-
const cache = refs.$cache;
5362+
const cache = directive.cache;
53655363
if ( cache === undefined ) { continue; }
53665364
const before = `${redirectURL.pathname}${redirectURL.search}${redirectURL.hash}`;
53675365
if ( cache.re.test(before) !== true ) { continue; }
@@ -5382,7 +5380,49 @@ StaticNetFilteringEngine.prototype.transformRequest = function(fctxt, out = [])
53825380
return out;
53835381
};
53845382

5385-
/******************************************************************************/
5383+
/**
5384+
* @trustedOption urlkip
5385+
*
5386+
* @description
5387+
* Extract a URL from another URL according to one or more transformation steps,
5388+
* thereby skipping over intermediate network request(s) to remote servers.
5389+
* Requires a trusted source.
5390+
*
5391+
* @param steps
5392+
* A serie of space-separated directives representing the transformation steps
5393+
* to perform to extract the final URL to which a network request should be
5394+
* redirected.
5395+
*
5396+
* Supported directives:
5397+
*
5398+
* `?name`: extract the value of parameter `name` as the current string.
5399+
*
5400+
* `&i`: extract the name of the parameter at position `i` as the current
5401+
* string. The position is 1-based.
5402+
*
5403+
* `/.../`: extract the first capture group of a regex as the current string.
5404+
*
5405+
* `+https`: prepend the current string with `https://`.
5406+
*
5407+
* `-base64`: decode the current string as a base64-encoded string.
5408+
*
5409+
* At any given step, the currently extracted string may not necessarily be
5410+
* a valid URL, and more transformation steps may be needed to obtain a valid
5411+
* URL once all the steps are applied.
5412+
*
5413+
* An unsupported step or a failed step will abort the transformation and no
5414+
* redirection will be performed.
5415+
*
5416+
* The final step is expected to yield a valid URL. If the result is not a
5417+
* valid URL, no redirection will be performed.
5418+
*
5419+
* @example
5420+
* ||example.com/path/to/tracker$urlskip=?url
5421+
* ||example.com/path/to/tracker$urlskip=?url ?to
5422+
* ||pixiv.net/jump.php?$urlskip=&1
5423+
* ||podtrac.com/pts/redirect.mp3/$urlskip=/podtrac\.com\/pts\/redirect\.mp3\/(.*?\.mp3\b)/ +https
5424+
*
5425+
* */
53865426

53875427
StaticNetFilteringEngine.prototype.urlSkip = function(fctxt, out = []) {
53885428
if ( fctxt.redirectURL !== undefined ) { return; }
@@ -5396,7 +5436,7 @@ StaticNetFilteringEngine.prototype.urlSkip = function(fctxt, out = []) {
53965436
const urlin = fctxt.url;
53975437
const value = directive.value;
53985438
const steps = value.includes(' ') && value.split(/ +/) || [ value ];
5399-
const urlout = urlSkip(urlin, steps);
5439+
const urlout = urlSkip(directive, urlin, steps);
54005440
if ( urlout === undefined ) { continue; }
54015441
if ( urlout === urlin ) { continue; }
54025442
fctxt.redirectURL = urlout;
@@ -5407,41 +5447,52 @@ StaticNetFilteringEngine.prototype.urlSkip = function(fctxt, out = []) {
54075447
return out;
54085448
};
54095449

5410-
function urlSkip(urlin, steps) {
5450+
function urlSkip(directive, urlin, steps) {
54115451
try {
5412-
let urlout;
5452+
let urlout = urlin;
54135453
for ( const step of steps ) {
5454+
const urlin = urlout;
54145455
const c0 = step.charCodeAt(0);
5415-
// Extract from URL parameter
5416-
if ( c0 === 0x3F ) { /* ? */
5417-
urlout = (new URL(urlin)).searchParams.get(step.slice(1));
5418-
if ( urlout === null ) { return; }
5419-
if ( urlout.includes(' ') ) {
5420-
urlout = urlout.replace(/ /g, '%20');
5421-
}
5422-
urlin = urlout;
5423-
continue;
5424-
}
54255456
// Extract from URL parameter name at position i
5426-
if ( c0 === 0x26 ) { /* & */
5457+
if ( c0 === 0x26 ) { // &
54275458
const i = (parseInt(step.slice(1)) || 0) - 1;
54285459
if ( i < 0 ) { return; }
54295460
const url = new URL(urlin);
54305461
if ( i >= url.searchParams.size ) { return; }
54315462
const params = Array.from(url.searchParams.keys());
5432-
urlin = urlout = decodeURIComponent(params[i]);
5463+
urlout = decodeURIComponent(params[i]);
54335464
continue;
54345465
}
54355466
// Enforce https
5436-
if ( step === '+https' ) {
5467+
if ( c0 === 0x2B && step === '+https' ) {
54375468
const s = urlin.replace(/^https?:\/\//, '');
54385469
if ( /^[\w-]:\/\//.test(s) ) { return; }
5439-
urlin = urlout = `https://${s}`;
5470+
urlout = `https://${s}`;
54405471
continue;
54415472
}
54425473
// Decode base64
5443-
if ( step === '-base64' ) {
5444-
urlin = urlout = self.atob(urlin);
5474+
if ( c0 === 0x2D && step === '-base64' ) {
5475+
urlout = self.atob(urlin);
5476+
continue;
5477+
}
5478+
// Regex extraction from first capture group
5479+
if ( c0 === 0x2F ) { // /
5480+
if ( directive.cache === null ) {
5481+
directive.cache = new RegExp(step.slice(1, -1));
5482+
}
5483+
const match = directive.cache.exec(urlin);
5484+
if ( match === null ) { return; }
5485+
if ( match.length <= 1 ) { return; }
5486+
urlout = match[1];
5487+
continue;
5488+
}
5489+
// Extract from URL parameter
5490+
if ( c0 === 0x3F ) { // ?
5491+
urlout = (new URL(urlin)).searchParams.get(step.slice(1));
5492+
if ( urlout === null ) { return; }
5493+
if ( urlout.includes(' ') ) {
5494+
urlout = urlout.replace(/ /g, '%20');
5495+
}
54455496
continue;
54465497
}
54475498
// Unknown directive

0 commit comments

Comments
 (0)