From 8da970cd2ccb0c4d38c2d0f7dcb51700e519cdf4 Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Tue, 9 Apr 2019 10:17:02 -0500 Subject: [PATCH 1/4] no recursion for nested blockquotes --- lib/marked.js | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/lib/marked.js b/lib/marked.js index 39c25f2610..f87970e1e6 100644 --- a/lib/marked.js +++ b/lib/marked.js @@ -299,16 +299,26 @@ Lexer.prototype.token = function(src, top) { type: 'blockquote_start' }); - cap = cap[0].replace(/^ *> ?/gm, ''); + var blockquote = cap[0].replace(/^ *> ?/gm, ''); + var count = 1; + while (blockquote.match(/^ {0,3}>/)) { + count++; + this.tokens.push({ + type: 'blockquote_start' + }); + blockquote = blockquote.replace(/^ *> ?/gm, ''); + } // Pass `top` to keep the current // "toplevel" state. This is exactly // how markdown.pl works. - this.token(cap, top); + this.token(blockquote, top); - this.tokens.push({ - type: 'blockquote_end' - }); + for (i = 0; i < count; i++) { + this.tokens.push({ + type: 'blockquote_end' + }); + } continue; } @@ -1233,13 +1243,27 @@ Parser.prototype.tok = function() { return this.renderer.table(header, body); } case 'blockquote_start': { + var count = 1; + while (this.peek() && this.peek().type === 'blockquote_start') { + this.next(); + count++; + } + body = ''; while (this.next().type !== 'blockquote_end') { body += this.tok(); } - return this.renderer.blockquote(body); + while (this.peek() && this.peek().type === 'blockquote_end') { + this.next(); + } + + for (i = 0; i < count; i++) { + body = this.renderer.blockquote(body); + } + + return body; } case 'list_start': { body = ''; From c8521e51e196a32f93a72c9bb927af70529e298f Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Tue, 9 Apr 2019 10:20:12 -0500 Subject: [PATCH 2/4] add test --- test/redos/nested_blockquote.js | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 test/redos/nested_blockquote.js diff --git a/test/redos/nested_blockquote.js b/test/redos/nested_blockquote.js new file mode 100644 index 0000000000..11e5f5f9d9 --- /dev/null +++ b/test/redos/nested_blockquote.js @@ -0,0 +1,4 @@ +module.exports = { + markdown: '>'.repeat(5000), + html: '
'.repeat(5000) + '
'.repeat(5000) +}; From 8316a936cd95d1110801786f965b71851c24ab8f Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Fri, 12 Apr 2019 10:56:54 -0500 Subject: [PATCH 3/4] clean up code --- lib/marked.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/marked.js b/lib/marked.js index f87970e1e6..42e0e8a08c 100644 --- a/lib/marked.js +++ b/lib/marked.js @@ -200,7 +200,9 @@ Lexer.prototype.token = function(src, top) { l, isordered, istask, - ischecked; + ischecked, + blockquote, + count; while (src) { // newline @@ -299,8 +301,8 @@ Lexer.prototype.token = function(src, top) { type: 'blockquote_start' }); - var blockquote = cap[0].replace(/^ *> ?/gm, ''); - var count = 1; + blockquote = cap[0].replace(/^ *> ?/gm, ''); + count = 1; while (blockquote.match(/^ {0,3}>/)) { count++; this.tokens.push({ From 0620845bf0ee313b7d22cb417b8b804d6a34e8dd Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Fri, 12 Apr 2019 11:33:41 -0500 Subject: [PATCH 4/4] allow redos spec only --- test/specs/redos-spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/specs/redos-spec.js b/test/specs/redos-spec.js index 1f94a42e99..9e0466df7d 100644 --- a/test/specs/redos-spec.js +++ b/test/specs/redos-spec.js @@ -10,8 +10,8 @@ describe('ReDOS tests', () => { return; } - it(file, () => { - const spec = require(path.resolve(redosDir, file)); + const spec = require(path.resolve(redosDir, file)); + (spec.only ? fit : it)(file, () => { const before = process.hrtime(); expect(spec).toRender(spec.html); const elapsed = process.hrtime(before);