Skip to content
JavaScript

Why Regex Falls Short for Markdown Security: A Practical Guide

Exploring the limitations of regex in sanitizing Markdown and the importance of placing security boundaries after parsing.

Topic
JavaScript
Reading time
4 min
Length
919 words
Published
Aug 31, 2026
01:29 pm IST
In this article
  1. Understanding the Limitation of Regex in Markdown Security
  2. The Core Issue with Regex
  3. The Recommended Approach: Parsing and Sanitizing
  4. Practical Recommendations for Your Codebase
  5. Limitations and Considerations

Understanding the Limitation of Regex in Markdown Security

When dealing with Markdown content, it's tempting to use regular expressions (regex) to sanitize inputs, especially to remove potentially harmful HTML tags like <script>. However, regex fails to be a reliable security boundary for Markdown because it operates at a different abstraction level than HTML parsing. In this post, we'll explore why regex isn't sufficient for securing Markdown and what practical steps you should take to ensure robust security.

The Core Issue with Regex

The main problem with using regex for sanitizing Markdown is that it doesn't account for all variations and permutations of HTML that a browser's parser can handle. A regex might successfully remove a straightforward <script> tag or an event attribute like onerror="alert(1)", but it can easily miss variations such as unquoted or single-quoted attributes. For instance, a regex may strip onerror="alert(1)" but leave onerror='alert(1)' or onerror=alert(1) intact. This discrepancy means that a regex-based approach will often leave holes in your security strategy. As the source article explains, the browser consumes a DOM, not the original Markdown string. Thus, the regex and HTML parsing operate at different abstraction levels, which can lead to security oversights.

To further illustrate, consider the challenge of handling JavaScript URLs. A regex might attempt to remove 'javascript:' from URLs, but simply altering the string doesn't ensure that the final URL complies with the application's protocol or navigation policies. This highlights the fundamental issue: regex is about string matching, whereas browsers parse HTML at a more complex level, interpreting and rendering content according to a wide range of specifications and standards.

The source article suggests a more reliable approach that involves parsing the Markdown first and then applying HTML sanitization. This method ensures that the security boundary is established after parsing, which is crucial for accurate and effective sanitization. The recommended sequence is:

  • Markdown source
  • Pinned parser
  • HTML sanitizer for the real output policy
  • Controlled DOM sink
  • Content Security Policy (CSP) or Trusted Types as defense in depth

This sequence allows you to handle the raw HTML and Markdown content separately, ensuring that the final output meets your application's security policies. For instance, DOMPurify is mentioned as a tool that works on a parsed, inert DOM and applies an allow-list to elements and attributes, making it a safer choice for sanitizing HTML. The strategy of using a pinned parser and sanitizer ensures that the behavior remains consistent across different environments and updates.

In practice, this means your application should first convert Markdown to HTML using a well-defined parser like Marked. This parser should be pinned to a specific version to prevent unexpected changes in behavior. After parsing, the HTML should be sanitized using a tool like DOMPurify, which is specifically designed to remove potentially dangerous elements and attributes. By applying these steps, you create a robust security boundary that accounts for the complexities of HTML parsing and rendering.

Practical Recommendations for Your Codebase

Moving forward with this understanding, here are some practical steps I'd recommend implementing in your codebase:

  • Pin Parser and Sanitizer Versions: Use specific versions of libraries like Marked and DOMPurify to ensure consistent behavior across environments. This avoids unexpected changes in how HTML is parsed or sanitized.
  • Disable Raw HTML if Not Needed: If your application doesn't require raw HTML, disable it to reduce the attack surface. This is particularly important when users do not need to input HTML directly.
  • Start with a Narrow Allow-List: When HTML is necessary, define a strict allow-list for elements and attributes to minimize risk. This involves explicitly enumerating which tags and attributes are allowed and rejecting everything else by default.
  • Define Protocol and Resource Rules: Establish clear rules for href and src attributes to control navigation and resource loading. This includes ensuring that links only point to trusted domains and resources are loaded over secure connections.
  • Avoid Post-Sanitization Modifications: Do not modify sanitized HTML. Instead, use safe methods like textContent for inserting untrusted values. This prevents reintroducing vulnerabilities after sanitizing content.
  • Inspect Final Outputs: Always inspect the final DOM and network behavior, not just the source string. This includes testing in different environments like CMS, browsers, and mobile layouts to ensure consistent security across platforms.

These steps help ensure that your application remains secure against XSS attacks and other vulnerabilities. For a deeper understanding of implementing secure practices, you might find concepts discussed in Silent Audio Fingerprinting on AliExpress: Security Implications insightful, as it highlights the importance of robust security measures.

Limitations and Considerations

While the outlined strategy significantly enhances security, it's important to recognize its limitations. For example, sanitized images can still make network requests, which might have privacy implications. Similarly, allowed styles can obscure content without affecting script safety. Therefore, it's crucial to differentiate between XSS prevention and privacy concerns, as well as script safety and visual-integrity policy.

Moreover, the approach requires discipline in handling untrusted content throughout the application's lifecycle. For instance, if you're using a CMS or a template engine, validate content at every boundary where it might be reparsed or mutated. This ensures that security is maintained even when content is transformed or delivered through different systems.

In my experience, maintaining a robust security posture involves continuous testing and validation. Retesting in the destination CMS, browser, and mobile layout helps catch potential issues that might not be apparent in the initial implementation. This proactive approach helps ensure that the security measures remain effective as the application evolves and as potential threats change over time.

Sources

Regex Cannot Sanitize Markdown: Put the Security Boundary After Parsing

Every claim above was checked against this source before publishing. The analysis, the code and the opinions are mine.

Frequently asked

Why can't regex fully sanitize Markdown?

Regex operates at a string level, missing variations in HTML parsing, whereas browsers parse at a DOM level, allowing for unhandled security gaps.

What is the recommended approach for Markdown security?

Parse the Markdown first, then apply HTML sanitization using tools like DOMPurify, ensuring security boundaries are set post-parsing.

What are the limitations of this security strategy?

Sanitized content can still pose risks like network requests or visual obfuscation, requiring separate controls for privacy and visual integrity.

Deepak Kumar

Written by

Deepak Kumar

Sr Software Engineer at India Today Group | Aaj Tak · MERN Stack · Generative AI

I build production web applications and Generative AI systems — React and Next.js on the front, Node.js and RAG pipelines behind them. I write here about what those systems actually do once real traffic hits them.

Message me