module Components::Elements::UrlAttributeValidation

Overview

SafeHTML v1 (docs/SAFE_HTML_V1.md ยง3.2): a construction/render-time backstop for the handful of concrete elements whose whole reason for existing is a URL-bearing attribute โ€” A#href, Img#src, and similar. Before this module, href/src reached these elements as a plain, unvalidated String through two paths: the constructor kwargs (A.new(href: "javascript:...")) and the ordinary #set_attribute("href", "javascript:...") call โ€” both of which HTML.escape-based attribute escaping does nothing to neutralize, since javascript:/vbscript:/data: survive attribute-value escaping unchanged (escaping quotes/</> doesn't touch the scheme).

include-ing this module and overriding url_bearing_attribute? for the one or two attribute names a tag actually has closes both paths at once: the constructor kwargs path already funnels through HTMLElement#set_attribute (see HTMLElement#initialize), so overriding #set_attribute here covers it for free.

This deliberately does not touch HTMLElement#set_attribute itself โ€” the shared, generic, String-typed setter every one of the ~94 element classes has, including the ~185 call sites in src/ui/renderers/web_renderer.cr that set data-*/style/ARIA attributes on elements that have nothing to do with URLs. Only classes that include UrlAttributeValidation (the actual link/src-bearing tags) get the extra check; every other element and every other attribute name is unaffected, matching docs/SAFE_HTML_V1.md's documented two-tier scope.

The already-typed HTMLElement#set_safe_url_attribute(name, SafeURL) path is unaffected by (and redundant with, in a good way) this module: it calls #set_attribute(name, url.to_s) with a String that has already passed SafeURL.parse!, so it passes this check trivially.

Direct including types

Defined in:

components/elements/base/url_attribute_validation.cr

Instance Method Summary

Instance Method Detail

def set_attribute(name : String, value : String | Nil) : self #

[View source]