Skip to content

fix: never interpolate item.icon into markup - #814

Merged
bbrala merged 2 commits into
masterfrom
fix/issue-810-icon-html-injection
Jul 30, 2026
Merged

fix: never interpolate item.icon into markup#814
bbrala merged 2 commits into
masterfrom
fix/issue-810-icon-html-injection

Conversation

@bbrala

@bbrala bbrala commented Jul 29, 2026

Copy link
Copy Markdown
Member

Problem

The two Font Awesome branches built the icon element by concatenating item.icon into a markup string:

item._icon = $('<i class="' + item.icon + '"></i>');
item._icon = $('<i class="fa ' + item.icon + '"></i>');

An icon value containing " or > closes the class attribute and injects arbitrary markup, which jQuery then parses and builds. That is a real concern for menus whose icon names come from stored or otherwise non-literal data. I confirmed it: an onerror payload smuggled through item.icon actually executes on current master.

Fix

Build the element and set the class without parsing:

item._icon = $('<i></i>').addClass(item.icon);
item._icon = $('<i></i>').addClass('fa').addClass(item.icon);

A hostile value now just lands as a set of nonsense class names on the <i>, which renders nothing.

I swept the rest of the item construction path for the same pattern. Every other $('<...>') call in src/ is a literal tag with no interpolation, so these two were the only occurrences.

Backwards compatibility

Both changed lines sit behind guards that narrow the input a long way before they are reached: if (item.icon) excludes every falsy value, and each branch additionally requires typeof item.icon === 'string' and a leading fab /fas /fad /far /fal or fa-. So null, undefined, '', 0, and any non-string can never reach the changed code. They keep falling through to the untouched built-in icon-font branch exactly as before.

For the values that can reach it, addClass and concatenation produce the same class list:

  • multiple space-separated classes: same classes, same order
  • trailing whitespace: concatenation used to put the trailing space into the attribute, addClass drops it. The browser's own class parsing discarded it anyway, so the resulting class list, every CSS rule, and every hasClass call are identical
  • repeated inner whitespace: same, collapses to the same class list

Nothing else moves: same <i> tag, still prepended as the first child, same context-menu-icon and context-menu-icon--fa5 hooks on the item. No rendered class name, DOM structure, or CSS hook changes, so anything users style against is untouched. item.icon still accepts the same things it did, and the function and object forms are not touched at all. Only addClass, which has been in jQuery since 1.0, is used, so jQuery 1.12+ support is intact (CI covers jQuery 1, 2, 3 and 4).

I verified this empirically rather than by inspection: the whole "built exactly as before" test module passes against the pre-fix source as well as the fixed one. Only the three injection tests flip.

One intentional behaviour change, for the release notes: an item.icon that deliberately smuggles extra markup into the icon element no longer does so. That has been possible for years, so someone may be relying on it. It is the bug being fixed, so the fix is not contorted to preserve it, but it is the one thing that changes for existing callers. Anyone doing that should use the function form of item.icon, which can return an arbitrary element and is the supported way to do it.

Tests

New test/unit/issue-810-icon-html-injection.test.js, mirroring the structure of the #731 regression tests:

  • the FA5 and legacy fa- branches cannot break out of the class attribute
  • the payload ends up as inert class names, and the <i> gets no child elements
  • a "supported icon inputs keep working" module covering both Font Awesome branches, the built-in icon font, an icon function returning a class string, and an icon function returning an element
  • a "built exactly as before" module pinning the rendered class list for multiple classes, trailing whitespace, repeated inner whitespace, a non-string icon, and a falsy icon

The three injection tests fail on master (the onerror handler runs) and pass with the fix; everything else passes on both. Full unit suite: 95/95. Acceptance suite: 36/36, including the existing menu-title-icon-alignment specs.

Closes #810

bbrala added 2 commits July 29, 2026 21:13
The Font Awesome icon branches built their <i> element by concatenating
item.icon into a markup string, so an icon name containing a quote or an
angle bracket closed the class attribute and injected arbitrary markup.
Build the element and set the class instead, which is behaviourally
identical for every legitimate icon name.

Fixes #810
Documents that building the <i> with addClass produces exactly the same
class list as the old markup concatenation for every legitimate icon
value: multiple classes, trailing whitespace, repeated inner whitespace,
and the non-string and falsy values that never reach the changed
branches at all. These all pass against the pre-fix source too.
@bbrala
bbrala merged commit 382f6da into master Jul 30, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

item.icon is interpolated into markup on the Font Awesome paths

1 participant