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
- Elements
- Attributes
- URI-Valued Attributes
- SVG Reference Rules
- Protocols and data: Media Types
- CSS Lists
- Case Handling
- Customizing the Safelist
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:
- 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. - Local-only
href(SVG_ALLOW_LOCAL_HREF): onaltGlyph,animate,animateColor,animateMotion,animateTransform,cursor,feImage,filter,linearGradient,pattern,radialGradient,set,textpath,tref, anduse, anhreforxlink: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: /:|(�*58)|(�*3a)|(%|%)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::Scrubbersubclass that callsLoofah::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.