Merge pull request #2650 from Elberet/fix-parser

Fix: block parser uses regexes for stop tags, allows stricter matching
This commit is contained in:
Robin Ward 2014-08-18 11:06:08 -04:00
commit 49f0eaea13
5 changed files with 21 additions and 14 deletions

View File

@ -120,7 +120,7 @@ Discourse.Markdown.whiteListTag('span', 'class', /^bbcode-size-\d+$/);
// Handles `[code] ... [/code]` blocks // Handles `[code] ... [/code]` blocks
Discourse.Dialect.replaceBlock({ Discourse.Dialect.replaceBlock({
start: /(\[code\])([\s\S]*)/igm, start: /(\[code\])([\s\S]*)/igm,
stop: '[/code]', stop: /\[\/code\]/igm,
rawContents: true, rawContents: true,
emitter: function(blockContents) { emitter: function(blockContents) {

View File

@ -21,7 +21,7 @@ function flattenBlocks(blocks) {
Discourse.Dialect.replaceBlock({ Discourse.Dialect.replaceBlock({
start: /^`{3}([^\n\[\]]+)?\n?([\s\S]*)?/gm, start: /^`{3}([^\n\[\]]+)?\n?([\s\S]*)?/gm,
stop: '```', stop: /^```$/gm,
emitter: function(blockContents, matches) { emitter: function(blockContents, matches) {
var klass = Discourse.SiteSettings.default_code_lang; var klass = Discourse.SiteSettings.default_code_lang;
@ -54,7 +54,7 @@ Discourse.Dialect.on('parseNode', function (event) {
Discourse.Dialect.replaceBlock({ Discourse.Dialect.replaceBlock({
start: /(<pre[^\>]*\>)([\s\S]*)/igm, start: /(<pre[^\>]*\>)([\s\S]*)/igm,
stop: '</pre>', stop: /<\/pre>/igm,
rawContents: true, rawContents: true,
skipIfTradtionalLinebreaks: true, skipIfTradtionalLinebreaks: true,

View File

@ -379,7 +379,7 @@ Discourse.Dialect = {
if (!match) { return; } if (!match) { return; }
var lastChance = function() { var lastChance = function() {
return !next.some(function(e) { return e.indexOf(args.stop) !== -1; }); return !next.some(function(blk) { return blk.match(args.stop); });
}; };
// shave off start tag and leading text, if any. // shave off start tag and leading text, if any.
@ -387,7 +387,8 @@ Discourse.Dialect = {
leading = block.slice(0, pos), leading = block.slice(0, pos),
trailing = match[2] ? match[2].replace(/^\n*/, "") : ""; trailing = match[2] ? match[2].replace(/^\n*/, "") : "";
// just give up if there's no stop tag in this or any next block // just give up if there's no stop tag in this or any next block
if (block.indexOf(args.stop, pos + args.stop.length) === -1 && lastChance()) { return; } args.stop.lastIndex = block.length - trailing.length;
if (!args.stop.exec(block) && lastChance()) { return; }
if (leading.length > 0) { result.push(['p'].concat(this.processInline(leading))); } if (leading.length > 0) { result.push(['p'].concat(this.processInline(leading))); }
if (trailing.length > 0) { if (trailing.length > 0) {
next.unshift(MD.mk_block(trailing, block.trailing, next.unshift(MD.mk_block(trailing, block.trailing,
@ -405,10 +406,10 @@ Discourse.Dialect = {
startPos.push(args.start.lastIndex - m[0].length); startPos.push(args.start.lastIndex - m[0].length);
args.start.lastIndex = args.start.lastIndex - (m[2] ? m[2].length : 0); args.start.lastIndex = args.start.lastIndex - (m[2] ? m[2].length : 0);
} }
var endPos = [], offset = 0; args.stop.lastIndex = 0;
while ((pos = currentBlock.indexOf(args.stop, offset)) !== -1) { var endPos = [];
endPos.push(pos); while (m = (args.stop).exec(currentBlock)) {
offset += (pos + args.stop.length); endPos.push(args.stop.lastIndex - m[0].length);
} }
// go through the available end tags: // go through the available end tags:
@ -421,7 +422,8 @@ Discourse.Dialect = {
// found an end tag, but we must go up a level first. // found an end tag, but we must go up a level first.
ep++; nesting--; ep++; nesting--;
} else { } else {
// found an end tag and we're at the top: done! // found an end tag and we're at the top: done! -- or: start tag and end tag are
// identical, (i.e. startPos[sp] == endPos[ep]), so we don't do nesting at all.
actualEndPos = endPos[ep]; actualEndPos = endPos[ep];
break blockloop; break blockloop;
} }
@ -442,8 +444,9 @@ Discourse.Dialect = {
contentBlocks.push(currentBlock); contentBlocks.push(currentBlock);
} }
var before = currentBlock.slice(0, actualEndPos).replace(/\n*$/, ""), var stopLen = currentBlock.match(args.stop)[0].length,
after = currentBlock.slice(actualEndPos + args.stop.length).replace(/^\n*/, ""); before = currentBlock.slice(0, actualEndPos).replace(/\n*$/, ""),
after = currentBlock.slice(actualEndPos + stopLen).replace(/^\n*/, "");
if (before.length > 0) contentBlocks.push(MD.mk_block(before, "", currentBlock.lineNumber)); if (before.length > 0) contentBlocks.push(MD.mk_block(before, "", currentBlock.lineNumber));
if (after.length > 0) next.unshift(MD.mk_block(after, "", currentBlock.lineNumber + countLines(before))); if (after.length > 0) next.unshift(MD.mk_block(after, "", currentBlock.lineNumber + countLines(before)));

View File

@ -6,7 +6,7 @@ var esc = Handlebars.Utils.escapeExpression;
Discourse.Dialect.replaceBlock({ Discourse.Dialect.replaceBlock({
start: new RegExp("\\[quote(=[^\\[\\]]+)?\\]([\\s\\S]*)", "igm"), start: new RegExp("\\[quote(=[^\\[\\]]+)?\\]([\\s\\S]*)", "igm"),
stop: '[/quote]', stop: /\[\/quote\]/igm,
emitter: function(blockContents, matches, options) { emitter: function(blockContents, matches, options) {
var params = {'class': 'quote'}, var params = {'class': 'quote'},

View File

@ -365,13 +365,17 @@ test("Code Blocks", function() {
"<p><pre><code class=\"lang-auto\">hello</code></pre></p>", "<p><pre><code class=\"lang-auto\">hello</code></pre></p>",
"it doesn't not whitelist all classes"); "it doesn't not whitelist all classes");
cooked("```[quote=\"sam, post:1, topic:9441, full:true\"]This is `<not>` a bug.[/quote]```", cooked("```\n[quote=\"sam, post:1, topic:9441, full:true\"]This is `<not>` a bug.[/quote]\n```",
"<p><pre><code class=\"lang-auto\">[quote=&quot;sam, post:1, topic:9441, full:true&quot;]This is &#x60;&lt;not&gt;&#x60; a bug.[/quote]</code></pre></p>", "<p><pre><code class=\"lang-auto\">[quote=&quot;sam, post:1, topic:9441, full:true&quot;]This is &#x60;&lt;not&gt;&#x60; a bug.[/quote]</code></pre></p>",
"it allows code with backticks in it"); "it allows code with backticks in it");
cooked(" hello\n<blockquote>test</blockquote>", cooked(" hello\n<blockquote>test</blockquote>",
"<pre><code>hello</code></pre>\n\n<blockquote>test</blockquote>", "<pre><code>hello</code></pre>\n\n<blockquote>test</blockquote>",
"it allows an indented code block to by followed by a `<blockquote>`"); "it allows an indented code block to by followed by a `<blockquote>`");
cooked("``` foo bar ```",
"<p><code>foo bar</code></p>",
"it tolerates misuse of code block tags as inline code");
}); });
test("sanitize", function() { test("sanitize", function() {