@browserless.io/browserless 2.16.0 → 2.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CHANGELOG.md +5 -1
  2. package/bin/browserless.js +6 -4
  3. package/bin/scaffold/src/hello-world.http.ts +1 -1
  4. package/build/routes/chrome/http/content.post.body.json +38 -8
  5. package/build/routes/chrome/http/pdf.post.body.json +38 -8
  6. package/build/routes/chrome/http/scrape.post.body.json +38 -8
  7. package/build/routes/chrome/http/screenshot.post.body.json +38 -8
  8. package/build/routes/chromium/http/content.post.body.json +38 -8
  9. package/build/routes/chromium/http/pdf.post.body.json +38 -8
  10. package/build/routes/chromium/http/scrape.post.body.json +38 -8
  11. package/build/routes/chromium/http/screenshot.post.body.json +38 -8
  12. package/extensions/ublock/_locales/be/messages.json +1 -1
  13. package/extensions/ublock/_locales/br_FR/messages.json +2 -2
  14. package/extensions/ublock/_locales/bs/messages.json +5 -5
  15. package/extensions/ublock/_locales/eu/messages.json +1 -1
  16. package/extensions/ublock/_locales/it/messages.json +21 -21
  17. package/extensions/ublock/_locales/kn/messages.json +13 -13
  18. package/extensions/ublock/_locales/lt/messages.json +1 -1
  19. package/extensions/ublock/_locales/ms/messages.json +9 -9
  20. package/extensions/ublock/_locales/nb/messages.json +2 -2
  21. package/extensions/ublock/_locales/no/messages.json +2 -2
  22. package/extensions/ublock/_locales/sv/messages.json +2 -2
  23. package/extensions/ublock/_locales/tr/messages.json +4 -4
  24. package/extensions/ublock/_locales/zh_TW/messages.json +17 -17
  25. package/extensions/ublock/assets/assets.json +4 -3
  26. package/extensions/ublock/assets/resources/scriptlets.js +208 -57
  27. package/extensions/ublock/assets/thirdparties/easylist/easylist.txt +3584 -4032
  28. package/extensions/ublock/assets/thirdparties/easylist/easyprivacy.txt +3732 -1683
  29. package/extensions/ublock/assets/thirdparties/pgl.yoyo.org/as/serverlist +27 -205
  30. package/extensions/ublock/assets/thirdparties/publicsuffix.org/list/effective_tld_names.dat +465 -434
  31. package/extensions/ublock/assets/thirdparties/urlhaus-filter/urlhaus-filter-online.txt +3232 -1382
  32. package/extensions/ublock/assets/ublock/badware.min.txt +867 -408
  33. package/extensions/ublock/assets/ublock/filters.min.txt +1382 -937
  34. package/extensions/ublock/assets/ublock/privacy.min.txt +43 -56
  35. package/extensions/ublock/assets/ublock/quick-fixes.min.txt +127 -71
  36. package/extensions/ublock/assets/ublock/unbreak.min.txt +145 -27
  37. package/extensions/ublock/js/contentscript-extra.js +25 -0
  38. package/extensions/ublock/js/contentscript.js +3 -1
  39. package/extensions/ublock/js/cosmetic-filtering.js +8 -7
  40. package/extensions/ublock/js/dom.js +8 -4
  41. package/extensions/ublock/js/epicker-ui.js +1 -1
  42. package/extensions/ublock/js/s14e-serializer.js +2 -1
  43. package/extensions/ublock/js/static-filtering-parser.js +255 -254
  44. package/extensions/ublock/js/storage.js +7 -6
  45. package/extensions/ublock/js/traffic.js +33 -34
  46. package/extensions/ublock/manifest.json +1 -1
  47. package/extensions/ublock/popup-fenix.html +1 -1
  48. package/package.json +15 -15
  49. package/static/debugger/index.html +1 -1
  50. package/static/debugger/router.js +1 -0
  51. package/static/debugger/tracker.js +1 -1
  52. package/static/docs/swagger.json +40 -10
  53. package/static/docs/swagger.min.json +39 -9
  54. package/static/function/client.js +4120 -16782
  55. package/static/function/index.html +4120 -16782
  56. package/static/debugger/router.bundle.js +0 -2
  57. package/static/debugger/router.bundle.js.map +0 -1
  58. package/static/debugger/router.d.ts +0 -2
