1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-07-19 03:05:22 +02:00

Re-work unescaping arguments in parser

Related issue:
https://github.com/uBlockOrigin/uAssets/issues/5184#issuecomment-1803455520
This commit is contained in:
Raymond Hill 2023-11-09 09:11:59 -05:00
parent 2bc7996d64
commit ec53a8f2c7
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2

View File

@ -599,11 +599,6 @@ const exCharCodeAt = (s, i) => {
return pos >= 0 ? s.charCodeAt(pos) : -1; return pos >= 0 ? s.charCodeAt(pos) : -1;
}; };
const toEscapedCharRegex = c => {
const safe = c.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
return new RegExp(`((?:^|[^\\\\])(?:\\\\\\\\)*)\\\\${safe}`, 'g');
};
/******************************************************************************/ /******************************************************************************/
class ArgListParser { class ArgListParser {
@ -619,11 +614,7 @@ class ArgListParser {
this.reWhitespaceStart = /^\s+/; this.reWhitespaceStart = /^\s+/;
this.reWhitespaceEnd = /\s+$/; this.reWhitespaceEnd = /\s+$/;
this.reOddTrailingEscape = /(?:^|[^\\])(?:\\\\)*\\$/; this.reOddTrailingEscape = /(?:^|[^\\])(?:\\\\)*\\$/;
this.reEscapedDoubleQuote = toEscapedCharRegex('"'); this.reTrailingEscapeChars = /\\+$/;
this.reEscapedSingleQuote = toEscapedCharRegex("'");
this.reEscapedBacktick = toEscapedCharRegex('`');
this.reEscapedSeparator = toEscapedCharRegex(this.separatorChar);
this.unescapedSeparator = `$1${this.separatorChar}`;
} }
nextArg(pattern, beg = 0) { nextArg(pattern, beg = 0) {
const len = pattern.length; const len = pattern.length;
@ -655,22 +646,23 @@ class ArgListParser {
} }
return this; return this;
} }
normalizeArg(s) { normalizeArg(s, char = '') {
switch ( this.actualSeparatorCode ) { if ( char === '' ) { char = this.actualSeparatorChar; }
case 0x22 /* " */: let out = '';
if ( s.includes('"') === false ) { return; } let pos = 0;
return s.replace(this.reEscapedDoubleQuote, '$1"'); while ( (pos = s.lastIndexOf(char)) !== -1 ) {
case 0x27 /* ' */: out = s.slice(pos) + out;
if ( s.includes("'") === false ) { return; } s = s.slice(0, pos);
return s.replace(this.reEscapedSingleQuote, "$1'"); const match = this.reTrailingEscapeChars.exec(s);
case 0x60 /* ` */: if ( match === null ) { continue; }
if ( s.includes('`') === false ) { return; } const tail = (match[0].length & 1) !== 0
return s.replace(this.reEscapedBacktick, '$1`'); ? match[0].slice(0, -1)
default: : match[0];
break; out = tail + out;
s = s.slice(0, -match[0].length);
} }
if ( s.includes(this.separatorChar) === false ) { return; } if ( out === '' ) { return s; }
return s.replace(this.reEscapedSeparator, this.unescapedSeparator); return s + out;
} }
leftWhitespaceCount(s) { leftWhitespaceCount(s) {
const match = this.reWhitespaceStart.exec(s); const match = this.reWhitespaceStart.exec(s);
@ -3006,7 +2998,6 @@ export function parseHeaderValue(arg) {
export function parseReplaceValue(s) { export function parseReplaceValue(s) {
if ( s.charCodeAt(0) !== 0x2F /* / */ ) { return; } if ( s.charCodeAt(0) !== 0x2F /* / */ ) { return; }
const { reEscapedComma, reEscapedDollarSign } = parseReplaceValue;
const parser = new ArgListParser('/'); const parser = new ArgListParser('/');
parser.nextArg(s, 1); parser.nextArg(s, 1);
let pattern = s.slice(parser.argBeg, parser.argEnd); let pattern = s.slice(parser.argBeg, parser.argEnd);
@ -3014,26 +3005,22 @@ export function parseReplaceValue(s) {
pattern = parser.normalizeArg(pattern); pattern = parser.normalizeArg(pattern);
} }
if ( pattern === '' ) { return; } if ( pattern === '' ) { return; }
pattern = pattern pattern = parser.normalizeArg(pattern, '$');
.replace(reEscapedDollarSign, '$1$$$') pattern = parser.normalizeArg(pattern, ',');
.replace(reEscapedComma, '$1,');
parser.nextArg(s, parser.separatorEnd); parser.nextArg(s, parser.separatorEnd);
let replacement = s.slice(parser.argBeg, parser.argEnd); let replacement = s.slice(parser.argBeg, parser.argEnd);
if ( parser.separatorEnd === parser.separatorBeg ) { return; } if ( parser.separatorEnd === parser.separatorBeg ) { return; }
if ( parser.transform ) { if ( parser.transform ) {
replacement = parser.normalizeArg(replacement); replacement = parser.normalizeArg(replacement);
} }
replacement = replacement replacement = parser.normalizeArg(replacement, '$');
.replace(reEscapedDollarSign, '$1$$') replacement = parser.normalizeArg(replacement, ',');
.replace(reEscapedComma, '$1,');
const flags = s.slice(parser.separatorEnd); const flags = s.slice(parser.separatorEnd);
try { try {
return { re: new RegExp(pattern, flags), replacement }; return { re: new RegExp(pattern, flags), replacement };
} catch(_) { } catch(_) {
} }
} }
parseReplaceValue.reEscapedDollarSign = toEscapedCharRegex('$');
parseReplaceValue.reEscapedComma = toEscapedCharRegex(',');
/******************************************************************************/ /******************************************************************************/