mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-08-03 02:11:07 +05:30
fix(security): strengthen ReDoS denylist in search-in-files
Follow-up to the push-sweep review: the previous UNSAFE_REGEX used JS bitwise OR (|) between regex literals, accidentally OR-ing the RegExp objects instead of combining alternatives into one pattern. Replace with a single RegExp that catches: - nested quantifiers (a+)+, [a-z]*+ - class + quantifier - dot-quantifier followed by dot-quantifier - lookahead / lookbehind (?=, ?!) - backrefs \1..\9 - alternation + quantifier Also add MAX_REGEX_LENGTH=200 hard cap. The denylist is still defense-in-depth — the length, files-traversed, and per-file-byte caps are the primary defense.
This commit is contained in:
@@ -105,6 +105,43 @@ describe('searchInFiles', () => {
|
||||
).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
|
||||
test('rejects regexes with lookahead / lookbehind', () => {
|
||||
fs.writeFileSync(path.join(tmpDir, 'a.md'), 'hello');
|
||||
expect(
|
||||
searchInFiles({ rootPath: tmpDir, query: '(?=hello)h', isRegex: true })
|
||||
).toEqual([]);
|
||||
expect(
|
||||
searchInFiles({ rootPath: tmpDir, query: '(?!foo)h', isRegex: true })
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
test('rejects regexes with backrefs', () => {
|
||||
fs.writeFileSync(path.join(tmpDir, 'a.md'), 'hello hello');
|
||||
expect(
|
||||
searchInFiles({ rootPath: tmpDir, query: '(hello)\\1', isRegex: true })
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
test('rejects regexes with class + quantifier', () => {
|
||||
fs.writeFileSync(path.join(tmpDir, 'a.md'), 'abc');
|
||||
expect(
|
||||
searchInFiles({ rootPath: tmpDir, query: '[a-z]+', isRegex: true })
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
test('rejects regexes longer than 200 chars', () => {
|
||||
fs.writeFileSync(path.join(tmpDir, 'a.md'), 'x');
|
||||
const longRe = 'a'.repeat(250);
|
||||
expect(
|
||||
searchInFiles({ rootPath: tmpDir, query: longRe, isRegex: true })
|
||||
).toEqual([]);
|
||||
// But 200 chars is fine
|
||||
const okRe = 'a'.repeat(200);
|
||||
expect(
|
||||
searchInFiles({ rootPath: tmpDir, query: okRe, isRegex: true }).length
|
||||
).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
|
||||
test('does not follow symlinks that escape rootPath', () => {
|
||||
// Create a target outside tmpDir and a symlink inside pointing to it.
|
||||
const outside = fs.mkdtempSync(path.join(os.tmpdir(), 'search-outside-'));
|
||||
|
||||
Reference in New Issue
Block a user