Safeguard
Open Source

marked on npm: Security Review and Safe Usage

marked is a fast Markdown parser, but it does not sanitize output and older versions carried a ReDoS bug. Here is how to use marked npm without opening an XSS hole.

Priya Mehta
Security Analyst
5 min read

The marked npm package is safe to use, but only if you sanitize its HTML output yourself, because marked deliberately does not do it for you. marked is one of the most popular Markdown-to-HTML parsers in the JavaScript ecosystem, and its speed is a big reason for that. The trade-off is that its threat model puts sanitization squarely on the caller.

This review walks through the two security topics that come up with npm marked: the removed sanitizer and its consequences, and an older regular-expression bug worth knowing if you inherit a legacy pin.

What marked does and does not promise

Install and use it like this:

npm install marked
const { marked } = require('marked');
const html = marked.parse('# Hello\n\nSome **bold** text.');

marked turns Markdown into an HTML string. Historically it shipped a sanitize option, but that option was deprecated and removed. The maintainers made a deliberate decision: sanitization is a hard, evolving problem best handled by a dedicated library, and baking a half-measure into a parser gives users false confidence. So current marked returns HTML exactly as the Markdown describes, including any raw HTML embedded in the source.

That means if you feed marked untrusted Markdown and drop the result straight into the DOM, you have an XSS vulnerability. This is not a bug in marked; it is the documented contract.

The sanitization gap, concretely

Consider Markdown that contains raw HTML or a crafted link:

[click me](javascript:alert(document.cookie))

<img src=x onerror="alert(1)">

marked will happily emit an anchor with a javascript: href and pass the img tag through. Rendered without sanitization, both can execute in a victim's browser. The defensive framing here is simple: treat marked's output as untrusted HTML, always.

The fix is to run the output through an allowlist sanitizer before it touches the page. DOMPurify is the standard pairing:

const { marked } = require('marked');
const createDOMPurify = require('dompurify');
const { JSDOM } = require('jsdom');
const DOMPurify = createDOMPurify(new JSDOM('').window);

const dirty = marked.parse(userMarkdown);
const clean = DOMPurify.sanitize(dirty);
// insert `clean` into the DOM

Snyk's write-up on fixing marked XSS reaches the same conclusion: marked plus a dedicated sanitizer, not marked alone. If you would rather never allow raw HTML at all, you can also configure marked to be stricter and reject or escape HTML blocks, but a sanitizer is still the belt to that suspenders.

The older ReDoS issue

Beyond sanitization, marked had a regular-expression denial-of-service problem in older releases. Versions in the 0.3.x through 0.6.x range evaluated certain email-like inputs in quadratic time, so a specially crafted string could pin a Node process's CPU and exhaust the event loop. The reported severity was moderate.

If you are on a current marked release, this is behind you. The reason to mention it is that legacy projects sometimes pin an ancient marked@0.x and never revisit it. If npm ls marked shows something in the 0.x line, treat that as a signal to upgrade, both for the ReDoS fix and for years of accumulated parser improvements. ReDoS is a recurring theme across the npm ecosystem, so untrusted text hitting an old regex-heavy parser is a pattern worth catching in review.

Safe usage checklist for marked npm

  • Never render marked output without sanitizing it. Pair it with DOMPurify or an equivalent allowlist sanitizer as the last step before the DOM.
  • Upgrade off any 0.x pin. Current major versions fixed the ReDoS behavior and many other edge cases.
  • Constrain input size. Even a well-behaved parser can be a soft target under multi-megabyte inputs. Cap the Markdown length you accept from untrusted sources.
  • Keep it in your dependency scan. marked releases regularly, and staying current is the cheapest control you have. Automated software composition analysis will alert you if a new advisory lands.

marked versus other Markdown parsers

Teams sometimes weigh marked against markdown-it or remark. From a security angle the picture is similar across all of them: the parser produces HTML, and you sanitize. markdown-it disables HTML by default, which some teams find a safer starting posture, while marked is faster and leaner. Neither choice removes your obligation to sanitize rendered output. Pick on ergonomics and performance, then apply the same DOMPurify discipline regardless. The Safeguard academy covers how these rendering pipelines fit into a broader XSS defense.

FAQ

Is the marked npm package safe?

Yes, when used correctly. marked is a well-maintained, fast Markdown parser with no serious open vulnerabilities in current versions. It does not sanitize its HTML output, though, so you must run that output through a sanitizer like DOMPurify before rendering it, or you risk XSS.

Why does marked not sanitize HTML anymore?

The maintainers removed the built-in sanitize option because it gave a false sense of safety and duplicated work that dedicated libraries do better. Sanitization is now explicitly the caller's responsibility, and the recommended pairing is a purpose-built sanitizer such as DOMPurify.

What was the marked ReDoS vulnerability?

Older marked releases in roughly the 0.3.x to 0.6.x range processed certain email-like inputs in quadratic time, allowing a crafted string to consume excessive CPU and cause a denial of service. It was rated moderate severity and is fixed in current versions.

How do I stop XSS when using npm marked?

Sanitize the HTML marked produces before inserting it into the page. Use DOMPurify with an allowlist configuration, cap the size of untrusted Markdown you accept, and stay on a current marked version. Do not rely on marked to strip dangerous content, because it will not.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.