Safelist Reference

The built-in sanitizers decide what to keep using the constants in Loofah::HTML5::SafeList (defined in lib/loofah/html5/safelist.rb). The lists were originally lifted from the html5lib sanitizer and have been extended over time with HTML5 elements, ARIA attributes, CSS properties, and SVG rules. This page summarizes every list, how they combine, and what that means for your content.

How the Lists Combine

The sanitizers do not read the ACCEPTABLE_* lists directly. They use these combined sets:

Combined constant Built from
ALLOWED_ELEMENTS ACCEPTABLE_ELEMENTS + MATHML_ELEMENTS + SVG_ELEMENTS
ALLOWED_ELEMENTS_WITH_LIBXML2 ALLOWED_ELEMENTS + TAGS_SAFE_WITH_LIBXML2 (html, head, body)
ALLOWED_ATTRIBUTES ACCEPTABLE_ATTRIBUTES + MATHML_ATTRIBUTES + SVG_ATTRIBUTES + ARIA_ATTRIBUTES
ALLOWED_CSS_PROPERTIES ACCEPTABLE_CSS_PROPERTIES
ALLOWED_CSS_KEYWORDS ACCEPTABLE_CSS_KEYWORDS + ACCEPTABLE_CSS_COLORS + ACCEPTABLE_CSS_EXTENDED_COLORS
ALLOWED_CSS_FUNCTIONS ACCEPTABLE_CSS_FUNCTIONS
ALLOWED_SVG_PROPERTIES ACCEPTABLE_SVG_PROPERTIES
ALLOWED_PROTOCOLS ACCEPTABLE_PROTOCOLS
ALLOWED_URI_DATA_MEDIATYPES ACCEPTABLE_URI_DATA_MEDIATYPES

The element check (Loofah::HTML5::Scrub.allowed_element?) uses ALLOWED_ELEMENTS_WITH_LIBXML2.

Attributes are not checked per element: an attribute that is allowed anywhere is allowed on every allowed element. For example href (an HTML and MathML attribute) is accepted on a <div> too, subject to the URI check.

Loofah::HTML5::WhiteList is a deprecated alias of SafeList.

Elements

List Contents
ACCEPTABLE_ELEMENTS a, abbr, acronym, address, area, article, aside, audio, b, bdi, bdo, big, blockquote, br, button, canvas, caption, center, cite, code, col, colgroup, command, datalist, dd, del, details, dfn, dir, div, dl, dt, em, fieldset, figcaption, figure, font, footer, form, h1–h6, header, hr, i, img, input, ins, kbd, label, legend, li, main, map, mark, menu, meter, nav, ol, optgroup, option, output, p, pre, q, s, samp, section, select, small, span, strike, strong, sub, summary, sup, table, tbody, td, textarea, tfoot, th, thead, time, tr, tt, u, ul, var, video, wbr
MATHML_ELEMENTS annotation, annotation-xml, maction, math, menclose, merror, mfenced, mfrac, mi, mmultiscripts, mn, mo, mover, mpadded, mphantom, mprescripts, mroot, mrow, ms, mspace, msqrt, mstyle, msub, msubsup, msup, mtable, mtd, mtext, mtr, munder, munderover, none, semantics
SVG_ELEMENTS a, altGlyph, animate, animateColor, animateMotion, animateTransform, circle, clipPath, cursor, defs, desc, ellipse, feGaussianBlur, feImage, filter, font-face, font-face-name, font-face-src, foreignObject, g, glyph, hkern, line, linearGradient, marker, mask, metadata, missing-glyph, mpath, path, pattern, polygon, polyline, radialGradient, rect, set, stop, svg, switch, symbol, text, textPath, title, tref, tspan, use
TAGS_SAFE_WITH_LIBXML2 body, head, html

Notable omissions: script, style, iframe, frame, frameset, object, embed, applet, link, meta, base, noscript, template, source, track, picture, and dialog are not on any list. Because audio and video are allowed but source is not, media must use the src attribute directly.

