Add trusted-replace-fetch-response scriptlet · gorhill/uBlock@82a7d11 (original) (raw)

`@@ -53,6 +53,9 @@ function safeSelf() {

`

53

53

`'RegExp_exec': self.RegExp.prototype.exec,

`

54

54

`'addEventListener': self.EventTarget.prototype.addEventListener,

`

55

55

`'removeEventListener': self.EventTarget.prototype.removeEventListener,

`

``

56

`+

'fetch': self.fetch,

`

``

57

`+

'jsonParse': self.JSON.parse.bind(self.JSON),

`

``

58

`+

'jsonStringify': self.JSON.stringify.bind(self.JSON),

`

56

59

`'log': console.log.bind(console),

`

57

60

`'uboLog': function(...args) {

`

58

61

`if ( args.length === 0 ) { return; }

`

`@@ -103,10 +106,15 @@ builtinScriptlets.push({

`

103

106

`function patternToRegex(pattern, flags = undefined) {

`

104

107

`if ( pattern === '' ) { return /^/; }

`

105

108

`const match = /^/(.+)/([gimsu]*)$/.exec(pattern);

`

106

``

`-

if ( match !== null ) {

`

``

109

`+

if ( match === null ) {

`

``

110

`+

return new RegExp(pattern.replace(/[.*+?^${}()|[]\]/g, '\$&'), flags);

`

``

111

`+

}

`

``

112

`+

try {

`

107

113

`return new RegExp(match[1], match[2] || flags);

`

108

114

`}

`

109

``

`-

return new RegExp(pattern.replace(/[.*+?^${}()|[]\]/g, '\$&'), flags);

`

``

115

`+

catch(ex) {

`

``

116

`+

}

`

``

117

`+

return /^/;

`

110

118

`}

`

111

119

``

112

120

`/******************************************************************************/

`

`@@ -210,11 +218,14 @@ function runAtHtmlElement(fn) {

`

210

218

`/******************************************************************************/

`

211

219

``

212

220

`builtinScriptlets.push({

`

213

``

`-

name: 'get-extra-args-entries.fn',

`

214

``

`-

fn: getExtraArgsEntries,

`

``

221

`+

name: 'get-extra-args.fn',

`

``

222

`+

fn: getExtraArgs,

`

``

223

`+

dependencies: [

`

``

224

`+

'get-extra-args-entries.fn',

`

``

225

`+

],

`

215

226

`});

`

216

``

`-

function getExtraArgsEntries(args, offset) {

`

217

``

`-

return args.slice(offset).reduce((out, v, i, a) => {

`

``

227

`+

function getExtraArgs(args, offset = 0) {

`

``

228

`+

const entries = args.slice(offset).reduce((out, v, i, a) => {

`

218

229

`if ( (i & 1) === 0 ) {

`

219

230

`const rawValue = a[i+1];

`

220

231

`const value = /^\d+$/.test(rawValue)

`

`@@ -224,28 +235,7 @@ function getExtraArgsEntries(args, offset) {

`

224

235

`}

`

225

236

`return out;

`

226

237

`}, []);

`

227

``

`-

}

`

228

``

-

229

``

`-

builtinScriptlets.push({

`

230

``

`-

name: 'get-extra-args-map.fn',

`

231

``

`-

fn: getExtraArgsMap,

`

232

``

`-

dependencies: [

`

233

``

`-

'get-extra-args-entries.fn',

`

234

``

`-

],

`

235

``

`-

});

`

236

``

`-

function getExtraArgsMap(args, offset = 0) {

`

237

``

`-

return new Map(getExtraArgsEntries(args, offset));

`

238

``

`-

}

`

239

``

-

240

``

`-

builtinScriptlets.push({

`

241

``

`-

name: 'get-extra-args.fn',

`

242

``

`-

fn: getExtraArgs,

`

243

``

`-

dependencies: [

`

244

``

`-

'get-extra-args-entries.fn',

`

245

``

`-

],

`

246

``

`-

});

`

247

``

`-

function getExtraArgs(args, offset = 0) {

`

248

``

`-

return Object.fromEntries(getExtraArgsEntries(args, offset));

`

``

238

`+

return Object.fromEntries(entries);

`

249

239

`}

`

250

240

``

251

241

`/******************************************************************************/

`

`@@ -447,7 +437,7 @@ function setConstantCore(

`

447

437

