diff --git a/lib/marked.js b/lib/marked.js index 39c25f2610..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,16 +301,26 @@ Lexer.prototype.token = function(src, top) { type: 'blockquote_start' }); - cap = cap[0].replace(/^ *> ?/gm, ''); + blockquote = cap[0].replace(/^ *> ?/gm, ''); + 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 +1245,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 = ''; 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) +}; 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);