diff --git a/CHANGELOG.md b/CHANGELOG.md index 5853e57..f10d79c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ Any non-code changes should be prefixed with `(docs)`. See `PUBLISH.md` for instructions on how to publish a new version. --> +- (patch) Avoid slowdown from `URL#searchParams` in glob embed + ## v1.12.1 - 39a3836 diff --git a/rules/embeds/glob.js b/rules/embeds/glob.js index e236792..53f8a62 100644 --- a/rules/embeds/glob.js +++ b/rules/embeds/glob.js @@ -78,7 +78,9 @@ module.exports = md => { if (closingMark === -1) return false; // Check for glob match - const match = currentLines.slice(0, closingMark + 3).match(/^\[glob (.+?(?:(?: [^ \n]+?)+|(?:\n.+?)+))\](?:$|\n)/); + // .+(?:\n.+)+ allows for a glob with spaces on the first line, and then tests separated by newlines + // [^ \n]+(?: [^ \n]+)+ allows for a glob with no spaces on the first line, and then tests separated by spaces + const match = currentLines.slice(0, closingMark + 3).match(/^\[glob (.+(?:\n.+)+|[^ \n]+(?: [^ \n]+)+)\](?:$|\n)/); if (!match) return false; // Get the full strings @@ -147,9 +149,9 @@ module.exports = md => { const tests = token.glob.tests.map((x, i) => `data-glob-test-${i}="${md.utils.escapeHtml(x)}"`).join(' '); // Construct the fallback URL - const url = new URL('https://www.digitalocean.com/community/tools/glob'); - url.searchParams.append('glob', token.glob.glob); - token.glob.tests.forEach(x => url.searchParams.append('tests', x)); + // Don't use URL#searchParams because it is very slow for large numbers of params + // https://twitter.com/MattIPv4/status/1748102513646047584 + const url = `https://www.digitalocean.com/community/tools/glob?glob=${encodeURIComponent(token.glob.glob)}${token.glob.tests.map(x => `&tests=${encodeURIComponent(x)}`).join('')}`; // Return the HTML return `
diff --git a/rules/embeds/glob.test.js b/rules/embeds/glob.test.js index 7b9ca24..e0cb5e4 100644 --- a/rules/embeds/glob.test.js +++ b/rules/embeds/glob.test.js @@ -60,7 +60,7 @@ it('handles glob embeds with linebreaks', () => { it('handles glob embeds with linebreaks and spaces in glob', () => { expect(md.render('[glob * test.js\n/a\n/b]')).toBe(`
- + Explore * test.js as a glob string in our glob testing tool
@@ -68,9 +68,19 @@ it('handles glob embeds with linebreaks and spaces in glob', () => { `); }); -it('handles glob embeds with many spaces in glob and a linebreak (ReDos)', () => { +it('handles glob embeds with many spaces in glob (DoS)', () => { + expect(md.render(`[glob ${Array.from('a'.repeat(50000)).join(' ')}]`)).toBe(`
`data-glob-test-${i}="${a}"`).join(' ')}> + + Explore a as a glob string in our glob testing tool + +
+ +`); +}); + +it('handles glob embeds with many spaces in glob and a linebreak (ReDoS)', () => { expect(md.render(`[glob ${Array.from('a'.repeat(50)).join(' ')}\nb\nc]`)).toBe(`
- + Explore ${Array.from('a'.repeat(50)).join(' ')} as a glob string in our glob testing tool