mirror of
https://github.com/gorhill/uBlock.git
synced 2024-11-10 01:02:08 +01:00
[mv3] Avoid String.replace() to safely replace templates
String.replace() has side effects which are unwelcomed when replacing template scriplets with code.
This commit is contained in:
parent
7e712246a9
commit
5874312b35
3 changed files with 111 additions and 78 deletions
|
@ -32,6 +32,7 @@ import redirectResourcesMap from './js/redirect-resources.js';
|
|||
import { dnrRulesetFromRawLists } from './js/static-dnr-filtering.js';
|
||||
import * as sfp from './js/static-filtering-parser.js';
|
||||
import * as makeScriptlet from './make-scriptlets.js';
|
||||
import { safeReplace } from './safe-replace.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
@ -400,14 +401,14 @@ async function processGenericCosmeticFilters(assetDetails, bucketsMap, exclusion
|
|||
const selectorLists = bucketsList.map(v => [ v[0], v[1].join(',') ]);
|
||||
const originalScriptletMap = await loadAllSourceScriptlets();
|
||||
|
||||
const patchedScriptlet = originalScriptletMap.get('css-generic')
|
||||
.replace(
|
||||
'$rulesetId$',
|
||||
assetDetails.id
|
||||
).replace(
|
||||
/\bself\.\$genericSelectorMap\$/m,
|
||||
`${JSON.stringify(selectorLists, scriptletJsonReplacer)}`
|
||||
);
|
||||
let patchedScriptlet = originalScriptletMap.get('css-generic').replace(
|
||||
'$rulesetId$',
|
||||
assetDetails.id
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$genericSelectorMap\$/,
|
||||
`${JSON.stringify(selectorLists, scriptletJsonReplacer)}`
|
||||
);
|
||||
|
||||
writeFile(
|
||||
`${scriptletDir}/generic/${assetDetails.id}.js`,
|
||||
|
@ -593,23 +594,26 @@ async function processCosmeticFilters(assetDetails, mapin) {
|
|||
}
|
||||
|
||||
const originalScriptletMap = await loadAllSourceScriptlets();
|
||||
const patchedScriptlet = originalScriptletMap.get('css-specific')
|
||||
.replace(
|
||||
'$rulesetId$',
|
||||
assetDetails.id
|
||||
).replace(
|
||||
/\bself\.\$argsList\$/m,
|
||||
`${JSON.stringify(argsList, scriptletJsonReplacer)}`
|
||||
).replace(
|
||||
/\bself\.\$hostnamesMap\$/m,
|
||||
`${JSON.stringify(hostnamesMap, scriptletJsonReplacer)}`
|
||||
).replace(
|
||||
/\bself\.\$entitiesMap\$/m,
|
||||
`${JSON.stringify(entitiesMap, scriptletJsonReplacer)}`
|
||||
).replace(
|
||||
/\bself\.\$exceptionsMap\$/m,
|
||||
`${JSON.stringify(exceptionsMap, scriptletJsonReplacer)}`
|
||||
);
|
||||
let patchedScriptlet = originalScriptletMap.get('css-specific').replace(
|
||||
'$rulesetId$',
|
||||
assetDetails.id
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$argsList\$/,
|
||||
`${JSON.stringify(argsList, scriptletJsonReplacer)}`
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$hostnamesMap\$/,
|
||||
`${JSON.stringify(hostnamesMap, scriptletJsonReplacer)}`
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$entitiesMap\$/,
|
||||
`${JSON.stringify(entitiesMap, scriptletJsonReplacer)}`
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$exceptionsMap\$/,
|
||||
`${JSON.stringify(exceptionsMap, scriptletJsonReplacer)}`
|
||||
);
|
||||
writeFile(`${scriptletDir}/specific/${assetDetails.id}.js`, patchedScriptlet);
|
||||
generatedFiles.push(`${assetDetails.id}`);
|
||||
|
||||
|
@ -677,23 +681,26 @@ async function processDeclarativeCosmeticFilters(assetDetails, mapin) {
|
|||
}
|
||||
|
||||
const originalScriptletMap = await loadAllSourceScriptlets();
|
||||
const patchedScriptlet = originalScriptletMap.get('css-declarative')
|
||||
.replace(
|
||||
'$rulesetId$',
|
||||
assetDetails.id
|
||||
).replace(
|
||||
/\bself\.\$argsList\$/m,
|
||||
`${JSON.stringify(argsList, scriptletJsonReplacer)}`
|
||||
).replace(
|
||||
/\bself\.\$hostnamesMap\$/m,
|
||||
`${JSON.stringify(hostnamesMap, scriptletJsonReplacer)}`
|
||||
).replace(
|
||||
/\bself\.\$entitiesMap\$/m,
|
||||
`${JSON.stringify(entitiesMap, scriptletJsonReplacer)}`
|
||||
).replace(
|
||||
/\bself\.\$exceptionsMap\$/m,
|
||||
`${JSON.stringify(exceptionsMap, scriptletJsonReplacer)}`
|
||||
);
|
||||
let patchedScriptlet = originalScriptletMap.get('css-declarative').replace(
|
||||
'$rulesetId$',
|
||||
assetDetails.id
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$argsList\$/,
|
||||
`${JSON.stringify(argsList, scriptletJsonReplacer)}`
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$hostnamesMap\$/,
|
||||
`${JSON.stringify(hostnamesMap, scriptletJsonReplacer)}`
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$entitiesMap\$/,
|
||||
`${JSON.stringify(entitiesMap, scriptletJsonReplacer)}`
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$exceptionsMap\$/,
|
||||
`${JSON.stringify(exceptionsMap, scriptletJsonReplacer)}`
|
||||
);
|
||||
writeFile(`${scriptletDir}/declarative/${assetDetails.id}.js`, patchedScriptlet);
|
||||
|
||||
if ( contentArray.length !== 0 ) {
|
||||
|
@ -760,23 +767,26 @@ async function processProceduralCosmeticFilters(assetDetails, mapin) {
|
|||
}
|
||||
|
||||
const originalScriptletMap = await loadAllSourceScriptlets();
|
||||
const patchedScriptlet = originalScriptletMap.get('css-procedural')
|
||||
.replace(
|
||||
'$rulesetId$',
|
||||
assetDetails.id
|
||||
).replace(
|
||||
/\bself\.\$argsList\$/m,
|
||||
`${JSON.stringify(argsList, scriptletJsonReplacer)}`
|
||||
).replace(
|
||||
/\bself\.\$hostnamesMap\$/m,
|
||||
`${JSON.stringify(hostnamesMap, scriptletJsonReplacer)}`
|
||||
).replace(
|
||||
/\bself\.\$entitiesMap\$/m,
|
||||
`${JSON.stringify(entitiesMap, scriptletJsonReplacer)}`
|
||||
).replace(
|
||||
/\bself\.\$exceptionsMap\$/m,
|
||||
`${JSON.stringify(exceptionsMap, scriptletJsonReplacer)}`
|
||||
);
|
||||
let patchedScriptlet = originalScriptletMap.get('css-procedural').replace(
|
||||
'$rulesetId$',
|
||||
assetDetails.id
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$argsList\$/,
|
||||
`${JSON.stringify(argsList, scriptletJsonReplacer)}`
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$hostnamesMap\$/,
|
||||
`${JSON.stringify(hostnamesMap, scriptletJsonReplacer)}`
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$entitiesMap\$/,
|
||||
`${JSON.stringify(entitiesMap, scriptletJsonReplacer)}`
|
||||
);
|
||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||
/\bself\.\$exceptionsMap\$/,
|
||||
`${JSON.stringify(exceptionsMap, scriptletJsonReplacer)}`
|
||||
);
|
||||
writeFile(`${scriptletDir}/procedural/${assetDetails.id}.js`, patchedScriptlet);
|
||||
|
||||
if ( contentArray.length !== 0 ) {
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
import fs from 'fs/promises';
|
||||
import { builtinScriptlets } from './scriptlets.js';
|
||||
import { safeReplace } from './safe-replace.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
@ -52,25 +53,6 @@ function createScriptletCoreCode(scriptletToken) {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
function safeReplace(text, pattern, replacement, count = 1) {
|
||||
const rePattern = typeof pattern === 'string'
|
||||
? new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
|
||||
: pattern;
|
||||
let out = text;
|
||||
for (;;) {
|
||||
const match = rePattern.exec(out);
|
||||
if ( match === null ) { break; }
|
||||
out = out.slice(0, match.index) +
|
||||
replacement +
|
||||
out.slice(match.index + match[0].length);
|
||||
count -= 1;
|
||||
if ( count === 0 ) { break; }
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
export function init() {
|
||||
for ( const scriptlet of builtinScriptlets ) {
|
||||
const { name, aliases, fn } = scriptlet;
|
||||
|
|
41
platform/mv3/safe-replace.js
Normal file
41
platform/mv3/safe-replace.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a browser extension to block requests.
|
||||
Copyright (C) 2017-present Raymond Hill
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
|
||||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
export function safeReplace(text, pattern, replacement, count = 1) {
|
||||
const rePattern = typeof pattern === 'string'
|
||||
? new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
|
||||
: pattern;
|
||||
let out = text;
|
||||
for (;;) {
|
||||
const match = rePattern.exec(out);
|
||||
if ( match === null ) { break; }
|
||||
out = out.slice(0, match.index) +
|
||||
replacement +
|
||||
out.slice(match.index + match[0].length);
|
||||
count -= 1;
|
||||
if ( count === 0 ) { break; }
|
||||
}
|
||||
return out;
|
||||
}
|
Loading…
Reference in a new issue