Skip to content

Commit bc82567

Browse files
fix: potential super-linear regular expressions (#463)
Improve regular expressions that could match in super-linear runtime for certain inputs.
1 parent bf3f39b commit bc82567

File tree

6 files changed

+45
-8
lines changed

6 files changed

+45
-8
lines changed

src/rules/no-duplicate-headings.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
*
3737
* @see https://spec.commonmark.org/0.31.2/#example-76
3838
*/
39-
const trailingAtxHeadingHashPattern = /[ \t]+#+[ \t]*$/u;
39+
const trailingAtxHeadingHashPattern = /(?<![ \t])[ \t]+#+[ \t]*$/u;
4040
const leadingAtxHeadingHashPattern = /^#{1,6}[ \t]+/u;
4141

4242
//-----------------------------------------------------------------------------

src/rules/no-missing-label-refs.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ function findMissingReferences(node, nodeText) {
4343
* `right` is the content between the second brackets. It can be empty, and it can be undefined.
4444
*/
4545
const labelPattern =
46-
/(?<!\\)\[(?<left>(?:\\.|[^\]])*)(?<!\\)\](?<!\\)(?:\[(?<right>(?:\\.|[^\]])*)(?<!\\)\])?/dgu;
46+
/(?<!\\)\[(?<left>(?:\\.|[^[\]])*)(?<!\\)\](?<!\\)(?:\[(?<right>(?:\\.|[^\]])*)(?<!\\)\])?/dgu;
4747

4848
let match;
4949

src/rules/no-missing-link-fragments.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import GithubSlugger from "github-slugger";
2828
const githubLineReferencePattern = /^L\d+(?:C\d+)?(?:-L\d+(?:C\d+)?)?$/u;
2929
const customHeadingIdPattern = /\{#([^}\s]+)\}\s*$/u;
3030
const htmlCommentPattern = /<!--[\s\S]*?-->/gu;
31-
const htmlIdNamePattern = /<(?:[^>]+)\s+(?:id|name)=["']([^"']+)["']/giu;
31+
const htmlIdNamePattern = /(?<!<)<(?:[^>]+)\s(?:id|name)=["']([^"']+)["']/giu;
3232

3333
/**
3434
* Checks if the fragment is a valid GitHub line reference

tests/rules/no-duplicate-headings.test.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
import rule from "../../src/rules/no-duplicate-headings.js";
1111
import markdown from "../../src/index.js";
12-
import { RuleTester } from "eslint";
12+
import { Linter, RuleTester } from "eslint";
1313
import dedent from "dedent";
1414

1515
//------------------------------------------------------------------------------
@@ -125,7 +125,7 @@ ruleTester.run("no-duplicate-headings", rule, {
125125
{
126126
code: dedent`
127127
# Heading 1
128-
128+
129129
# Heading 1 ##
130130
`,
131131
errors: [
@@ -141,7 +141,7 @@ ruleTester.run("no-duplicate-headings", rule, {
141141
{
142142
code: dedent`
143143
# Heading 1
144-
144+
145145
# Heading 1 ##########
146146
`,
147147
errors: [
@@ -378,3 +378,13 @@ Heading 1
378378
},
379379
],
380380
});
381+
382+
// https://github.com/eslint/markdown/pull/463
383+
it("`no-duplicate-headings` should not timeout for large inputs", () => {
384+
const linter = new Linter();
385+
linter.verify(`# example${" ".repeat(500_000)}?#`, {
386+
language: "markdown/commonmark",
387+
plugins: { markdown },
388+
rules: { "markdown/no-duplicate-headings": "error" },
389+
});
390+
});

tests/rules/no-missing-label-refs.test.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
import rule from "../../src/rules/no-missing-label-refs.js";
1111
import markdown from "../../src/index.js";
12-
import { RuleTester } from "eslint";
12+
import { Linter, RuleTester } from "eslint";
1313

1414
//------------------------------------------------------------------------------
1515
// Tests
@@ -383,3 +383,13 @@ ruleTester.run("no-missing-label-refs", rule, {
383383
},
384384
],
385385
});
386+
387+
// https://github.com/eslint/markdown/pull/463
388+
it("`no-missing-label-refs` should not timeout for large inputs", () => {
389+
const linter = new Linter();
390+
linter.verify("[abcd".repeat(100_000), {
391+
language: "markdown/commonmark",
392+
plugins: { markdown },
393+
rules: { "markdown/no-missing-label-refs": "error" },
394+
});
395+
});

tests/rules/no-missing-link-fragments.test.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
import rule from "../../src/rules/no-missing-link-fragments.js";
1111
import markdown from "../../src/index.js";
12-
import { RuleTester } from "eslint";
12+
import { Linter, RuleTester } from "eslint";
1313
import dedent from "dedent";
1414

1515
//------------------------------------------------------------------------------
@@ -658,3 +658,20 @@ ruleTester.run("no-missing-link-fragments", rule, {
658658
},
659659
],
660660
});
661+
662+
// https://github.com/eslint/markdown/pull/463
663+
it("`no-missing-link-fragments` should not timeout for large inputs", () => {
664+
const inputs = [
665+
`<div>${"<".repeat(500_000)}x</div>`,
666+
`<div><${" ".repeat(500_000)}x</div>`,
667+
];
668+
669+
const linter = new Linter();
670+
for (const input of inputs) {
671+
linter.verify(input, {
672+
language: "markdown/commonmark",
673+
plugins: { markdown },
674+
rules: { "markdown/no-missing-link-fragments": "error" },
675+
});
676+
}
677+
});

0 commit comments

Comments
 (0)