`if ( Math.abs(cValue) > 0x7FFF ) { return; }

`

448

438

`} else if ( trusted ) {

`

449

439

`if ( cValue.startsWith('{') && cValue.endsWith('}') ) {

`

450

``

`-

try { cValue = JSON.parse(cValue).value; } catch(ex) { return; }

`

``

440

`+

try { cValue = safe.jsonParse(cValue).value; } catch(ex) { return; }

`

451

441

`}

`

452

442

`} else {

`

453

443

`return;

`

`@@ -2415,7 +2405,7 @@ function xmlPrune(

`

2415

2405

`` log(xmlPrune: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mrow><mi>i</mi><mi>t</mi><mi>e</mi><mi>m</mi><mi mathvariant="normal">.</mi><mi>c</mi><mi>o</mi><mi>n</mi><mi>s</mi><mi>t</mi><mi>r</mi><mi>u</mi><mi>c</mi><mi>t</mi><mi>o</mi><mi>r</mi><mi mathvariant="normal">.</mi><mi>n</mi><mi>a</mi><mi>m</mi><mi>e</mi></mrow><mi mathvariant="normal">.</mi></mrow><annotation encoding="application/x-tex">{item.constructor.name}.</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6595em;"></span><span class="mord"><span class="mord mathnormal">i</span><span class="mord mathnormal">t</span><span class="mord mathnormal">e</span><span class="mord mathnormal">m</span><span class="mord">.</span><span class="mord mathnormal">co</span><span class="mord mathnormal">n</span><span class="mord mathnormal">s</span><span class="mord mathnormal">t</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">u</span><span class="mord mathnormal">c</span><span class="mord mathnormal">t</span><span class="mord mathnormal" style="margin-right:0.02778em;">or</span><span class="mord">.</span><span class="mord mathnormal">nam</span><span class="mord mathnormal">e</span></span><span class="mord">.</span></span></span></span>{item.nodeName} removed);

``

2416

2406

`}

`

2417

2407

`} catch(ex) {

`

2418

``

`-

if ( log ) { safeSelf().uboLog(ex); }

`

``

2408

`+

log(ex);

`

2419

2409

`}

`

2420

2410

`return xmlDoc;

`

2421

2411

`};

`

`@@ -2438,13 +2428,12 @@ function xmlPrune(

`

2438

2428

`if ( arg instanceof Request ) { return arg.url; }

`

2439

2429

`return String(arg);

`

2440

2430

`};

`

2441

``

`-

const realFetch = self.fetch;

`

2442

2431

`self.fetch = new Proxy(self.fetch, {

`

2443

2432

`apply: function(target, thisArg, args) {

`

2444

2433

`if ( reUrl.test(urlFromArg(args[0])) === false ) {

`

2445

2434

`return Reflect.apply(target, thisArg, args);

`

2446

2435

`}

`

2447

``

`-

return realFetch(...args).then(realResponse =>

`

``

2436

`+

return safe.fetch(...args).then(realResponse =>

`

2448

2437

`realResponse.text().then(text =>

`

2449

2438

`new Response(pruneFromText(text), {

`

2450

2439

`status: realResponse.status,

`

`@@ -3329,4 +3318,103 @@ function trustedSetLocalStorageItem(key = '', value = '') {

`

3329

3318

`setLocalStorageItemCore('local', true, key, value);

`

3330

3319

`}

`

3331

3320

``

``

3321

`+

/*******************************************************************************

`

``

3322

`+

*

`

``

3323

`+

`

``

3324

`+

*

`

``

3325

`+

`

``

3326

`+

`

``

3327

`+

*

`

``

3328

`+

`

``

3329

`+

`

``

3330

`+

*

`

``

3331

`+

**/

`

``

3332

+

``

3333

`+

builtinScriptlets.push({

`

``

3334

`+

name: 'trusted-replace-fetch-response.js',

`

``

3335

`+

requiresTrust: true,

`

``

3336

`+

fn: trustedReplaceFetchResponse,

`

``

3337

`+

dependencies: [

`

``

3338

`+

'get-extra-args.fn',

`

``

3339

`+

'pattern-to-regex.fn',

`

``

3340

`+

'safe-self.fn',

`

``

3341

`+

'should-log.fn',

`

``

3342

`+

],

`

``

3343

`+

});

`

``

3344

`+

function trustedReplaceFetchResponse(

`

``

3345

`+

pattern = '',

`

``

