diff --git a/lib/marked.js b/lib/marked.js index 83974865e4..837c586778 100644 --- a/lib/marked.js +++ b/lib/marked.js @@ -217,7 +217,7 @@ Lexer.prototype.token = function(src, top) { this.tokens.push({ type: 'code', text: !this.options.pedantic - ? cap.replace(/\n+$/, '') + ? rtrim(cap, '\n') : cap }); continue; @@ -1303,7 +1303,7 @@ function resolveUrl(base, href) { if (/^[^:]+:\/*[^/]*$/.test(base)) { baseUrls[' ' + base] = base + '/'; } else { - baseUrls[' ' + base] = base.replace(/[^/]*$/, ''); + baseUrls[' ' + base] = rtrim(base, '/', true); } } base = baseUrls[' ' + base]; @@ -1355,6 +1355,32 @@ function splitCells(tableRow, count) { return cells; } +// Remove trailing 'c's. Equivalent to str.replace(/c*$/, ''). +// /c*$/ is vulnerable to REDOS. +// invert: Remove suffix of non-c chars instead. Default falsey. +function rtrim(str, c, invert) { + if (str.length === 0) { + return ''; + } + + // Length of suffix matching the invert condition. + var suffLen = 0; + + // Step left until we fail to match the invert condition. + while (suffLen < str.length) { + var currChar = str.charAt(str.length - suffLen - 1); + if (currChar === c && !invert) { + suffLen++; + } else if (currChar !== c && invert) { + suffLen++; + } else { + break; + } + } + + return str.substr(0, str.length - suffLen); +} + /** * Marked */