@@ -5356,12 +5356,10 @@ StaticNetFilteringEngine.prototype.transformRequest = function(fctxt, out = [])
5356
5356
out . push ( directive ) ;
5357
5357
continue ;
5358
5358
}
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 ) ;
5363
5361
}
5364
- const cache = refs . $ cache;
5362
+ const cache = directive . cache ;
5365
5363
if ( cache === undefined ) { continue ; }
5366
5364
const before = `${ redirectURL . pathname } ${ redirectURL . search } ${ redirectURL . hash } ` ;
5367
5365
if ( cache . re . test ( before ) !== true ) { continue ; }
@@ -5382,7 +5380,49 @@ StaticNetFilteringEngine.prototype.transformRequest = function(fctxt, out = [])
5382
5380
return out ;
5383
5381
} ;
5384
5382
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
+ * */
5386
5426
5387
5427
StaticNetFilteringEngine . prototype . urlSkip = function ( fctxt , out = [ ] ) {
5388
5428
if ( fctxt . redirectURL !== undefined ) { return ; }
@@ -5396,7 +5436,7 @@ StaticNetFilteringEngine.prototype.urlSkip = function(fctxt, out = []) {
5396
5436
const urlin = fctxt . url ;
5397
5437
const value = directive . value ;
5398
5438
const steps = value . includes ( ' ' ) && value . split ( / + / ) || [ value ] ;
5399
- const urlout = urlSkip ( urlin , steps ) ;
5439
+ const urlout = urlSkip ( directive , urlin , steps ) ;
5400
5440
if ( urlout === undefined ) { continue ; }
5401
5441
if ( urlout === urlin ) { continue ; }
5402
5442
fctxt . redirectURL = urlout ;
@@ -5407,41 +5447,52 @@ StaticNetFilteringEngine.prototype.urlSkip = function(fctxt, out = []) {
5407
5447
return out ;
5408
5448
} ;
5409
5449
5410
- function urlSkip ( urlin , steps ) {
5450
+ function urlSkip ( directive , urlin , steps ) {
5411
5451
try {
5412
- let urlout ;
5452
+ let urlout = urlin ;
5413
5453
for ( const step of steps ) {
5454
+ const urlin = urlout ;
5414
5455
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
- }
5425
5456
// Extract from URL parameter name at position i
5426
- if ( c0 === 0x26 ) { /* & */
5457
+ if ( c0 === 0x26 ) { // &
5427
5458
const i = ( parseInt ( step . slice ( 1 ) ) || 0 ) - 1 ;
5428
5459
if ( i < 0 ) { return ; }
5429
5460
const url = new URL ( urlin ) ;
5430
5461
if ( i >= url . searchParams . size ) { return ; }
5431
5462
const params = Array . from ( url . searchParams . keys ( ) ) ;
5432
- urlin = urlout = decodeURIComponent ( params [ i ] ) ;
5463
+ urlout = decodeURIComponent ( params [ i ] ) ;
5433
5464
continue ;
5434
5465
}
5435
5466
// Enforce https
5436
- if ( step === '+https' ) {
5467
+ if ( c0 === 0x2B && step === '+https' ) {
5437
5468
const s = urlin . replace ( / ^ h t t p s ? : \/ \/ / , '' ) ;
5438
5469
if ( / ^ [ \w - ] : \/ \/ / . test ( s ) ) { return ; }
5439
- urlin = urlout = `https://${ s } ` ;
5470
+ urlout = `https://${ s } ` ;
5440
5471
continue ;
5441
5472
}
5442
5473
// 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
+ }
5445
5496
continue ;
5446
5497
}
5447
5498
// Unknown directive
0 commit comments