#@sanitize

    Policy-driven DOM sanitization. The library ships a conservative default policy that matches the upstream JustHTML defaults, plus a builder API for constructing custom policies covering tag/attribute allowlists, URL schemes, CSS properties, and content-removal modes.

    The examples below are mbt check blocks and run as part of moon test sanitize.

    #The default policy

    default_sanitization_policy() is the right starting point for anything resembling user-submitted HTML. It strips disallowed elements (unwrapping them so their text survives), removes unsafe attributes, drops javascript: URLs, and removes comments.

    ///|
    test "readme sanitize default policy" {
    let frag = @dom.fragment(children=[
    @dom.element(
    "a",
    attrs={ "href": Some("javascript:alert(1)"), "onclick": Some("x") },
    children=[@dom.text("bad link")],
    ),
    @dom.element("script", children=[@dom.text("evil")]),
    @dom.element("p", children=[@dom.text("kept")]),
    ])
    let clean = @sanitize.sanitize_dom(frag)
    inspect(
    @ser.to_html(clean, pretty=false),
    content=(
    #|<a>bad link</a><p>kept</p>
    ),
    )
    }

    #Building a custom policy

    SanitizationPolicy::SanitizationPolicy takes the allowed tag list and a host of keyword arguments controlling tag/attribute handling, URL policy, CSS allowlist, comment/doctype handling, and unsafe-construct reporting.

    ///|
    test "readme sanitize custom policy" {
    let policy = @sanitize.SanitizationPolicy(
    ["p", "a"],
    allowed_attributes={ "a": ["href"] },
    disallowed_tag_handling=Drop,
    )
    let frag = @dom.fragment(children=[
    @dom.element("p", children=[@dom.text("keep")]),
    @dom.element(
    "a",
    attrs={ "href": Some("/x"), "onclick": Some("alert(1)") },
    children=[@dom.text("link")],
    ),
    @dom.element("script", children=[@dom.text("evil")]),
    ])
    let clean = @sanitize.sanitize_dom(frag, policy~)
    inspect(
    @ser.to_html(clean, pretty=false),
    content=(
    #|<p>keep</p><a href="/x">link</a>
    ),
    )
    }

    #Disallowed-tag handling

    Three modes:

    ///|
    pub(all) enum DisallowedTagHandling {
    Unwrap // remove tag, keep children (default)
    Drop // remove tag and children
    Escape // serialize the tag to HTML-escaped text
    }

    #Reporting modes

    UnsafeHandling controls what the sanitizer does when it refuses content: silently strip, collect diagnostics on the policy for later inspection, or raise immediately.

    ///|
    test "readme sanitize unsafe handling collect" {
    let policy = @sanitize.SanitizationPolicy(
    ["p"],
    disallowed_tag_handling=Drop,
    unsafe_handling=Collect,
    )
    let _ = @sanitize.sanitize_dom(
    @dom.fragment(children=[
    @dom.element("script", children=[@dom.text("evil")]),
    ]),
    policy~,
    )
    // collected_security_errors() returns a list of ParseError records
    // describing each refusal. Use this to surface a warning UI.
    debug_inspect(
    policy.collected_security_errors().length() > 0,
    content=(
    #|true
    ),
    )
    }

    #URL policy

    By default no URL attribute survives — to allow href="/x" or src="https://example.com/img.png", register UrlPolicyRules under UrlPolicy(allow_rules=...).

    ///|

    ///|
    test "readme sanitize url policy" {
    let url_policy = @sanitize.UrlPolicy(allow_rules=[
    UrlPolicyRule(
    "a",
    "href",
    UrlRule(allowed_schemes=["https"], allowed_hosts=["example.com"]),
    ),
    ])
    let policy = @sanitize.SanitizationPolicy(
    ["a"],
    allowed_attributes={ "a": ["href"] },
    url_policy~,
    )
    let frag = @dom.fragment(children=[
    @dom.element("a", attrs={ "href": Some("https://example.com/x") }, children=[
    @dom.text("ok"),
    ]),
    @dom.element("a", attrs={ "href": Some("http://other.example/x") }, children=[
    @dom.text("blocked"),
    ]),
    ])
    let clean = @sanitize.sanitize_dom(frag, policy~)
    // Only the example.com / https link kept its href; the other one
    // had its href stripped but the anchor element survived.
    inspect(
    @ser.to_html(clean, pretty=false),
    content=(
    #|<a href="https://example.com/x">ok</a><a>blocked</a>
    ),
    )
    }

    #Rewriting URLs with a filter

    UrlFilter runs after policy validation; return None to strip, Some(new_value) to rewrite.

    @sanitize.UrlFilter((tag, attr, value) =>
    if value.starts_with("https://old.example/") {
    Some(value.replace("old.example", "new.example"))
    } else {
    Some(value)
    })

    DisallowedTagHandling

    pub(all) enum DisallowedTagHandling {
    Unwrap
    Escape
    Drop
    } derive(Eq,
    Debug
    )

    How sanitizer handles elements whose tag names are not allowlisted.

    DisallowedTagHandling::equal

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn DisallowedTagHandling::equal(DisallowedTagHandling, DisallowedTagHandling) -> Bool

    DisallowedTagHandling::not_equal

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn DisallowedTagHandling::not_equal(x : DisallowedTagHandling, y : DisallowedTagHandling) -> Bool

    DisallowedTagHandling::to_repr

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn DisallowedTagHandling::to_repr(DisallowedTagHandling) ->
    Repr

    SanitizationPolicy

    pub struct SanitizationPolicy {
    // private fields
    } derive(
    Debug
    )

    DOM sanitization policy.

    A policy controls allowed tags and attributes, URL filtering, comment and doctype handling, foreign-content hardening, CSS style allowlists, selector limits used by transform hooks, and unsafe-input reporting.

    SanitizationPolicy::SanitizationPolicy

    fn SanitizationPolicy::SanitizationPolicy(allowed_tags : Array[String], allowed_attributes? : Map[String, Array[String]], url_policy? : UrlPolicy, drop_comments? : Bool, drop_doctype? : Bool, drop_foreign_namespaces? : Bool, drop_content_tags? : Array[String], disallowed_tag_handling? : DisallowedTagHandling, force_link_rel? : Array[String], allowed_css_properties? : Array[String], strip_invisible_unicode? : Bool, selector_limits? :
    SelectorLimits
    , unsafe_handling? : UnsafeHandling) -> SanitizationPolicy

    Construct a DOM sanitization policy.

    Tag and attribute names are normalized to lowercase. By default comments, doctypes, foreign namespaces, script/style content, and invisible Unicode controls are removed. Unsafe findings are stripped unless unsafe_handling is set to Raise or Collect.

    SanitizationPolicy::collected_security_errors

    Return a copy of unsafe findings accumulated in Collect mode.

    SanitizationPolicy::has_url_rule

    fn SanitizationPolicy::has_url_rule(self : SanitizationPolicy, tag_name : String, attr_name : String) -> Bool

    Return whether this policy has an exact URL rule for a tag and attribute.

    tag_name and attr_name should be normalized lowercase names. The default URL handling is not considered a rule; this checks only explicit tag/attribute bindings.

    SanitizationPolicy::reset_collected_security_errors

    fn SanitizationPolicy::reset_collected_security_errors(self : SanitizationPolicy) -> Unit

    Clear unsafe findings accumulated in Collect mode.

    SanitizationPolicy::sanitize_attribute_value

    fn SanitizationPolicy::sanitize_attribute_value(self : SanitizationPolicy, tag_name : String, attr_name : String, value : String, effectively_foreign? : Bool) -> String?

    Sanitize a single attribute value with this policy.

    tag_name and attr_name should already be normalized to lowercase. URL attributes, URL lists, foreign SVG-like URL function attributes, and inline style values are validated through the policy. Returns None when the attribute value should be dropped.

    SanitizationPolicy::sanitize_inline_style_value

    fn SanitizationPolicy::sanitize_inline_style_value(self : SanitizationPolicy, tag_name : String, value : String) -> String?

    Sanitize the value of an inline style attribute.

    Only declarations whose property names are in allowed_css_properties are kept. Declarations that may load external resources are kept only when their url(...) values pass the policy's URL rules. Returns None when no safe declaration remains.

    SanitizationPolicy::selector_limits

    Return the selector limits used by this policy.

    Sanitizer transforms use these limits when selector-based hooks are evaluated during sanitization.

    SanitizationPolicy::to_repr

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn SanitizationPolicy::to_repr(SanitizationPolicy) ->
    Repr

    SanitizationPolicy::with_extra_allowed_tags

    fn SanitizationPolicy::with_extra_allowed_tags(self : SanitizationPolicy, extra_tags : Array[String]) -> SanitizationPolicy

    Return a copy of this policy with additional allowed tag names.

    Extra tags are trimmed, ASCII-lowercased, deduplicated, and merged with the existing allowlist. Other policy settings, including URL rules and unsafe handling, are preserved.

    SanitizeTransformObserver

    pub struct SanitizeTransformObserver {
    // private fields
    }

    Observer callbacks for sanitizer-driven DOM rewrites.

    The node hook runs for events that have an associated DOM node. The report callback receives every sanitizer event message and the optional related node, including unsafe input that was stripped or collected.

    SanitizeTransformObserver::SanitizeTransformObserver

    fn SanitizeTransformObserver::SanitizeTransformObserver(hook : (
    Node
    ) -> Unit?, report_callback : (String,
    Node
    ?) -> Unit?) -> SanitizeTransformObserver

    Construct sanitizer observer callbacks.

    Pass None for either callback to observe only reports or only affected nodes. Both callbacks are best-effort notifications; they do not change the sanitizer decision.

    UnsafeHandling

    pub(all) enum UnsafeHandling {
    Strip
    Raise
    Collect
    } derive(Eq,
    Debug
    )

    How sanitizer reports unsafe input that it strips or rewrites.

    UnsafeHandling::equal

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn UnsafeHandling::equal(UnsafeHandling, UnsafeHandling) -> Bool

    UnsafeHandling::not_equal

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn UnsafeHandling::not_equal(x : UnsafeHandling, y : UnsafeHandling) -> Bool

    UnsafeHandling::to_repr

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn UnsafeHandling::to_repr(UnsafeHandling) ->
    Repr

    UrlFilter

    pub struct UrlFilter {
    // private fields
    }

    Callback wrapper used to rewrite or reject URL values before validation.

    The callback receives normalized tag name, normalized attribute name, and the raw attribute value. Returning None drops the URL.

    UrlFilter::UrlFilter

    fn UrlFilter::UrlFilter(callback : (String, String, String) -> String?) -> UrlFilter

    Construct a URL filter from a callback.

    UrlFilter::to_repr

    UrlHandling

    pub(all) enum UrlHandling {
    UrlAllow
    UrlStrip
    UrlProxy
    } derive(Eq,
    Debug
    )

    Action used after a URL value passes the configured URL checks.

    UrlHandling::equal

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn UrlHandling::equal(UrlHandling, UrlHandling) -> Bool

    UrlHandling::not_equal

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn UrlHandling::not_equal(x : UrlHandling, y : UrlHandling) -> Bool

    UrlHandling::to_repr

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn UrlHandling::to_repr(UrlHandling) ->
    Repr

    UrlPolicy

    pub struct UrlPolicy {
    // private fields
    } derive(
    Debug
    )

    URL sanitization policy shared by URL-bearing attributes.

    Exact (tag, attr) rules take precedence. Unmatched URL-like attributes use the default handling and relative-URL behavior.

    UrlPolicy::UrlPolicy

    fn UrlPolicy::UrlPolicy(default_handling? : UrlHandling, default_allow_relative? : Bool, allow_rules? : Array[UrlPolicyRule], proxy? : UrlProxy, url_filter? : UrlFilter) -> UrlPolicy

    Construct a URL policy.

    allow_rules are exact tag/attribute bindings. url_filter runs before rule validation and can rewrite or drop the URL value.

    UrlPolicy::to_repr

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn UrlPolicy::to_repr(UrlPolicy) ->
    Repr

    UrlPolicyRule

    pub struct UrlPolicyRule {
    // private fields
    } derive(
    Debug
    )

    URL rule bound to a tag and attribute name.

    UrlPolicyRule::UrlPolicyRule

    fn UrlPolicyRule::UrlPolicyRule(tag : StringView, attr : StringView, rule : UrlRule) -> UrlPolicyRule

    Construct a URL policy rule for one tag and attribute.

    UrlPolicyRule::to_repr

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn UrlPolicyRule::to_repr(UrlPolicyRule) ->
    Repr

    UrlProxy

    pub struct UrlProxy {
    // private fields
    } derive(
    Debug
    )

    Proxy endpoint used when a URL rule selects UrlProxy.

    Sanitized URLs are emitted as url?param=<encoded-url> or url&param=<encoded-url> depending on whether the proxy URL already has a query string.

    UrlProxy::UrlProxy

    fn UrlProxy::UrlProxy(url : StringView, param? : String) -> UrlProxy

    Construct a URL proxy descriptor.

    UrlProxy::to_repr

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn UrlProxy::to_repr(UrlProxy) ->
    Repr

    UrlRule

    pub struct UrlRule {
    // private fields
    } derive(
    Debug
    )

    Per-attribute URL validation rule.

    Rules can restrict schemes and hosts, disallow fragments, normalize protocol-relative URLs, override relative-URL handling, or route accepted URLs through a proxy.

    UrlRule::UrlRule

    fn UrlRule::UrlRule(allowed_schemes? : Array[String], allowed_hosts? : Array[String], allow_fragment? : Bool, resolve_protocol_relative? : String?, handling? : UrlHandling, allow_relative? : Bool, proxy? : UrlProxy) -> UrlRule

    Construct a per-attribute URL validation rule.

    Schemes and hosts are normalized to lowercase. resolve_protocol_relative rewrites protocol-relative URLs such as //example.com before validation.

    UrlRule::to_repr

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn UrlRule::to_repr(UrlRule) ->
    Repr

    css_preset_text

    fn css_preset_text() -> Array[String]

    Return the conservative text-style CSS property allowlist.

    css_value_may_load_external_resource

    fn css_value_may_load_external_resource(value : StringView) -> Bool

    Conservatively detect CSS values that can load external resources.

    This returns true for imports, url(...), image-set-like functions, legacy browser extension hooks, and values that cannot be normalized safely because of escapes or malformed comments. It is a prefilter; URL-bearing declarations still need URL-policy validation before they are kept.

    default_document_sanitization_policy

    fn default_document_sanitization_policy() -> SanitizationPolicy

    Return the default document sanitizer policy.

    This extends the fragment policy with document shell tags and preserves the doctype.

    default_sanitization_policy

    fn default_sanitization_policy() -> SanitizationPolicy

    Return the default fragment sanitizer policy.

    is_foreign_url_function_like_attr

    fn is_foreign_url_function_like_attr(name : String) -> Bool

    Return whether a foreign-content attribute can load URLs through CSS syntax.

    These SVG-style presentation attributes are checked for resource-loading CSS functions when the node is effectively foreign.

    is_single_url_like_attr

    fn is_single_url_like_attr(name : String) -> Bool

    Return whether name is a single URL-bearing HTML attribute.

    Inputs should be normalized lowercase attribute names.

    is_space_separated_url_list_attr

    fn is_space_separated_url_list_attr(name : String) -> Bool

    Return whether name carries a space-separated URL list.

    Inputs should be normalized lowercase attribute names.

    is_srcset_like_attr

    fn is_srcset_like_attr(name : String) -> Bool

    Return whether name carries a comma-separated image candidate URL list.

    Inputs should be normalized lowercase attribute names.

    node_is_effectively_foreign

    fn node_is_effectively_foreign(node :
    Node
    ) -> Bool

    Return whether a node is treated as foreign-content for sanitization.

    A node is effectively foreign when it or an ancestor has a non-HTML namespace, or when it appears under an svg or math element name. This is used to harden URL-bearing attributes and active foreign-content elements.

    sanitize_dom

    Sanitize a DOM node in place and return the sanitized root.

    When no policy is supplied, document roots use the document policy and other roots use the fragment policy.

    sanitize_dom_with_observer

    Sanitize a DOM node in place with optional observer callbacks.

    When policy is None, document roots use the document policy and other roots use the fragment policy. The observer is notified about sanitizer rewrites and unsafe input reports. Non-document/non-fragment roots are sanitized through a temporary fragment wrapper; the returned node may therefore be the original node or a fragment containing surviving nodes.