3346

`+

replacement = '',

`

``

3347

`+

propsToMatch = ''

`

``

3348

`+

) {

`

``

3349

`+

const safe = safeSelf();

`

``

3350

`+

const extraArgs = getExtraArgs(Array.from(arguments), 3);

`

``

3351

`+

const logLevel = shouldLog({

`

``

3352

`+

log: pattern === '' || extraArgs.log,

`

``

3353

`+

});

`

``

3354

`+

const log = logLevel ? ((...args) => { safe.uboLog(...args); }) : (( ) => { });

`

``

3355

`+

if ( pattern === '' ) { pattern = '.'; }

`

``

3356

`+

const rePattern = patternToRegex(pattern);

`

``

3357

`+

const propNeedles = new Map();

`

``

3358

`+

for ( const needle of propsToMatch.split(/\s+/) ) {

`

``

3359

`+

const [ prop, value ] = needle.split(':');

`

``

3360

`+

if ( prop === '' ) { continue; }

`

``

3361

`+

propNeedles.set(prop, patternToRegex(value));

`

``

3362

`+

}

`

``

3363

`+

self.fetch = new Proxy(self.fetch, {

`

``

3364

`+

apply: function(target, thisArg, args) {

`

``

3365

`+

if ( logLevel === true ) {

`

``

3366

`+

log('trusted-replace-fetch-response:', JSON.stringify(Array.from(args)).slice(1,-1));

`

``

3367

`+

}

`

``

3368

`+

const fetchPromise = Reflect.apply(target, thisArg, args);

`

``

3369

`+

if ( pattern === '' ) { return fetchPromise; }

`

``

3370

`+

let skip = false;

`

``

3371

`+

if ( propNeedles.size !== 0 ) {

`

``

3372

`+

const fetchDetails = {};

`

``

3373

`+

if ( args[0] instanceof self.Request ) {

`

``

3374

`+

Object.assign(fetchDetails, args[0]);

`

``

3375

`+

} else {

`

``

3376

`+

Object.assign(fetchDetails, { url: args[0] });

`

``

3377

`+

}

`

``

3378

`+

if ( args[1] instanceof Object ) {

`

``

3379

`+

Object.assign(fetchDetails, args[1]);

`

``

3380

`+

}

`

``

3381

`+

for ( const prop of Object.keys(fetchDetails) ) {

`

``

3382

`+

let value = fetchDetails[prop];

`

``

3383

`+

if ( typeof value !== 'string' ) {

`

``

3384

`+

try { value = JSON.stringify(value); }

`

``

3385

`+

catch(ex) { }

`

``

3386

`+

}

`

``

3387

`+

if ( typeof value !== 'string' ) { continue; }

`

``

3388

`+

const needle = propNeedles.get(prop);

`

``

3389

`+

if ( needle === undefined ) { continue; }

`

``

3390

`+

if ( needle.test(value) ) { continue; }

`

``

3391

`+

skip = true;

`

``

3392

`+

break;

`

``

3393

`+

}

`

``

3394

`+

}

`

``

3395

`+

if ( skip ) { return fetchPromise; }

`

``

3396

`+

return fetchPromise.then(responseBefore => {

`

``

3397

`+

return responseBefore.text().then(textBefore => {

`

``

3398

`+

const textAfter = textBefore.replace(rePattern, replacement);

`

``

3399

`+

const responseAfter = new Response(textAfter, {

`

``

3400

`+

status: responseBefore.status,

`

``

3401

`+

statusText: responseBefore.statusText,

`

``

3402

`+

headers: responseBefore.headers,

`

``

3403

`+

});

`

``

3404

`+

Object.defineProperties(responseAfter, {

`

``

3405

`+

ok: { value: responseBefore.ok },

`

``

3406

`+

redirected: { value: responseBefore.redirected },

`

``

3407

`+

type: { value: responseBefore.type },

`

``

3408

`+

url: { value: responseBefore.url },

`

``

3409

`+

});

`

``

3410

`+

return responseAfter;

`

``

3411

`+

}).catch(reason => {

`

``

3412

`+

log('trusted-replace-fetch-response:', reason);

`

``

3413

`+

return responseBefore;

`

``

3414

`+

});

`

``

3415

`+

});

`

``

3416

`+

}

`

``

3417

`+

});

`

``

3418

`+

}

`

``

3419

+

3332

3420

`/******************************************************************************/

`