rehype-sanitize

repository·main·Indexed 42 days ago

https://github.com/rehypejs/rehype-sanitize

A unified/rehype plugin version 6.0.0 designed to sanitize HTML by stripping elements or attributes not permitted by a schema. It is used to process untrusted user input and mitigate XSS and DOM clobbering. By default, it uses a GitHub-style sanitation schema, but it supports custom schemas via hast-util-sanitize.

Tokens
2.2K
Snippets
8
Records
11
Agent score
4%

What's inside rehype-sanitize

  1. Security best practices for rehype-sanitize

    main

    The default configuration of rehype-sanitize is safe, but improper usage can lead to Cross-Site Scripting (XSS) attacks.

    Crucial Rule: Always use rehype-sanitize after the last potentially unsafe operation. Any plugin or transformation executed after rehype-sanitize is not covered by the sanitization and could introduce unsafe HTML.

  2. Prevent DOM clobbering with rehype-sanitize

    main

    DOM clobbering is an attack where malicious HTML uses id or name attributes to overshadow properties in the global window scope. rehype-sanitize mitigates this by prefixing every id and name attribute with 'user-content-'.

    Note that this will break existing links to those IDs. To fix this, you should either rewrite your links to include the prefix or use a client-side script to intercept hash changes and scroll to the prefixed elements (similar to how GitHub handles it).

    // Example of how rehype-sanitize changes attributes
    // Input:
    // <a name="old"></a>
    // <h1 id="current">Current</h1>
    
    // Output after .use(rehypeSanitize):
    // <a name="user-content-old"></a>
    // <h1 id="user-content-current">Current</h1>
  3. Install rehype-sanitize

    main

    rehype-sanitize is an ESM-only package. Use the following commands depending on your environment:

    Node.js (version 16+):

    npm install rehype-sanitize

    Deno (via esm.sh):

    import rehypeSanitize from 'https://esm.sh/rehype-sanitize@6'

    Browsers (via esm.sh):

    <script type="module">
      import rehypeSanitize from 'https://esm.sh/rehype-sanitize@6?bundle'
    </script>
    npm install rehype-sanitize
  4. Use rehype-sanitize to sanitize HTML

    main

    To sanitize HTML, integrate rehypeSanitize into a unified pipeline. The plugin works by transforming a hast (HTML Abstract Syntax Tree) and dropping any elements or attributes not explicitly allowed by the schema. By default, it uses a schema that follows GitHub's sanitation style.

    Example workflow using rehype-parse, rehype-sanitize, and rehype-stringify:

    import rehypeParse from 'rehype-parse'
    import rehypeSanitize from 'rehype-sanitize'
    import rehypeStringify from 'rehype-stringify'
    import {read} from 'to-vfile'
    import {unified} from 'unified'
    
    const file = await unified()
      .use(rehypeParse, {fragment: true})
      .use(rehypeSanitize)
      .use(rehypeStringify)
      .process(await read('index.html'))
    
    console.log(String(file))
  5. Configure rehype-sanitize for Math (rehype-katex/rehype-mathjax)

    main

    When using math plugins like rehype-katex or rehype-mathjax, rehype-sanitize will strip the complex markup and inline styles they generate by default.

    To allow math, do not try to allow all inline styles (which is unsafe). Instead, sanitize first using a custom schema that allows the specific classes used by the math plugins, and then run the math plugin. This ensures the math markup is generated on already-sanitized content.

    import rehypeKatex from 'rehype-katex'
    import rehypeParse from 'rehype-parse'
    import rehypeSanitize, {defaultSchema} from 'rehype-sanitize'
    import rehypeStringify from 'rehype-stringify'
    import {unified} from 'unified'
    
    const file = await unified()
      .use(rehypeParse, {fragment: true})
      .use(rehypeSanitize, {
        ...defaultSchema,
        attributes: {
          ...defaultSchema.attributes,
          // Allow the specific classes used by math plugins
          code: [['className', /^language-./, 'math-inline', 'math-display']]
        }
      })
      .use(rehypeKatex)
      .use(rehypeStringify)
      .process('<code class="math-inline">L</code>')
  6. Configure rehype-sanitize for Syntax Highlighting (rehype-highlight)

    main

    To use syntax highlighting with rehype-highlight, you have two main strategies:

    1. Allow specific languages: Sanitize first by allowing specific className values for code elements (e.g., language-js), then run the highlighter.
    2. Allow all token classes: Run the highlighter first, then sanitize by allowing all the specific className values used by the highlighter's tokens on span elements.

    Strategy 1 is generally preferred as it is more restrictive and safer.

    import rehypeHighlight from 'rehype-highlight'
    import rehypeParse from 'rehype-parse'
    import rehypeSanitize, {defaultSchema} from 'rehype-sanitize'
    import rehypeStringify from 'rehype-stringify'
    import {unified} from 'unified'
    
    const file = await unified()
      .use(rehypeParse, {fragment: true})
      .use(rehypeSanitize, {
        ...defaultSchema,
        attributes: {
          ...defaultSchema.attributes,
          code: [
            ...(defaultSchema.attributes.code || []),
            // List of all allowed languages:
            ['className', 'language-js', 'language-css', 'language-md']
          ]
        }
      })
      .use(rehypeHighlight, {subset: false})
      .use(rehypeStringify)
      .process('<pre><code className="language-js">console.log(1)</code></pre>')
  7. Configure rehype-sanitize with a custom schema

    main

    You can pass a custom schema to rehypeSanitize to define which nodes and properties are allowed. This schema follows the Schema definition from hast-util-sanitize.

    Note: This is an advanced configuration that requires knowledge of syntax trees.

    // Usage pattern
    .use(rehypeSanitize, customSchema)
  8. Use rehype-sanitize as a unified plugin

    main

    The default export of rehype-sanitize is a plugin for unified (and by extension rehype). You can use it by calling .use(rehypeSanitize) on your unified instance. You can optionally provide a custom schema to override the default sanitization rules.

    import { unified } from 'unified'
    import rehypeParse from 'rehype-parse'
    import rehypeStringify from 'rehype-stringify'
    import rehypeSanitize from 'rehype-sanitize'
    
    const processor = unified()
      .use(rehypeParse)
      .use(rehypeSanitize) // Uses defaultSchema
      .use(rehypeStringify)
    
    const html = '<img src=x onerror=alert(1)>'
    const result = await processor.process(html)
    console.log(result.toString()) // Output will be sanitized
  9. Import the default schema from rehype-sanitize

    main

    You can import defaultSchema directly from rehype-sanitize. This schema is exported from hast-util-sanitize and defines the default allowed elements, attributes, and protocols for sanitization.

    import { defaultSchema } from 'rehype-sanitize'