Skip to content

Commit 17a550f

Browse files
wip
1 parent 727cad9 commit 17a550f

File tree

1 file changed

+51
-8
lines changed

1 file changed

+51
-8
lines changed

src/content.js

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,13 @@ async function _getRuleFromUrl(url) {
3131
return url.endsWith(urlFragment);
3232
case 'REGEX':
3333
case 'REGEXP':
34-
return new RegExp(urlFragment).test(url);
34+
try {
35+
const regex = createSafeRegex(urlFragment);
36+
return regex.test(url);
37+
} catch (e) {
38+
console.error('Error processing regex pattern for URL matching:', e);
39+
return false;
40+
}
3541
case 'EXACT':
3642
return url === urlFragment;
3743
default:
@@ -52,6 +58,37 @@ export function updateTitle(title, tag, value) {
5258
return value ? title.replace(tag, decodeURI(value)) : title;
5359
}
5460

61+
function isRegexSafe(pattern) {
62+
// Basic validation to prevent ReDoS attacks
63+
if (typeof pattern !== 'string' || pattern.length > 200) {
64+
return false;
65+
}
66+
67+
// Check for potentially dangerous patterns that can cause ReDoS
68+
const dangerousPatterns = [
69+
/\(\?\=.*\)\+/, // Positive lookahead with quantifiers
70+
/\(\?\!.*\)\+/, // Negative lookahead with quantifiers
71+
/\(.+\)\+\$/, // Catastrophic backtracking patterns
72+
/\(.+\)\*\+/, // Conflicting quantifiers
73+
/\(\.\*\)\{2,\}/, // Multiple .* in groups
74+
/\(\.\+\)\{2,\}/, // Multiple .+ in groups
75+
];
76+
77+
return !dangerousPatterns.some(dangerous => dangerous.test(pattern));
78+
}
79+
80+
function createSafeRegex(pattern, flags = 'g') {
81+
if (!isRegexSafe(pattern)) {
82+
throw new Error('Potentially unsafe regex pattern detected');
83+
}
84+
85+
try {
86+
return new RegExp(pattern, flags);
87+
} catch (e) {
88+
throw new Error(`Invalid regex pattern: ${e.message}`);
89+
}
90+
}
91+
5592
export function getTextBySelector(selector) {
5693
let el = null;
5794

@@ -61,7 +98,7 @@ export function getTextBySelector(selector) {
6198
const toSafe = (s) =>
6299
typeof CSS !== 'undefined' && CSS.escape
63100
? CSS.escape(s)
64-
: s.replace(/[\"]/g, '$&').replace(/]/g, '\\]');
101+
: s.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/]/g, '\\]');
65102

66103
const modifiedParts = parts.map((part) => {
67104
if (!part.includes('*')) return part;
@@ -124,35 +161,41 @@ export function processTitle(currentUrl, currentTitle, rule) {
124161

125162
if (rule.tab.title_matcher) {
126163
try {
127-
const regex = new RegExp(rule.tab.title_matcher, 'g');
164+
const regex = createSafeRegex(rule.tab.title_matcher, 'g');
128165
let matches;
129166
let i = 0;
167+
let iterationCount = 0;
168+
const maxIterations = 100; // Prevent infinite loops
130169

131-
while ((matches = regex.exec(currentTitle)) !== null) {
170+
while ((matches = regex.exec(currentTitle)) !== null && iterationCount < maxIterations) {
132171
for (let j = 0; j < matches.length; j++) {
133172
title = updateTitle(title, '@' + i, matches[j]);
134173
i++;
135174
}
175+
iterationCount++;
136176
}
137177
} catch (e) {
138-
console.error(e);
178+
console.error('Error processing title_matcher regex:', e);
139179
}
140180
}
141181

142182
if (rule.tab.url_matcher) {
143183
try {
144-
const regex = new RegExp(rule.tab.url_matcher, 'g');
184+
const regex = createSafeRegex(rule.tab.url_matcher, 'g');
145185
let matches;
146186
let i = 0;
187+
let iterationCount = 0;
188+
const maxIterations = 100; // Prevent infinite loops
147189

148-
while ((matches = regex.exec(currentUrl)) !== null) {
190+
while ((matches = regex.exec(currentUrl)) !== null && iterationCount < maxIterations) {
149191
for (let j = 0; j < matches.length; j++) {
150192
title = updateTitle(title, '$' + i, matches[j]);
151193
i++;
152194
}
195+
iterationCount++;
153196
}
154197
} catch (e) {
155-
console.error(e);
198+
console.error('Error processing url_matcher regex:', e);
156199
}
157200
}
158201

0 commit comments

Comments
 (0)