URL and CSS value sanitization for Tiptapex.Renderer.
These allow-lists are the core XSS defense: link/image/video URLs must be http(s), mailto, or relative; iframes are only ever emitted for canonical YouTube embed URLs rebuilt from a validated video id; CSS values must match a strict grammar that cannot break out of a style attribute.
Summary
Functions
Coerces a width attribute to integer pixels.
Validates a CSS value for inline style emission (colors, font sizes,
font families, line heights). The grammar admits no ;, braces, quotes,
url(, or escapes, so a value can never break out of its declaration.
Validates a URL for an img src.
Validates a user-supplied URL for href/src attributes.
Validates a text-align value.
Normalizes any of the common YouTube URL shapes to the canonical embed
URL, validating the video id. This is the ONLY URL ever placed in an
iframe src; anything that doesn't resolve here drops the whole node.
Functions
@spec int_width(term()) :: {:ok, pos_integer()} | :error
Coerces a width attribute to integer pixels.
Validates a CSS value for inline style emission (colors, font sizes,
font families, line heights). The grammar admits no ;, braces, quotes,
url(, or escapes, so a value can never break out of its declaration.
Validates a URL for an img src.
Everything safe_url/1 accepts, plus data: URIs for images — which
safe_url/1 rejects (a data: link is a navigation vector, but a data:
image is inert, and it is the only src Chrome can resolve inside a PDF
running header). SVG is included: an SVG loaded through <img> cannot run
scripts, which is the only way these are ever rendered.
Validates a user-supplied URL for href/src attributes.
Accepts absolute http/https/mailto URLs, protocol-relative URLs, and
relative paths/fragments. Rejects everything else — notably
javascript:, vbscript:, and data: — plus anything containing
control characters.
Validates a text-align value.
Normalizes any of the common YouTube URL shapes to the canonical embed
URL, validating the video id. This is the ONLY URL ever placed in an
iframe src; anything that doesn't resolve here drops the whole node.
Mirrors toYoutubeEmbed/1 in assets/js/tiptapex/extensions/video.js,
but rebuilds the URL from the extracted id instead of passing the
original through.