@@ -57,12 +57,14 @@ function safeSelf() {
57
57
  'Math_random': Math.random,
58
58
  'Object': Object,
59
59
  'Object_defineProperty': Object.defineProperty.bind(Object),
60
+ 'Object_defineProperties': Object.defineProperties.bind(Object),
60
61
  'Object_fromEntries': Object.fromEntries.bind(Object),
61
62
  'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object),
62
63
  'RegExp': self.RegExp,
63
64
  'RegExp_test': self.RegExp.prototype.test,
64
65
  'RegExp_exec': self.RegExp.prototype.exec,
65
66
  'Request_clone': self.Request.prototype.clone,
67
+ 'String_fromCharCode': String.fromCharCode,
66
68
  'XMLHttpRequest': self.XMLHttpRequest,
67
69
  'addEventListener': self.EventTarget.prototype.addEventListener,
68
70
  'removeEventListener': self.EventTarget.prototype.removeEventListener,
@@ -210,7 +212,7 @@ builtinScriptlets.push({
210
212
  function getExceptionToken() {
211
213
  const safe = safeSelf();
212
214
  const token =
213
- String.fromCharCode(Date.now() % 26 + 97) +
215
+ safe.String_fromCharCode(Date.now() % 26 + 97) +
214
216
  safe.Math_floor(safe.Math_random() * 982451653 + 982451653).toString(36);
215
217
  const oe = self.onerror;
216
218
  self.onerror = function(msg, ...args) {
@@ -484,9 +486,8 @@ builtinScriptlets.push({
484
486
  'safe-self.fn',
485
487
  ],
486
488
  });
487
- function validateConstantFn(trusted, raw) {
489
+ function validateConstantFn(trusted, raw, extraArgs = {}) {
488
490
  const safe = safeSelf();
489
- const extraArgs = safe.getExtraArgs(Array.from(arguments), 2);
490
491
  let value;
491
492
  if ( raw === 'undefined' ) {
492
493
  value = undefined;
@@ -586,7 +587,7 @@ function setConstantFn(
586
587
  };
587
588
  if ( trappedProp === '' ) { return; }
588
589
  const thisScript = document.currentScript;
589
- let normalValue = validateConstantFn(trusted, rawValue);
590
+ let normalValue = validateConstantFn(trusted, rawValue, extraArgs);
590
591
  if ( rawValue === 'noopFunc' || rawValue === 'trueFunc' || rawValue === 'falseFunc' ) {
591
592
  normalValue = cloakFunc(normalValue);
592
593
  }
@@ -714,7 +715,12 @@ function replaceNodeTextFn(
714
715
  const reNodeName = safe.patternToRegex(nodeName, 'i', true);
715
716
  const rePattern = safe.patternToRegex(pattern, 'gms');
716
717
  const extraArgs = safe.getExtraArgs(Array.from(arguments), 3);
717
- const reCondition = safe.patternToRegex(extraArgs.condition || '', 'ms');
718
+ const reIncludes = extraArgs.includes || extraArgs.condition
719
+ ? safe.patternToRegex(extraArgs.includes || extraArgs.condition, 'ms')
720
+ : null;
721
+ const reExcludes = extraArgs.excludes
722
+ ? safe.patternToRegex(extraArgs.excludes, 'ms')
723
+ : null;
718
724
  const stop = (takeRecord = true) => {
719
725
  if ( takeRecord ) {
720
726
  handleMutations(observer.takeRecords());
@@ -727,8 +733,14 @@ function replaceNodeTextFn(
727
733
  let sedCount = extraArgs.sedCount || 0;
728
734
  const handleNode = node => {
729
735
  const before = node.textContent;
730
- reCondition.lastIndex = 0;
731
- if ( safe.RegExp_test.call(reCondition, before) === false ) { return true; }
736
+ if ( reIncludes ) {
737
+ reIncludes.lastIndex = 0;
738
+ if ( safe.RegExp_test.call(reIncludes, before) === false ) { return true; }
739
+ }
740
+ if ( reExcludes ) {
741
+ reExcludes.lastIndex = 0;
742
+ if ( safe.RegExp_test.call(reExcludes, before) ) { return true; }
743
+ }
732
744
  rePattern.lastIndex = 0;
733
745
  if ( safe.RegExp_test.call(rePattern, before) === false ) { return true; }
734
746
  rePattern.lastIndex = 0;
@@ -764,6 +776,7 @@ function replaceNodeTextFn(
764
776
  count += 1;
765
777
  if ( node === null ) { break; }
766
778
  if ( reNodeName.test(node.nodeName) === false ) { continue; }
779
+ if ( node === document.currentScript ) { continue; }
767
780
  if ( handleNode(node) ) { continue; }
768
781
  stop(); break;
769
782
  }
@@ -1008,6 +1021,8 @@ function setCookieFn(
1008
1021
  cookieParts.push(`; domain=${options.domain}`);
1009
1022
  }
1010
1023
  cookieParts.push('; Secure');
1024
+ } else if ( /^__(Host|Secure)-/.test(name) ) {
1025
+ cookieParts.push('; Secure');
1011
1026
  }
1012
1027
 
1013
1028
  try {
@@ -1332,6 +1347,8 @@ function replaceFetchResponseFn(
1332
1347
  if ( pattern === '*' ) { pattern = '.*'; }
1333
1348
  const rePattern = safe.patternToRegex(pattern);
1334
1349
  const propNeedles = parsePropertiesToMatch(propsToMatch, 'url');
1350
+ const extraArgs = safe.getExtraArgs(Array.from(arguments), 4);
1351
+ const reIncludes = extraArgs.includes ? safe.patternToRegex(extraArgs.includes) : null;
1335
1352
  self.fetch = new Proxy(self.fetch, {
1336
1353
  apply: function(target, thisArg, args) {
1337
1354
  const fetchPromise = Reflect.apply(target, thisArg, args);
@@ -1361,6 +1378,9 @@ function replaceFetchResponseFn(
1361
1378
  return fetchPromise.then(responseBefore => {
1362
1379
  const response = responseBefore.clone();
1363
1380
  return response.text().then(textBefore => {
1381
+ if ( reIncludes && reIncludes.test(textBefore) === false ) {
1382
+ return responseBefore;
1383
+ }
1364
1384
  const textAfter = textBefore.replace(rePattern, replacement);
1365
1385
  const outcome = textAfter !== textBefore ? 'match' : 'nomatch';
1366
1386
  if ( outcome === 'nomatch' ) { return responseBefore; }
@@ -1642,6 +1662,8 @@ function addEventListenerDefuser(
1642
1662
  const debug = shouldDebug(extraArgs);
1643
1663
  const targetSelector = extraArgs.elements || undefined;
1644
1664
  const elementMatches = elem => {
1665
+ if ( targetSelector === 'window' ) { return elem === window; }
1666
+ if ( targetSelector === 'document' ) { return elem === document; }
1645
1667
  if ( elem && elem.matches && elem.matches(targetSelector) ) { return true; }
1646
1668
  const elems = Array.from(document.querySelectorAll(targetSelector));
1647
1669
  return elems.includes(elem);
@@ -1651,7 +1673,9 @@ function addEventListenerDefuser(
1651
1673
  if ( elem instanceof Document ) { return 'document'; }
1652
1674
  if ( elem instanceof Element === false ) { return '?'; }
1653
1675
  const parts = [];
1654
- if ( elem.id !== '' ) { parts.push(`#${CSS.escape(elem.id)}`); }
1676
+ // https://github.com/uBlockOrigin/uAssets/discussions/17907#discussioncomment-9871079
1677
+ const id = String(elem.id);
1678
+ if ( id !== '' ) { parts.push(`#${CSS.escape(id)}`); }
1655
1679
  for ( let i = 0; i < elem.classList.length; i++ ) {
1656
1680
  parts.push(`.${CSS.escape(elem.classList.item(i))}`);
1657
1681
  }
@@ -2055,11 +2079,11 @@ builtinScriptlets.push({
2055
2079
  });
2056
2080
  function noFetchIf(
2057
2081
  propsToMatch = '',
2058
- responseBody = ''
2082
+ responseBody = '',
2083
+ responseType = ''
2059
2084
  ) {
2060
- if ( typeof propsToMatch !== 'string' ) { return; }
2061
2085
  const safe = safeSelf();
2062
- const logPrefix = safe.makeLogPrefix('prevent-fetch', propsToMatch, responseBody);
2086
+ const logPrefix = safe.makeLogPrefix('prevent-fetch', propsToMatch, responseBody, responseType);
2063
2087
  const needles = [];
2064
2088
  for ( const condition of propsToMatch.split(/\s+/) ) {
2065
2089
  if ( condition === '' ) { continue; }
@@ -2074,6 +2098,28 @@ function noFetchIf(
2074
2098
  }
2075
2099
  needles.push({ key, re: safe.patternToRegex(value) });
2076
2100
  }
2101
+ const validResponseProps = {
2102
+ ok: [ false, true ],
2103
+ statusText: [ '', 'Not Found' ],
2104
+ type: [ 'basic', 'cors', 'default', 'error', 'opaque' ],
2105
+ };
2106
+ const responseProps = {
2107
+ statusText: { value: 'OK' },
2108
+ };
2109
+ if ( /^\{.*\}$/.test(responseType) ) {
2110
+ try {
2111
+ Object.entries(JSON.parse(responseType)).forEach(([ p, v ]) => {
2112
+ if ( validResponseProps[p] === undefined ) { return; }
2113
+ if ( validResponseProps[p].includes(v) === false ) { return; }
2114
+ responseProps[p] = { value: v };
2115
+ });
2116
+ }
2117
+ catch(ex) {}
2118
+ } else if ( responseType !== '' ) {
2119
+ if ( validResponseProps.type.includes(responseType) ) {
2120
+ responseProps.type = { value: responseType };
2121
+ }
2122
+ }
2077
2123
  self.fetch = new Proxy(self.fetch, {
2078
2124
  apply: function(target, thisArg, args) {
2079
2125
  const details = args[0] instanceof self.Request
@@ -2111,33 +2157,18 @@ function noFetchIf(
2111
2157
  if ( proceed ) {
2112
2158
  return Reflect.apply(target, thisArg, args);
2113
2159
  }
2114
- let responseType = '';
2115
- if ( details.mode === undefined || details.mode === 'cors' ) {
2116
- try {
2117
- const desURL = new URL(details.url);
2118
- responseType = desURL.origin !== document.location.origin
2119
- ? 'cors'
2120
- : 'basic';
2121
- } catch(ex) {
2122
- safe.uboErr(logPrefix, `Error: ${ex}`);
2123
- }
2124
- }
2125
2160
  return generateContentFn(responseBody).then(text => {
2126
2161
  safe.uboLog(logPrefix, `Prevented with response "${text}"`);
2127
2162
  const response = new Response(text, {
2128
- statusText: 'OK',
2129
2163
  headers: {
2130
2164
  'Content-Length': text.length,
2131
2165
  }
2132
2166
  });
2133
- safe.Object_defineProperty(response, 'url', {
2134
- value: details.url
2135
- });
2136
- if ( responseType !== '' ) {
2137
- safe.Object_defineProperty(response, 'type', {
2138
- value: responseType
2139
- });
2140
- }
2167
+ const props = Object.assign(
2168
+ { url: { value: details.url } },
2169
+ responseProps
2170
+ );
2171
+ safe.Object_defineProperties(response, props);
2141
2172
  return response;
2142
2173
  });
2143
2174
  }
@@ -3465,7 +3496,7 @@ function hrefSanitizer(
3465
3496
  };
3466
3497
  const validateURL = text => {
3467
3498
  if ( text === '' ) { return ''; }
3468
- if ( /[^\x21-\x7e]/.test(text) ) { return ''; }
3499
+ if ( /[\x00-\x20\x7f]/.test(text) ) { return ''; }
3469
3500
  try {
3470
3501
  const url = new URL(text, document.location);
3471
3502
  return url.href;
@@ -3473,17 +3504,26 @@ function hrefSanitizer(
3473
3504
  }
3474
3505
  return '';
3475
3506
  };
3507
+ const extractParam = (href, source) => {
3508
+ if ( Boolean(source) === false ) { return href; }
3509
+ const recursive = source.includes('?', 1);
3510
+ const end = recursive ? source.indexOf('?', 1) : source.length;
3511
+ try {
3512
+ const url = new URL(href, document.location);
3513
+ const value = url.searchParams.get(source.slice(1, end));
3514
+ if ( value === null ) { return href }
3515
+ if ( recursive ) { return extractParam(value, source.slice(end)); }
3516
+ return value;
3517
+ } catch(x) {
3518
+ }
3519
+ return href;
3520
+ };
3476
3521
  const extractText = (elem, source) => {
3477
3522
  if ( /^\[.*\]$/.test(source) ) {
3478
3523
  return elem.getAttribute(source.slice(1,-1).trim()) || '';
3479
3524
  }
3480
3525
  if ( source.startsWith('?') ) {
3481
- try {
3482
- const url = new URL(elem.href, document.location);
3483
- return url.searchParams.get(source.slice(1)) || '';
3484
- } catch(x) {
3485
- }
3486
- return '';
3526
+ return extractParam(elem.href, source);
3487
3527
  }
3488
3528
  if ( source === 'text' ) {
3489
3529
  return elem.textContent
@@ -3730,10 +3770,10 @@ builtinScriptlets.push({
3730
3770
  });
3731
3771
  function removeNodeText(
3732
3772
  nodeName,
3733
- condition,
3773
+ includes,
3734
3774
  ...extraArgs
3735
3775
  ) {
3736
- replaceNodeTextFn(nodeName, '', '', 'condition', condition || '', ...extraArgs);
3776
+ replaceNodeTextFn(nodeName, '', '', 'includes', includes || '', ...extraArgs);
3737
3777
  }
3738
3778
 
3739
3779
  /*******************************************************************************
@@ -3778,6 +3818,9 @@ function setCookie(
3778
3818
  'yes', 'y', 'no', 'n',
3779
3819
  'necessary', 'required',
3780
3820
  'approved', 'disapproved',
3821
+ 'hide', 'hidden',
3822
+ 'essential', 'nonessential',
3823
+ 'dismiss', 'dismissed',
3781
3824
  ];
3782
3825
  const normalized = value.toLowerCase();
3783
3826
  const match = /^("?)(.+)\1$/.exec(normalized);
@@ -4228,10 +4271,14 @@ function trustedSetCookie(
4228
4271
  const logPrefix = safe.makeLogPrefix('set-cookie', name, value, path);
4229
4272
  const time = new Date();
4230
4273
 
4231
- if ( value === '$now$' ) {
4232
- value = Date.now();
4233
- } else if ( value === '$currentDate$' ) {
4234
- value = time.toUTCString();
4274
+ if ( value.includes('$now$') ) {
4275
+ value = value.replaceAll('$now$', time.getTime());
4276
+ }
4277
+ if ( value.includes('$currentDate$') ) {
4278
+ value = value.replaceAll('$currentDate$', time.toUTCString());
4279
+ }
4280
+ if ( value.includes('$currentISODate$') ) {
4281
+ value = value.replaceAll('$currentISODate$', time.toISOString());
4235
4282
  }
4236
4283
 
4237
4284
  let expires = '';
@@ -4362,6 +4409,8 @@ function trustedReplaceXhrResponse(
4362
4409
  if ( pattern === '*' ) { pattern = '.*'; }
4363
4410
  const rePattern = safe.patternToRegex(pattern);
4364
4411
  const propNeedles = parsePropertiesToMatch(propsToMatch, 'url');
4412
+ const extraArgs = safe.getExtraArgs(Array.from(arguments), 3);
4413
+ const reIncludes = extraArgs.includes ? safe.patternToRegex(extraArgs.includes) : null;
4365
4414
  self.XMLHttpRequest = class extends self.XMLHttpRequest {
4366
4415
  open(method, url, ...args) {
4367
4416
  const outerXhr = this;
@@ -4399,6 +4448,9 @@ function trustedReplaceXhrResponse(
4399
4448
  if ( typeof innerResponse !== 'string' ) {
4400
4449
  return (xhrDetails.response = innerResponse);
4401
4450
  }
4451
+ if ( reIncludes && reIncludes.test(innerResponse) === false ) {
4452
+ return (xhrDetails.response = innerResponse);
4453
+ }
4402
4454
  const textBefore = innerResponse;
4403
4455
  const textAfter = textBefore.replace(rePattern, replacement);
4404
4456
  if ( textAfter !== textBefore ) {
@@ -4733,24 +4785,29 @@ builtinScriptlets.push({
4733
4785
  });
4734
4786
  function trustedReplaceArgument(
4735
4787
  propChain = '',
4736
- argpos = '',
4788
+ argposRaw = '',
4737
4789
  argraw = ''
4738
4790
  ) {
4739
4791
  if ( propChain === '' ) { return; }
4740
- if ( argpos === '' ) { return; }
4741
- if ( argraw === '' ) { return; }
4742
4792
  const safe = safeSelf();
4743
- const logPrefix = safe.makeLogPrefix('trusted-replace-argument', propChain, argpos, argraw);
4793
+ const logPrefix = safe.makeLogPrefix('trusted-replace-argument', propChain, argposRaw, argraw);
4794
+ const argpos = parseInt(argposRaw, 10) || 0;
4744
4795
  const extraArgs = safe.getExtraArgs(Array.from(arguments), 3);
4745
- const normalValue = validateConstantFn(true, argraw);
4796
+ const normalValue = validateConstantFn(true, argraw, extraArgs);
4746
4797
  const reCondition = extraArgs.condition
4747
4798
  ? safe.patternToRegex(extraArgs.condition)
4748
4799
  : /^/;
4749
4800
  const reflector = proxyApplyFn(propChain, function(...args) {
4801
+ if ( argposRaw === '' ) {
4802
+ safe.uboLog(logPrefix, `Arguments:\n${args.join('\n')}`);
4803
+ return reflector(...args);
4804
+ }
4750
4805
  const arglist = args[args.length-1];
4751
4806
  if ( Array.isArray(arglist) === false ) { return reflector(...args); }
4752
4807
  const argBefore = arglist[argpos];
4753
- if ( reCondition.test(argBefore) === false ) { return reflector(...args); }
4808
+ if ( safe.RegExp_test.call(reCondition, argBefore) === false ) {
4809
+ return reflector(...args);
4810
+ }
4754
4811
  arglist[argpos] = normalValue;
4755
4812
  safe.uboLog(logPrefix, `Replaced argument:\nBefore: ${JSON.stringify(argBefore)}\nAfter: ${normalValue}`);
4756
4813
  return reflector(...args);
@@ -4781,20 +4838,114 @@ function trustedReplaceOutboundText(
4781
4838
  const extraArgs = safe.getExtraArgs(args);
4782
4839
  const reCondition = safe.patternToRegex(extraArgs.condition || '');
4783
4840
  const reflector = proxyApplyFn(propChain, function(...args) {
4784
- const textBefore = reflector(...args);
4841
+ const encodedTextBefore = reflector(...args);
4842
+ let textBefore = encodedTextBefore;
4843
+ if ( extraArgs.encoding === 'base64' ) {
4844
+ try { textBefore = self.atob(encodedTextBefore); }
4845
+ catch(ex) { return encodedTextBefore; }
4846
+ }
4785
4847
  if ( pattern === '' ) {
4786
- safe.uboLog(logPrefix, 'Outbound text:\n', textBefore);
4787
- return textBefore;
4848
+ safe.uboLog(logPrefix, 'Decoded outbound text:\n', textBefore);
4849
+ return encodedTextBefore;
4788
4850
  }
4789
4851
  reCondition.lastIndex = 0;
4790
- if ( reCondition.test(textBefore) === false ) { return textBefore; }
4852
+ if ( reCondition.test(textBefore) === false ) { return encodedTextBefore; }
4791
4853
  const textAfter = textBefore.replace(rePattern, replacement);
4792
- if ( textAfter === textBefore ) { return textBefore; }
4854
+ if ( textAfter === textBefore ) { return encodedTextBefore; }
4793
4855
  safe.uboLog(logPrefix, 'Matched and replaced');
4794
4856
  if ( safe.logLevel > 1 ) {
4795
- safe.uboLog(logPrefix, 'Modified outbound text:\n', textAfter);
4857
+ safe.uboLog(logPrefix, 'Modified decoded outbound text:\n', textAfter);
4858
+ }
4859
+ let encodedTextAfter = textAfter;
4860
+ if ( extraArgs.encoding === 'base64' ) {
4861
+ encodedTextAfter = self.btoa(textAfter);
4862
+ }
4863
+ return encodedTextAfter;
4864
+ });
4865
+ }
4866
+
4867
+ /*******************************************************************************
4868
+ *
4869
+ * Reference:
4870
+ * https://github.com/AdguardTeam/Scriptlets/blob/5a92d79489/wiki/about-trusted-scriptlets.md#trusted-suppress-native-method
4871
+ *
4872
+ * This is a first version with current limitations:
4873
+ * - Does not support matching arguments which are object or array
4874
+ * - Does not support `stack` parameter
4875
+ *
4876
+ * If `signatureStr` parameter is not declared, the scriptlet will log all calls
4877
+ * to `methodPath` along with the arguments passed and will not prevent the
4878
+ * trapped method.
4879
+ *
4880
+ * */
4881
+
4882
+ builtinScriptlets.push({
4883
+ name: 'trusted-suppress-native-method.js',
4884
+ requiresTrust: true,
4885
+ fn: trustedSuppressNativeMethod,
4886
+ dependencies: [
4887
+ 'proxy-apply.fn',
4888
+ 'safe-self.fn',
4889
+ ],
4890
+ });
4891
+ function trustedSuppressNativeMethod(
4892
+ methodPath = '',
4893
+ signature = '',
4894
+ how = '',
4895
+ stack = ''
4896
+ ) {
4897
+ if ( methodPath === '' ) { return; }
4898
+ if ( stack !== '' ) { return; }
4899
+ const safe = safeSelf();
4900
+ const logPrefix = safe.makeLogPrefix('trusted-suppress-native-method', methodPath, signature, how);
4901
+ const signatureArgs = signature.split(/\s*\|\s*/).map(v => {
4902
+ if ( /^".*"$/.test(v) ) {
4903
+ return { type: 'pattern', re: safe.patternToRegex(v.slice(1, -1)) };
4904
+ }
4905
+ if ( v === 'false' ) {
4906
+ return { type: 'exact', value: false };
4907
+ }
4908
+ if ( v === 'true' ) {
4909
+ return { type: 'exact', value: true };
4910
+ }
4911
+ if ( v === 'null' ) {
4912
+ return { type: 'exact', value: null };
4913
+ }
4914
+ if ( v === 'undefined' ) {
4915
+ return { type: 'exact', value: undefined };
4916
+ }
4917
+ });
4918
+ const reflector = proxyApplyFn(methodPath, function(...args) {
4919
+ if ( signature === '' ) {
4920
+ safe.uboLog(logPrefix, `Arguments:\n${args.join('\n')}`);
4921
+ return reflector(...args);
4922
+ }
4923
+ const arglist = args[args.length-1];
4924
+ if ( Array.isArray(arglist) === false ) {
4925
+ return reflector(...args);
4926
+ }
4927
+ if ( arglist.length < signatureArgs.length ) {
4928
+ return reflector(...args);
4929
+ }
4930
+ for ( let i = 0; i < signatureArgs.length; i++ ) {
4931
+ const signatureArg = signatureArgs[i];
4932
+ if ( signatureArg === undefined ) { continue; }
4933
+ const targetArg = arglist[i];
4934
+ if ( signatureArg.type === 'exact' ) {
4935
+ if ( targetArg !== signatureArg.value ) {
4936
+ return reflector(...args);
4937
+ }
4938
+ }
4939
+ if ( signatureArg.type === 'pattern' ) {
4940
+ if ( safe.RegExp_test.call(signatureArg.re, targetArg) === false ) {
4941
+ return reflector(...args);
4942
+ }
4943
+ }
4944
+ }
4945
+ safe.uboLog(logPrefix, `Suppressed:\n${args.join('\n')}`);
4946
+ if ( how === 'abort' ) {
4947
+ throw new ReferenceError();
4796
4948
  }
4797
- return textAfter;
4798
4949
  });
4799
4950
  }
4800
4951