diff --git a/README.md b/README.md index 2dfd5cd1c5..e93504ca93 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Also read about: ## Usage -### Warning: 🚨 Marked does not [sanitize](https://marked.js.org/#/USING_ADVANCED.md#options) the output HTML by default 🚨 +### Warning: 🚨 Marked does not [sanitize](https://marked.js.org/#/USING_ADVANCED.md#options) the output HTML. Please use a sanitize library, like [DOMPurify](https://github.com/cure53/DOMPurify) (recommended), [sanitize-html](https://github.com/apostrophecms/sanitize-html) or [insane](https://github.com/bevacqua/insane) on the output HTML! 🚨 **CLI** diff --git a/docs/README.md b/docs/README.md index b31e52c09a..f5ba9d2b71 100644 --- a/docs/README.md +++ b/docs/README.md @@ -25,7 +25,7 @@ These documentation pages are also rendered using marked 💯

Usage

-### Warning: 🚨 Marked does not [sanitize](https://marked.js.org/#/USING_ADVANCED.md#options) the output HTML by default 🚨 +### Warning: 🚨 Marked does not [sanitize](https://marked.js.org/#/USING_ADVANCED.md#options) the output HTML. Please use a sanitize library, like [DOMPurify](https://github.com/cure53/DOMPurify) (recommended), [sanitize-html](https://github.com/apostrophecms/sanitize-html) or [insane](https://github.com/bevacqua/insane) on the output HTML! 🚨 **CLI** diff --git a/docs/USING_ADVANCED.md b/docs/USING_ADVANCED.md index fd9fbaf2f4..8e24b65693 100644 --- a/docs/USING_ADVANCED.md +++ b/docs/USING_ADVANCED.md @@ -50,7 +50,7 @@ console.log(marked(markdownString)); |mangle |`boolean` |`true` |v0.3.4 |If true, autolinked email address is escaped with HTML character references.| |pedantic |`boolean` |`false` |v0.2.1 |If true, conform to the original `markdown.pl` as much as possible. Don't fix original markdown bugs or behavior. Turns off and overrides `gfm`.| |renderer |`object` |`new Renderer()`|v0.3.0|An object containing functions to render tokens to HTML. See [extensibility](USING_PRO.md) for more details.| -|sanitize |`boolean` |`false` |v0.2.1 |If true, sanitize the HTML passed into `markdownString` with the `sanitizer` function.| +|sanitize |`boolean` |`false` |v0.2.1 |If true, sanitize the HTML passed into `markdownString` with the `sanitizer` function.
**Warning**: This feature is deprecated and it should NOT be used as it cannot be considered secure.
Instead use a sanitize library, like [DOMPurify](https://github.com/cure53/DOMPurify) (recommended), [sanitize-html](https://github.com/apostrophecms/sanitize-html) or [insane](https://github.com/bevacqua/insane) on the output HTML! | |sanitizer |`function`|`null` |v0.3.4 |A function to sanitize the HTML passed into `markdownString`.| |silent |`boolean` |`false` |v0.2.7 |If true, the parser does not throw any exception.| |smartLists |`boolean` |`false` |v0.2.8 |If true, use smarter list behavior than those found in `markdown.pl`.| diff --git a/lib/marked.js b/lib/marked.js index 319a3c6430..ce8e033e95 100644 --- a/lib/marked.js +++ b/lib/marked.js @@ -431,7 +431,7 @@ Lexer.prototype.token = function(src, top) { : 'html', pre: !this.options.sanitizer && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'), - text: cap[0] + text: this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0])) : cap[0] }); continue; } @@ -725,7 +725,7 @@ InlineLexer.prototype.output = function(src) { if (cap = this.rules.link.exec(src)) { var lastParenIndex = findClosingBracket(cap[2], '()'); if (lastParenIndex > -1) { - var linkLen = cap[0].length - (cap[2].length - lastParenIndex) - (cap[3] || '').length; + var linkLen = 4 + cap[1].length + lastParenIndex; cap[2] = cap[2].substring(0, lastParenIndex); cap[0] = cap[0].substring(0, linkLen).trim(); cap[3] = ''; @@ -847,7 +847,7 @@ InlineLexer.prototype.output = function(src) { if (cap = this.rules.text.exec(src)) { src = src.substring(cap[0].length); if (this.inRawBlock) { - out += this.renderer.text(cap[0]); + out += this.renderer.text(this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0])) : cap[0]); } else { out += this.renderer.text(escape(this.smartypants(cap[0]))); } @@ -1536,6 +1536,12 @@ function findClosingBracket(str, b) { return -1; } +function checkSanitizeDeprecation(opt) { + if (opt && opt.sanitize && !opt.silent) { + console.warn('marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options'); + } +} + /** * Marked */ @@ -1557,6 +1563,7 @@ function marked(src, opt, callback) { } opt = merge({}, marked.defaults, opt || {}); + checkSanitizeDeprecation(opt); var highlight = opt.highlight, tokens, @@ -1621,6 +1628,7 @@ function marked(src, opt, callback) { } try { if (opt) opt = merge({}, marked.defaults, opt); + checkSanitizeDeprecation(opt); return Parser.parse(Lexer.lex(src, opt), opt); } catch (e) { e.message += '\nPlease report this to https://github.com/markedjs/marked.'; diff --git a/test/specs/new/links_paren.html b/test/specs/new/links_paren.html new file mode 100644 index 0000000000..375c0f0cf1 --- /dev/null +++ b/test/specs/new/links_paren.html @@ -0,0 +1,5 @@ +

(one) (two)

+ +

(one) (two)

+ +

(one) (two)

diff --git a/test/specs/new/links_paren.md b/test/specs/new/links_paren.md new file mode 100644 index 0000000000..96f106ce03 --- /dev/null +++ b/test/specs/new/links_paren.md @@ -0,0 +1,5 @@ +([one](http://example.com/1)) ([two](http://example.com/2)) + +([one](http://example.com/1)) ([two](http://example.com/2)) + +([one](http://example.com/1 "a")) ([two](http://example.com/2 "b")) diff --git a/test/specs/run-spec.js b/test/specs/run-spec.js index a362af2296..3d5359bd58 100644 --- a/test/specs/run-spec.js +++ b/test/specs/run-spec.js @@ -16,6 +16,10 @@ function runSpecs(title, dir, showCompletionTable, options) { spec.options = Object.assign({}, options, (spec.options || {})); const example = (spec.example ? ' example ' + spec.example : ''); const passFail = (spec.shouldFail ? 'fail' : 'pass'); + if (spec.options.sanitizer) { + // eslint-disable-next-line no-eval + spec.options.sanitizer = eval(spec.options.sanitizer); + } (spec.only ? fit : (spec.skip ? xit : it))('should ' + passFail + example, () => { const before = process.hrtime(); if (spec.shouldFail) { @@ -40,3 +44,4 @@ runSpecs('CommonMark', './commonmark', true, { gfm: false, pedantic: false, head runSpecs('Original', './original', false, { gfm: false, pedantic: true }); runSpecs('New', './new'); runSpecs('ReDOS', './redos'); +runSpecs('Security', './security', false, { silent: true }); // silent - do not show deprecation warning diff --git a/test/specs/security/sanitizer_bypass.html b/test/specs/security/sanitizer_bypass.html new file mode 100644 index 0000000000..fb35223cf0 --- /dev/null +++ b/test/specs/security/sanitizer_bypass.html @@ -0,0 +1,6 @@ +

AAA<script> <img <script> src=x onerror=alert(1) />BBB

+ +

AAA<sometag> <img <sometag> src=x onerror=alert(1)BBB

+ +

<a>a2<a2t>a2</a> b <c>c</c> d

+

text

diff --git a/test/specs/security/sanitizer_bypass.md b/test/specs/security/sanitizer_bypass.md new file mode 100644 index 0000000000..99091d0198 --- /dev/null +++ b/test/specs/security/sanitizer_bypass.md @@ -0,0 +1,9 @@ +--- +sanitize: true +--- +AAA