Element names are compared as the parser reports them, without regard to namespace. An SVG title and an HTML title therefore both pass.

Attributes

List Contents
ACCEPTABLE_ATTRIBUTES abbr, accept, accept-charset, accesskey, action, align, alt, axis, border, cellpadding, cellspacing, char, charoff, charset, checked, cite, class, clear, color, cols, colspan, compact, contenteditable, coords, datetime, dir, disabled, enctype, for, frame, headers, height, href, hreflang, hspace, id, ismap, label, lang, longdesc, loop, loopcount, loopend, loopstart, maxlength, media, method, multiple, name, nohref, noshade, nowrap, poster, preload, prompt, readonly, rel, rev, rows, rowspan, rules, scope, selected, shape, size, span, src, start, style, summary, tabindex, target, title, type, usemap, valign, value, vspace, width, xml:lang
MATHML_ATTRIBUTES Presentation attributes such as mathvariant, mathcolor, displaystyle, columnalign, rowspan, plus href, xlink:href, xlink:show, xlink:type, xmlns, xmlns:xlink
SVG_ATTRIBUTES Geometry and presentation attributes such as d, points, x, y, cx, cy, r, viewBox, transform, fill, stroke, opacity, attributeName, begin, dur, plus xlink:href, xml:base, xml:space, xmlns
ARIA_ATTRIBUTES role and the aria-* attributes from WAI-ARIA, for example aria-label, aria-labelledby, aria-describedby, aria-description, aria-hidden, aria-expanded, aria-live

See the source file for the complete MathML and SVG attribute lists.

Not allowed: every event handler (onclick, onerror, onload, ...), srcdoc, formaction, srcset, and sandbox. The SVG animation attributes from, to, by, and values are deliberately missing too, because they can set an href to a javascript: URI (CVE-2018-16468 and CVE-2019-15587, covered by the test suite).

Always-allowed data attributes

Any attribute whose name matches /\Adata-[\w-]+\z/ (for example data-id or data-user-name) is kept without any check of its value. It is also exempt from the empty-value cleanup. If your JavaScript reads data-* attributes and inserts them into the page as HTML or URLs, sanitize those values yourself.

Empty attributes are removed

After the other checks, any remaining attribute (other than data-*) whose value is empty or only whitespace is removed. This includes style attributes that became empty because every declaration was rejected.

URI-Valued Attributes

The values of these attributes (ATTR_VAL_IS_URI) must pass allowed_uri?, or the attribute is removed:

action, cite, href, longdesc, poster, preload, src, xlink:href, xml:base

Removing the attribute leaves the element in place. <a href="javascript:alert(1)">x</a> becomes <a>x</a>.

SVG Reference Rules

