fix(escape): escape `<` and `>` in attribute values
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 < and > with > in attribute mode as well as in text mode. Browsers already serialize attributes this way. Headless Chromium 153 gives <p title="a<b>"> 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: '<img src=x onerror=alert(1)>'
Tests
- Updated the existing
escapeAttributeexpectation insrc/escape.spec.ts, which matched the old spec text, and added a case that covers bothescapeAttributeandencodewithEncodingMode.Attribute. The new and updated cases fail without the source change and pass with it. npm test: vitest passed 5141/5141.lint:esreports 2 jsdoc warnings, and the base branch has the same 2.lint:tsis 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 throughescapeAttributeand compared with headless Chromium'souterHTML. 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 <span> 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.
- HTML attribute escaping now converts