Skip to content

Commit

Permalink
security: replace vulnerable regex with parser (#1223)
Browse files Browse the repository at this point in the history
* security: replace vulnerable regex with parser

Problem: link regex was vulnerable
Solution: dedicated parser

Fixes: #1222
  • Loading branch information
davisjam authored and styfle committed Apr 17, 2018
1 parent fa15a37 commit 5ab4ae3
Showing 1 changed file with 62 additions and 3 deletions.
65 changes: 62 additions & 3 deletions lib/marked.js
Original file line number Diff line number Diff line change
Expand Up @@ -554,9 +554,68 @@ inline.normal = merge({}, inline);
inline.pedantic = merge({}, inline.normal, {
strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/,
link: edit(/^!?\[(label)\]\(\s*<?([\s\S]*?)>?(?:\s+(['"][\s\S]*?['"]))?\s*\)/)
.replace('label', inline._label)
.getRegex(),
/* Original link re: /^!?\[(label)\]\(\s*<?([\s\S]*?)>?(?:\s+(['"][\s\S]*?['"]))?\s*\)/
* This captures the spec reasonably well but is vulnerable to REDOS.
* Instead we use a custom parser that follows the RegExp.exec semantics. */
link: {
exec: function (s) {
// [TEXT](DESTINATION)
var generalLinkRe = edit(/^!?\[(label)\]\((.*?)\)/)
.replace('label', inline._label)
.getRegex();

// destination: DESTINATION from generalLinkRe
// returns [destination, title]: no angle-brackets on destination, no quotes on title
function splitIntoDestinationAndTitle (destination) {
function unwrapAngleBrackets (str) {
if (str.match(/^<.*>$/)) {
str = str.slice(1, -1);
}
return str;
}

// Valid DESTINATIONs, in decreasing specificity.
var destinationAndTitleRe = /^([^'"(]*[^\s])\s+(['"(].*['")])/;
var destinationRe = /^(<?[\s\S]*>?)/;
var parsingRegexes = [destinationAndTitleRe, destinationRe];

var match = false;
for (var i = 0; i < parsingRegexes.length; i++) {
match = parsingRegexes[i].exec(destination);
if (match) {
break;
}
}

if (!match) {
return null;
}

var dest = match[1];
var title = match[2] || ''; // Not all parsingRegexes have 2 groups.

// Format dest.
dest = dest.trim();
dest = unwrapAngleBrackets(dest);

return [dest, title];
}

var fullMatch = generalLinkRe.exec(s);
if (!fullMatch) {
return null;
}

var text = fullMatch[1];
var destination = fullMatch[2];

var destinationAndTitle = splitIntoDestinationAndTitle(destination);
if (!destinationAndTitle) {
return null;
}
return [fullMatch[0], text, destinationAndTitle[0], destinationAndTitle[1]];
}
},
reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/)
.replace('label', inline._label)
.getRegex()
Expand Down

0 comments on commit 5ab4ae3

Please sign in to comment.