HtmlSanitizeEx

repository·master·Indexed 43 days ago

https://github.com/rrrene/html_sanitize_ex

A fast Elixir-based HTML sanitizer designed to protect web applications against XSS by stripping or filtering untrusted HTML content. It provides built-in scrubbers for basic HTML, HTML5, and Markdown, as well as a DSL and behavior for creating custom scrubbers with fine-grained control over tags, attributes, and URI schemes. Includes specialized tools for CSS sanitization and URI validation.

Tokens
4.2K
Snippets
16
Records
21
Agent score
51%

What's inside HtmlSanitizeEx

  1. Create a custom Scrubber

    master

    You can define a custom scrubber by creating a module and using the HtmlSanitizeEx macro. This allows you to specify exactly which tags, attributes, and URI schemes are permitted. Anything not explicitly allowed will be stripped.

    Use the following macros within your module:

    • allow_tag_with_these_attributes(tag, attributes): Allows a specific tag with a list of allowed attributes.
    • allow_tag_with_uri_attributes(tag, attributes, uri_schemes): Allows a tag with specific attributes (like href) and restricts them to a list of allowed URI schemes (e.g., ["https", "mailto"]).

    Once defined, use YourModule.sanitize(html) to perform the scrubbing.

    defmodule MyProject.MyScrubber do
      use HtmlSanitizeEx
    
      allow_tag_with_these_attributes("p", [])
      allow_tag_with_these_attributes("h1", [])
    
      allow_tag_with_uri_attributes("a", ["href"], ["https", "mailto"])
    end
    
    # Usage
    text = "<h1>Hello <script>World!</script></h1>"
    MyProject.MyScrubber.sanitize(text)
    # => "<h1>Hello World!</h1>"
  2. Extend existing scrubbers

    master

    You can extend built-in scrubbers or your own custom scrubbers using the extend option in the use HtmlSanitizeEx macro. This allows you to inherit existing rules and add new ones.

    Supported built-in extensions:

    • :basic_html
    • :html5
    • :markdown_html
    • :strip_tags

    You can also extend a custom module you have already defined.

    # Extending a built-in scrubber
    defmodule MyProject.MyScrubber do
      use HtmlSanitizeEx, extend: :basic_html
    
      allow_tag_with_these_attributes("small", [])
    end
    
    # Extending a custom scrubber
    defmodule MyProject.MyOtherScrubber do
      use HtmlSanitizeEx, extend: MyProject.MyScrubber
    
      allow_tag_with_these_attributes("p", ["class"])
    end
  3. How custom scrubbers work

    master

    A scrubber is a module that defines a sanitization policy. When you use HtmlSanitizeEx, the library injects several behaviors into your module:

    1. Policy Definition: You use DSL commands (like allow_tag_with_these_attributes) to build a whitelist of allowed elements.
    2. Inheritance: If you use the extend: option, your module inherits the rules of the target scrubber as a fallback mechanism.
    3. The sanitize/1 function: The macro provides a sanitize(html) function that executes the scrubbing logic using the rules defined in your module.
    4. Default behaviors: By default, scrubbers are configured to remove_cdata_sections_before_scrub() and strip_comments().
  4. Implement a custom scrubber using NoScrub as a template

    master

    To create a custom HTML scrubber, you can implement the HtmlSanitizeEx.Scrubber behaviour. The HtmlSanitizeEx.Scrubber.NoScrub module serves as a template that preserves all tags, attributes, and text nodes.

    When implementing your own scrubber, you should define the following functions:

    1. sanitize(html): The entry point that calls HtmlSanitizeEx.Scrubber.scrub(html, __MODULE__).
    2. before_scrub(html): An optional hook to preprocess the HTML string before scrubbing begins.
    3. scrub(arg): The core logic that handles different HTML components:
      • {tag, attributes, children}: A single tag. You should call scrub_attributes/2 here.
      • {_token, children}: Tokens like comments or doctypes.
      • Text nodes (strings).
    4. scrub_attribute(tag, attribute): A function to sanitize individual attributes. Returning nil will remove the attribute from the tag.

    Example of implementing a specific rule (allowing only http or https in href attributes):

    # Example: Only allow href with http or https protocols
    def scrub_attribute("a", {"href", "http" <> target}) do
      {"href", "http" <> target}
    end
    
    def scrub_attribute("a", {"href", _}) do
      nil
    end
    
    # Fallback for other attributes
    def scrub_attribute(_tag, attribute) do
      attribute
    end
  5. Define custom scrubbers using HtmlSanitizeEx.Scrubber.Meta

    master

    To create a custom HTML scrubber, define a new module and require HtmlSanitizeEx.Scrubber.Meta. You can then use various macros provided by the Meta module to declaratively define which tags, attributes, and URI schemes are allowed. The StripTags scrubber pattern is the recommended starting point for building custom rules.

    defmodule MyStripTags do
      require HtmlSanitizeEx.Scrubber.Meta
      alias HtmlSanitizeEx.Scrubber.Meta
    
      # Define rules using Meta macros
      Meta.allow_tag_with_uri_attributes "img", ["src"], ["http", "https"]
      Meta.allow_tag_with_these_attributes "img", ["width", "height"]
    
      # Finalize the scrubber
      Meta.strip_comments
    end
  6. Use built-in HTML scrubbers

    master

    The library provides several pre-defined scrubbers for common use cases. You can call these functions directly to sanitize HTML strings.

    Available built-in scrubbers:

    • HtmlSanitizeEx.basic_html(html): Allows certain basic HTML elements to remain.
    • HtmlSanitizeEx.html5(html): Follows HTML5 standards.
    • HtmlSanitizeEx.markdown_html(html): Optimized for HTML generated from Markdown.
    • HtmlSanitizeEx.strip_tags(html): Completely strips all HTML tags, leaving only the text content.
    • HtmlSanitizeEx.noscrub(html): Primarily used for testing; performs no scrubbing.
    # Strip all tags
    text = "<a href=\"javascript:alert('XSS');\">text here</a>"
    HtmlSanitizeEx.strip_tags(text)
    # => "text here"
    
    # Allow basic HTML
    text = "<h1>Hello <script>World!</script></h1>"
    HtmlSanitizeEx.basic_html(text)
    # => "<h1>Hello World!</h1>"
  7. Check if a URI has a valid scheme with valid_schema?/2

    master

    The valid_schema?/2 function determines if a given URI string uses an allowed scheme.

    Behavior:

    • If the URI contains a protocol separator (like :// or encoded variants), it extracts the scheme and checks if it exists in the valid_schemes list.
    • If no protocol separator is detected, the URI is considered valid by default.

    Arguments:

    • uri: The URI string to validate.
    • valid_schemes: A list of allowed scheme strings (e.g., `[
  8. Validate URI schemes with HtmlSanitizeEx.Scrubber.URI

    master

    The HtmlSanitizeEx.Scrubber.URI module provides logic to validate the schemes (e.g., http, https, mailto) of URIs within HTML attributes. This is used to ensure that links or other URI-based attributes only use allowed protocols.

    scrub_attribute/3

    Used to process an attribute and its URI value.

    Arguments:

    • tag: The HTML tag name.
    • {attr_name, uri}: A tuple containing the attribute name and the URI string.
    • valid_schemes: A list of allowed scheme strings (e.g., `[
  9. Scrub CSS with HtmlSanitizeEx.Scrubber.CSS

    master

    The HtmlSanitizeEx.Scrubber.CSS module provides functionality to sanitize CSS text by removing dangerous elements and validating properties and values against an allowlist.

    When scrub/1 is called on a binary string, it performs the following operations:

    1. Decodes escaped characters.
    2. Removes comment markers (/*, */, <!--, -->).
    3. Removes @import statements.
    4. Validates CSS property-value pairs against an allowlist of properties and values.

    Allowed properties include common layout and typography properties (e.g., color, font-size, margin, padding, width, height) and their sub-properties (e.g., background-*, border-*).

    Values are validated to ensure they are either recognized keywords (e.g., auto, bold, transparent, red) or valid measured units (e.g., hex colors, rgb(), px, em, %). If a value contains suspicious characters like \ or &, it is cleared.

    # Example usage of the CSS scrubber
    css_input = "color: red; font-size: 16px; @import 'malicious.css';"
    scrubbed = HtmlSanitizeEx.Scrubber.CSS.scrub(css_input)
    # Result: "color: red; font-size: 16px;"