From b15e42b67cec9ded8505e9d68bb8741ad7a9590d Mon Sep 17 00:00:00 2001 From: Jamie Davis Date: Mon, 26 Feb 2018 11:06:41 -0500 Subject: [PATCH] security: fix regexes vulnerable to catastrophic backtracking Problem: Four regexes were vulnerable to catastrophic backtracking. This leaves markdown servers open to a potential REDOS attack. Solution: Refactor the regexes. For two similar regexes (html) I didn't change the language. For two similar regexes (noline) I slightly changed the language: ![[[[[[[[[[[]] was accepted by the old noline pattern. It is now rejected. All tests pass, though I'm not sure if I've broken something that was untested. This addresses #1070 (with #1058 along the way). Bonus: rename a stray test to use _ instead of -. --- lib/marked.js | 8 ++++---- test/new/{headings-id.html => headings_id.html} | 0 test/new/{headings-id.md => headings_id.md} | 0 test/new/redos_html_closing.html | 0 test/new/redos_html_closing.md | 1 + test/new/redos_nolink.html | 0 test/new/redos_nolink.md | 1 + 7 files changed, 6 insertions(+), 4 deletions(-) rename test/new/{headings-id.html => headings_id.html} (100%) rename test/new/{headings-id.md => headings_id.md} (100%) create mode 100644 test/new/redos_html_closing.html create mode 100644 test/new/redos_html_closing.md create mode 100644 test/new/redos_nolink.html create mode 100644 test/new/redos_nolink.md diff --git a/lib/marked.js b/lib/marked.js index 5ca95941ec..a6a2bbe6a5 100644 --- a/lib/marked.js +++ b/lib/marked.js @@ -55,7 +55,7 @@ block._tag = '(?!(?:' block.html = edit(block.html) .replace('comment', //) .replace('closed', /<(tag)[\s\S]+?<\/\1>/) - .replace('closing', /]*)*?\/?>/) + .replace('closing', /\s]*)*?\/?>/) .replace(/tag/g, block._tag) .getRegex(); @@ -461,10 +461,10 @@ var inline = { escape: /^\\([\\`*{}\[\]()#+\-.!_>])/, autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/, url: noop, - tag: /^|^<\/?[a-zA-Z0-9\-]+(?:"[^"]*"|'[^']*'|\s[^<'">\/]*)*?\/?>/, + tag: /^|^<\/?[a-zA-Z0-9\-]+(?:"[^"]*"|'[^']*'|\s[^<'">\/\s]*)*?\/?>/, link: /^!?\[(inside)\]\(href\)/, reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/, - nolink: /^!?\[((?:\[[^\]]*\]|\\[\[\]]|[^\[\]])*)\]/, + nolink: /^!?\[((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\]/, strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/, em: /^_([^\s_](?:[^_]|__)+?[^\s_])_\b|^\*((?:\*\*|[^*])+?)\*(?!\*)/, code: /^(`+)\s*([\s\S]*?[^`]?)\s*\1(?!`)/, @@ -481,7 +481,7 @@ inline.autolink = edit(inline.autolink) .replace('email', inline._email) .getRegex() -inline._inside = /(?:\[[^\]]*\]|\\[\[\]]|[^\[\]]|\](?=[^\[]*\]))*/; +inline._inside = /(?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]]|\](?=[^\[]*\]))*/; inline._href = /\s*?(?:\s+['"]([\s\S]*?)['"])?\s*/; inline.link = edit(inline.link) diff --git a/test/new/headings-id.html b/test/new/headings_id.html similarity index 100% rename from test/new/headings-id.html rename to test/new/headings_id.html diff --git a/test/new/headings-id.md b/test/new/headings_id.md similarity index 100% rename from test/new/headings-id.md rename to test/new/headings_id.md diff --git a/test/new/redos_html_closing.html b/test/new/redos_html_closing.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/new/redos_html_closing.md b/test/new/redos_html_closing.md new file mode 100644 index 0000000000..65bc5f7362 --- /dev/null +++ b/test/new/redos_html_closing.md @@ -0,0 +1 @@ +a'a diff --git a/test/new/redos_nolink.html b/test/new/redos_nolink.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/new/redos_nolink.md b/test/new/redos_nolink.md new file mode 100644 index 0000000000..a43a156820 --- /dev/null +++ b/test/new/redos_nolink.md @@ -0,0 +1 @@ +![\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]\[[]!\