@@ -52,6 +52,8 @@ function safeSelf() {
52
52
'Function_toStringFn' : self . Function . prototype . toString ,
53
53
'Function_toString' : thisArg => safe . Function_toStringFn . call ( thisArg ) ,
54
54
'Math_floor' : Math . floor ,
55
+ 'Math_max' : Math . max ,
56
+ 'Math_min' : Math . min ,
55
57
'Math_random' : Math . random ,
56
58
'Object_defineProperty' : Object . defineProperty . bind ( Object ) ,
57
59
'RegExp' : self . RegExp ,
@@ -242,6 +244,64 @@ function runAtHtmlElementFn(fn) {
242
244
243
245
/******************************************************************************/
244
246
247
+ // Reference:
248
+ // https://github.com/AdguardTeam/Scriptlets/blob/master/wiki/about-scriptlets.md#prevent-xhr
249
+
250
+ builtinScriptlets . push ( {
251
+ name : 'generate-content.fn' ,
252
+ fn : generateContentFn ,
253
+ dependencies : [
254
+ 'safe-self.fn' ,
255
+ ] ,
256
+ } ) ;
257
+ function generateContentFn ( directive ) {
258
+ const safe = safeSelf ( ) ;
259
+ const randomize = len => {
260
+ const chunks = [ ] ;
261
+ let textSize = 0 ;
262
+ do {
263
+ const s = safe . Math_random ( ) . toString ( 36 ) . slice ( 2 ) ;
264
+ chunks . push ( s ) ;
265
+ textSize += s . length ;
266
+ }
267
+ while ( textSize < len ) ;
268
+ return chunks . join ( ' ' ) . slice ( 0 , len ) ;
269
+ } ;
270
+ if ( directive === 'true' ) {
271
+ return Promise . resolve ( randomize ( 10 ) ) ;
272
+ }
273
+ if ( directive . startsWith ( 'length:' ) ) {
274
+ const match = / ^ l e n g t h : ( \d + ) (?: - ( \d + ) ) ? $ / . exec ( directive ) ;
275
+ if ( match ) {
276
+ const min = parseInt ( match [ 1 ] , 10 ) ;
277
+ const extent = safe . Math_max ( parseInt ( match [ 2 ] , 10 ) || 0 , min ) - min ;
278
+ const len = safe . Math_min ( min + extent * safe . Math_random ( ) , 500000 ) ;
279
+ return Promise . resolve ( randomize ( len | 0 ) ) ;
280
+ }
281
+ }
282
+ if ( directive . startsWith ( 'war:' ) && scriptletGlobals . has ( 'warOrigin' ) ) {
283
+ return new Promise ( resolve => {
284
+ const warOrigin = scriptletGlobals . get ( 'warOrigin' ) ;
285
+ const warName = directive . slice ( 4 ) ;
286
+ const fullpath = [ warOrigin , '/' , warName ] ;
287
+ const warSecret = scriptletGlobals . get ( 'warSecret' ) ;
288
+ if ( warSecret !== undefined ) {
289
+ fullpath . push ( '?secret=' , warSecret ) ;
290
+ }
291
+ const warXHR = new safe . XMLHttpRequest ( ) ;
292
+ warXHR . responseType = 'text' ;
293
+ warXHR . onloadend = ev => {
294
+ resolve ( ev . target . responseText || '' ) ;
295
+ } ;
296
+ warXHR . open ( 'GET' , fullpath . join ( '' ) ) ;
297
+ warXHR . send ( ) ;
298
+ } ) ;
299
+ }
300
+ return Promise . resolve ( '' ) ;
301
+ }
302
+
303
+ /******************************************************************************/
304
+
245
305
builtinScriptlets . push ( {
246
306
name : 'abort-current-script-core.fn' ,
247
307
fn : abortCurrentScriptCore ,
@@ -1757,16 +1817,18 @@ builtinScriptlets.push({
1757
1817
] ,
1758
1818
fn : noFetchIf ,
1759
1819
dependencies : [
1820
+ 'generate-content.fn' ,
1760
1821
'safe-self.fn' ,
1761
1822
] ,
1762
1823
} ) ;
1763
1824
function noFetchIf (
1764
- arg1 = '' ,
1825
+ propsToMatch = '' ,
1826
+ directive = ''
1765
1827
) {
1766
- if ( typeof arg1 !== 'string' ) { return ; }
1828
+ if ( typeof propsToMatch !== 'string' ) { return ; }
1767
1829
const safe = safeSelf ( ) ;
1768
1830
const needles = [ ] ;
1769
- for ( const condition of arg1 . split ( / \s + / ) ) {
1831
+ for ( const condition of propsToMatch . split ( / \s + / ) ) {
1770
1832
if ( condition === '' ) { continue ; }
1771
1833
const pos = condition . indexOf ( ':' ) ;
1772
1834
let key , value ;
@@ -1782,14 +1844,11 @@ function noFetchIf(
1782
1844
const log = needles . length === 0 ? console . log . bind ( console ) : undefined ;
1783
1845
self . fetch = new Proxy ( self . fetch , {
1784
1846
apply : function ( target , thisArg , args ) {
1847
+ const details = args [ 0 ] instanceof self . Request
1848
+ ? args [ 0 ]
1849
+ : Object . assign ( { url : args [ 0 ] } , args [ 1 ] ) ;
1785
1850
let proceed = true ;
1786
1851
try {
1787
- let details ;
1788
- if ( args [ 0 ] instanceof self . Request ) {
1789
- details = args [ 0 ] ;
1790
- } else {
1791
- details = Object . assign ( { url : args [ 0 ] } , args [ 1 ] ) ;
1792
- }
1793
1852
const props = new Map ( ) ;
1794
1853
for ( const prop in details ) {
1795
1854
let v = details [ prop ] ;
@@ -1818,9 +1877,21 @@ function noFetchIf(
1818
1877
}
1819
1878
} catch ( ex ) {
1820
1879
}
1821
- return proceed
1822
- ? Reflect . apply ( target , thisArg , args )
1823
- : Promise . resolve ( new Response ( ) ) ;
1880
+ if ( proceed ) {
1881
+ return Reflect . apply ( target , thisArg , args ) ;
1882
+ }
1883
+ return generateContentFn ( directive ) . then ( text => {
1884
+ const response = new Response ( text , {
1885
+ statusText : 'OK' ,
1886
+ headers : {
1887
+ 'Content-Length' : text . length ,
1888
+ }
1889
+ } ) ;
1890
+ Object . defineProperty ( response , 'url' , {
1891
+ value : details . url
1892
+ } ) ;
1893
+ return response ;
1894
+ } ) ;
1824
1895
}
1825
1896
} ) ;
1826
1897
}
@@ -2259,6 +2330,7 @@ builtinScriptlets.push({
2259
2330
] ,
2260
2331
fn : noXhrIf ,
2261
2332
dependencies : [
2333
+ 'generate-content.fn' ,
2262
2334
'match-object-properties.fn' ,
2263
2335
'parse-properties-to-match.fn' ,
2264
2336
'safe-self.fn' ,
@@ -2269,41 +2341,10 @@ function noXhrIf(
2269
2341
directive = ''
2270
2342
) {
2271
2343
if ( typeof propsToMatch !== 'string' ) { return ; }
2272
- const safe = safeSelf ( ) ;
2273
2344
const xhrInstances = new WeakMap ( ) ;
2274
2345
const propNeedles = parsePropertiesToMatch ( propsToMatch , 'url' ) ;
2275
2346
const log = propNeedles . size === 0 ? console . log . bind ( console ) : undefined ;
2276
2347
const warOrigin = scriptletGlobals . get ( 'warOrigin' ) ;
2277
- const generateRandomString = len => {
2278
- let s = '' ;
2279
- do { s += safe . Math_random ( ) . toString ( 36 ) . slice ( 2 ) ; }
2280
- while ( s . length < 10 ) ;
2281
- return s . slice ( 0 , len ) ;
2282
- } ;
2283
- const generateContent = async directive => {
2284
- if ( directive === 'true' ) {
2285
- return generateRandomString ( 10 ) ;
2286
- }
2287
- if ( directive . startsWith ( 'war:' ) ) {
2288
- if ( warOrigin === undefined ) { return '' ; }
2289
- return new Promise ( resolve => {
2290
- const warName = directive . slice ( 4 ) ;
2291
- const fullpath = [ warOrigin , '/' , warName ] ;
2292
- const warSecret = scriptletGlobals . get ( 'warSecret' ) ;
2293
- if ( warSecret !== undefined ) {
2294
- fullpath . push ( '?secret=' , warSecret ) ;
2295
- }
2296
- const warXHR = new safe . XMLHttpRequest ( ) ;
2297
- warXHR . responseType = 'text' ;
2298
- warXHR . onloadend = ev => {
2299
- resolve ( ev . target . responseText || '' ) ;
2300
- } ;
2301
- warXHR . open ( 'GET' , fullpath . join ( '' ) ) ;
2302
- warXHR . send ( ) ;
2303
- } ) ;
2304
- }
2305
- return '' ;
2306
- } ;
2307
2348
self . XMLHttpRequest = class extends self . XMLHttpRequest {
2308
2349
open ( method , url , ...args ) {
2309
2350
if ( log !== undefined ) {
@@ -2370,7 +2411,7 @@ function noXhrIf(
2370
2411
default :
2371
2412
if ( directive === '' ) { break ; }
2372
2413
promise = promise . then ( details => {
2373
- return generateContent ( details . directive ) . then ( text => {
2414
+ return generateContentFn ( details . directive ) . then ( text => {
2374
2415
details . props . response . value = text ;
2375
2416
details . props . responseText . value = text ;
2376
2417
return details ;
0 commit comments