Two extra rules prevent SVG from pulling in external resources:

  1. Reference-taking properties (SVG_ATTR_VAL_ALLOWS_REF): clip-path, color-profile, cursor, fill, filter, marker, marker-end, marker-mid, marker-start, mask, stroke. Their values are tokenized as CSS; url(...) tokens are kept only when they point inside the document (url(#id)), and identifiers, hashes, and strings are kept. Everything else is dropped from the value.
  2. Local-only href (SVG_ALLOW_LOCAL_HREF): on altGlyph, animate, animateColor, animateMotion, animateTransform, cursor, feImage, filter, linearGradient, pattern, radialGradient, set, textpath, tref, and use, an href or xlink:href (SVG_HREF_ATTRIBUTES) whose value does not start with # (after optional whitespace) is removed.

Version 2.25.2 extended rule 2 to the plain SVG 2 href attribute; previously only xlink:href was restricted.

Protocols and data: Media Types

ACCEPTABLE_PROTOCOLS lists the URI schemes allowed in URI-valued attributes:

afs, aim, callto, data, ed2k, fax, ftp, gopher, http, https, irc, line, mailto, modem, news, nntp, rsync, rtsp, sftp, sms, ssh, tag, tel, telnet, urn, webcal, xmpp

javascript, vbscript, file, and anything else not listed are rejected. feed was removed in 2.25.2.

For data: URIs, the media type must be one of ACCEPTABLE_URI_DATA_MEDIATYPES:

image/gif, image/jpeg, image/png, text/css, text/plain

data:image/svg+xml and data:text/html are rejected. The parsing rules are on the URI and CSS page.

PROTOCOL_SEPARATOR is the regular expression that recognizes the colon after a scheme, including its encoded forms: /:|(&#0;*58)|(&#x0;*3a)|(%|&#37;)3A/i.

CSS Lists

List Purpose
ACCEPTABLE_CSS_PROPERTIES Properties allowed in style: layout and text properties such as display, color, background-color, font, font-family, font-size, font-weight, text-align, text-decoration, width, height, min-width, max-width, min-height, max-height, float, clear, overflow, vertical-align, white-space, line-height, flexbox properties (flex, flex-direction, justify-content, align-items, ...), aspect-ratio, cursor, list-style, plus aural properties inherited from html5lib
SHORTHAND_CSS_PROPERTIES background, border, margin, padding: any property whose name starts with one of these followed by - (or equals it) is allowed, with restricted keyword values
ACCEPTABLE_SVG_PROPERTIES fill, fill-opacity, fill-rule, stroke, stroke-width, stroke-linecap, stroke-linejoin, stroke-opacity
ACCEPTABLE_CSS_KEYWORDS !important, auto, block, bold, both, bottom, center, collapse, dashed, dotted, double, groove, hidden, inherit, initial, inset, italic, left, medium, none, normal, nowrap, outset, pointer, revert, ridge, right, separate, solid, thick, thin, top, transparent, underline, unset
ACCEPTABLE_CSS_COLORS, ACCEPTABLE_CSS_EXTENDED_COLORS Named colors (the 16 HTML4 colors and the extended CSS color keywords such as lightblue)
ACCEPTABLE_CSS_FUNCTIONS attr, blur, brightness, calc, circle, contrast, counter, counters, cubic-bezier, drop-shadow, ellipse, grayscale, hsl, hsla, hue-rotate, hwb, inset, invert, linear-gradient, matrix, matrix3d, opacity, perspective, polygon, radial-gradient, repeating-linear-gradient, repeating-radial-gradient, rgb, rgba, rotate, rotate3d, rotateX, rotateY, rotateZ, saturate, sepia, scale, scale3d, scaleX, scaleY, scaleZ, skew, skewX, skewY, symbols, translate, translate3d, translateX, translateY, translateZ (deliberately without url and image)

Notable properties that are not allowed: position, top, left, right, bottom, z-index, transform, opacity, visibility, content, grid properties, and gap. How values are checked is described in URI and CSS Sanitization.

Case Handling

When the library loads, Loofah::MetaHelpers.add_downcased_set_members_to_all_set_constants adds a lower-cased copy of every entry to each Set constant in SafeList (and in Loofah::Elements). So animateColor and animatecolor, or viewBox and viewbox, are both present. This matters because the HTML parsers may report SVG names in either case.

Customizing the Safelist

Loofah offers no configuration API for the safelist. The constants are ordinary, unfrozen Sets that the scrubbing code reads at run time, so mutating one (for example adding an element to ALLOWED_ELEMENTS_WITH_LIBXML2) changes behavior for the entire process, including any other library that uses Loofah (such as rails-html-sanitizer). This is not a documented extension point, and a future release could change it.

Safer alternatives:

  • Convert or remove elements with a custom scrubber before or after the built-in sanitizer.
  • Write a complete custom sanitizer as a Loofah::Scrubber subclass that calls Loofah::HTML5::Scrub.scrub_attributes(node) for elements you accept.
  • Use rails-html-sanitizer, which builds on Loofah and supports per-call allowed tags and attributes.

The repository includes a generate_safelists Rake task for maintainers; see Contributing.

See Also