fix(escape): escape `<` and `>` in attribute values

YB
8d ago
+14-2

escapeAttribute (and encode(input, { mode: EncodingMode.Attribute })) links to the HTML spec's escaping a string algorithm, but it only escapes &, " and U+00A0. Since whatwg/html#6362 (merged May 2025), that algorithm replaces < with &lt; and > with &gt; in attribute mode as well as in text mode. Browsers already serialize attributes this way. Headless Chromium 153 gives <p title="a&lt;b&gt;"> for p.setAttribute("title", "a<b>").

This adds < and > to attributeEscapeRegex. getEscape already maps both characters, so nothing else needs to change.

escapeAttribute('<img src=x onerror=alert(1)>');
// before: '<img src=x onerror=alert(1)>'
// after:  '&lt;img src=x onerror=alert(1)&gt;'

Tests

  • Updated the existing escapeAttribute expectation in src/escape.spec.ts, which matched the old spec text, and added a case that covers both escapeAttribute and encode with EncodingMode.Attribute. The new and updated cases fail without the source change and pass with it.
  • npm test: vitest passed 5141/5141. lint:es reports 2 jsdoc warnings, and the base branch has the same 2. lint:ts is clean. Locally, biome check . skipped every file because of the checkout's location, so it was run directly on the two changed files, which pass.
  • As an extra check outside the repo, 400 random attribute values made from " & ' < > U+00A0, space, tab, =, ` and / were run through escapeAttribute and compared with headless Chromium's outerHTML. With this change all 400 match. Before it, 187 differ.

Downstream note

This changes output for consumers. parse5's test/data/serialization/tests.json has an "Attribute value escaping - < and >" case that expects the old data-foo="<span>" output. With this build of entities patched into parse5's node_modules, that is the only parse5 test that fails, and with the expectation updated to &lt;span&gt; the whole parse5 suite passes (19329/19329). I'm happy to open that parse5 change once this is released, and I'll leave the choice between a minor and a major version to you.

Summary by CodeRabbit

  • Bug Fixes
    • HTML attribute escaping now converts < and > to safe HTML entities while preserving other attribute content.