Skip to content

Escape "<" and ">" when serializing attribute values - #6362

Merged
annevk merged 1 commit into
mainfrom
bocoup/always-escape-lt-gt
May 20, 2025
Merged

annevk merged 1 commit into
mainfrom
bocoup/always-escape-lt-gt

Conversation

@zcorpan

@zcorpan zcorpan commented Feb 5, 2021 •

Copy link
Copy Markdown
Member

Avoid a class of XSS attacks where markup goes through
a lossy parse-serialize-parse roundtrip and the original
attribute value is parsed in the data state.

This reverts 4eeb8a1.

Fixes #6235.

(See WHATWG Working Mode: Changes for more details.)


/parsing.html ( diff )


/parsing.html ( diff )

@zcorpan
zcorpan force-pushed the bocoup/always-escape-lt-gt branch from 7dfae8b to ac056b2 Compare February 5, 2021 10:20
@mozfreddyb

Copy link
Copy Markdown
Contributor

@zcorpan Thank you for nudging this along! Did you spend any time thinking about compat analysis? Mostly curious, I've never done any compat analysis..

zcorpan added a commit to web-platform-tests/wpt that referenced this pull request Feb 5, 2021
@zcorpan

zcorpan commented Feb 5, 2021

Copy link
Copy Markdown
Member Author

@mozfreddyb I did think about it. I think static analysis is unlikely to find anything. What is possible is to add a use counter in Chromium (or equivalent in other engines) when the HTML serializer serializes an attribute value that contains "<" or ">". When the use counter reaches stable, the next HTTP Archive crawl will collect URLs from its data set of around 7 million pages, which can be queried with BigQuery. If there is an experimental implementation of this change, one can manually test those URLs and see if anything is obviously broken.

If this quickly finds multiple things that are broken, then it likely is not a web compatible change. If nothing broken is found, then it might be web compatible, but obviously this wouldn't be a guarantee. The lack of complaints during the dev and beta period is probably a more convincing signal that it is web compatible.

zcorpan added a commit to web-platform-tests/wpt that referenced this pull request Apr 3, 2025
evilpie pushed a commit to web-platform-tests/wpt that referenced this pull request Apr 16, 2025
moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this pull request Apr 23, 2025
…in attribute values, a=testonly

Automatic update from web-platform-tests
HTML: tentative test for serializing <> in attribute values

See whatwg/html#6362

--

wpt-commits: c5d4ba83796ad90ea175682d9e4477656fc5ddb5
wpt-pr: 51827
shtrom pushed a commit to mozilla-conduit/ff-test that referenced this pull request Apr 28, 2025
…in attribute values, a=testonly

Automatic update from web-platform-tests
HTML: tentative test for serializing <> in attribute values

See whatwg/html#6362

--

wpt-commits: c5d4ba83796ad90ea175682d9e4477656fc5ddb5
wpt-pr: 51827
@evilpie

evilpie commented Apr 29, 2025

Copy link
Copy Markdown
Member

@annevk annevk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably hold off until Chromium has actually deployed this?

webkit-commit-queue pushed a commit to annevk/WebKit that referenced this pull request May 20, 2025
https://bugs.webkit.org/show_bug.cgi?id=292432
rdar://150520333

Reviewed by Ryosuke Niwa and Sam Weinig.

Implement whatwg/html#6362 and add a preference
in case we find out we have to disable it in certain cases.

The preference does not impact the recently added getHTML() method on
Element and ShadowRoot as that should be new enough to not matter.

Add new test from: web-platform-tests/wpt#27501

Canonical link: https://commits.webkit.org/295149@main
@annevk

annevk commented May 20, 2025

Copy link
Copy Markdown
Member

Given that everyone is moving ahead with this and we still haven't seen breakage, let's flip the switch now. It's also rather awkward to keep the tests and such in limbo that long.

Avoid a class of XSS attacks where markup goes through
a lossy parse-serialize-parse roundtrip and the original
attribute value is parsed in the data state.

This reverts 4eeb8a1.

Fixes #6235.
@annevk
annevk force-pushed the bocoup/always-escape-lt-gt branch from ac056b2 to 1896a6a Compare May 20, 2025 09:07
annevk pushed a commit to web-platform-tests/wpt that referenced this pull request May 20, 2025
@annevk
annevk merged commit e21bd3b into main May 20, 2025
@annevk
annevk deleted the bocoup/always-escape-lt-gt branch May 20, 2025 09:11
Gingeh added a commit to Gingeh/ladybird that referenced this pull request May 22, 2025
AtkinsSJ pushed a commit to LadybirdBrowser/ladybird that referenced this pull request May 22, 2025
lando-worker Bot pushed a commit to mozilla-firefox/firefox that referenced this pull request May 23, 2025
…lues when serializing,

Automatic update from web-platform-tests
HTML: Escape "<" and ">" in attribute values when serializing

See whatwg/html#6362.
--

wpt-commits: 3fad71d22b6681a3430ff498add3ea8c31660401
wpt-pr: 27501

Differential Revision: https://phabricator.services.mozilla.com/D250932
moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this pull request May 24, 2025
…lues when serializing,

Automatic update from web-platform-tests
HTML: Escape "<" and ">" in attribute values when serializing

See whatwg/html#6362.
--

wpt-commits: 3fad71d22b6681a3430ff498add3ea8c31660401
wpt-pr: 27501

Differential Revision: https://phabricator.services.mozilla.com/D250932
gecko-dev-updater pushed a commit to marco-c/gecko-dev-wordified-and-comments-removed that referenced this pull request May 28, 2025
…lues when serializing,

Automatic update from web-platform-tests
HTML: Escape "<" and ">" in attribute values when serializing

See whatwg/html#6362.
--

wpt-commits: 3fad71d22b6681a3430ff498add3ea8c31660401
wpt-pr: 27501

Differential Revision: https://phabricator.services.mozilla.com/D250932

UltraBlame original commit: b534c633b2ea53fe10c965fc83f8a795b7f7e2a8
gecko-dev-updater pushed a commit to marco-c/gecko-dev-comments-removed that referenced this pull request May 28, 2025
…lues when serializing,

Automatic update from web-platform-tests
HTML: Escape "<" and ">" in attribute values when serializing

See whatwg/html#6362.
--

wpt-commits: 3fad71d22b6681a3430ff498add3ea8c31660401
wpt-pr: 27501

Differential Revision: https://phabricator.services.mozilla.com/D250932

UltraBlame original commit: b534c633b2ea53fe10c965fc83f8a795b7f7e2a8
gecko-dev-updater pushed a commit to marco-c/gecko-dev-wordified that referenced this pull request May 28, 2025
…lues when serializing,

Automatic update from web-platform-tests
HTML: Escape "<" and ">" in attribute values when serializing

See whatwg/html#6362.
--

wpt-commits: 3fad71d22b6681a3430ff498add3ea8c31660401
wpt-pr: 27501

Differential Revision: https://phabricator.services.mozilla.com/D250932

UltraBlame original commit: b534c633b2ea53fe10c965fc83f8a795b7f7e2a8
gotlougit pushed a commit to gotlougit/ladybird that referenced this pull request May 29, 2025
aarongable pushed a commit to chromium/chromium that referenced this pull request Jul 10, 2025
The experiment to escape "<" and ">" in attributes has been successful
and after https://crrev.com/c/6597054 it's been launched to everyone (in
M138) and is a part of the spec (see
whatwg/html#6362).

This CL does a cleanup changes the following: 1) Removes the use
counter which is no longer necessary. 2) Updates markup_formatter.{cc,h}
so that "<>" are always escaped. 3) Removes EscapeLtGtInAttributes as a
runtime enabled feature

Note that removing the usecounter forced change to the signature of
`AppendAttributeValue` method -- it no longer needs to accept
`Document`. So this CL changes more files so all call sites are changed
accordingly.

Because this has been launched in M138, it's now safe to remove in M140.

Bug: 40747109
Change-Id: If14151908eb74643fcbd2fa1d461f6002da3ccd7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6714366
Reviewed-by: Mason Freed <masonf@chromium.org>
Reviewed-by: Mike West <mkwst@chromium.org>
Commit-Queue: Michał Bentkowski <securitymb@google.com>
Cr-Commit-Position: refs/heads/main@{#1484826}
aarongable pushed a commit to chromium/chromium that referenced this pull request Jul 22, 2025
This reverts commit 8d717b4.

Reason for revert: Because I failed to merge the original change in M138, it actually landed in M139. So I'm reverting this one to make sure that the cleanup will happen at M141 at the earliest.

Bug: 40747109
Original change's description:
> Cleanup after EscapeLtGtInAttributes experiment
>
> The experiment to escape "<" and ">" in attributes has been successful
> and after https://crrev.com/c/6597054 it's been launched to everyone (in
> M138) and is a part of the spec (see
> whatwg/html#6362).
>
> This CL does a cleanup changes the following: 1) Removes the use
> counter which is no longer necessary. 2) Updates markup_formatter.{cc,h}
> so that "<>" are always escaped. 3) Removes EscapeLtGtInAttributes as a
> runtime enabled feature
>
> Note that removing the usecounter forced change to the signature of
> `AppendAttributeValue` method -- it no longer needs to accept
> `Document`. So this CL changes more files so all call sites are changed
> accordingly.
>
> Because this has been launched in M138, it's now safe to remove in M140.
>
> Bug: 40747109
> Change-Id: If14151908eb74643fcbd2fa1d461f6002da3ccd7
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6714366
> Reviewed-by: Mason Freed <masonf@chromium.org>
> Reviewed-by: Mike West <mkwst@chromium.org>
> Commit-Queue: Michał Bentkowski <securitymb@google.com>
> Cr-Commit-Position: refs/heads/main@{#1484826}

Bug: 40747109
Change-Id: I617c4a1159c33f4d30d52eea11eca52b58e64d26
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6769766
Reviewed-by: Mason Freed <masonf@chromium.org>
Commit-Queue: Michał Bentkowski <securitymb@google.com>
Reviewed-by: Mike West <mkwst@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1489988}
mnutt pushed a commit to movableink/webkit that referenced this pull request Jul 30, 2025
https://bugs.webkit.org/show_bug.cgi?id=292432
rdar://150520333

Reviewed by Ryosuke Niwa and Sam Weinig.

Implement whatwg/html#6362 and add a preference
in case we find out we have to disable it in certain cases.

The preference does not impact the recently added getHTML() method on
Element and ShadowRoot as that should be new enough to not matter.

Add new test from: web-platform-tests/wpt#27501

Canonical link: https://commits.webkit.org/295149@main
aarongable pushed a commit to chromium/chromium that referenced this pull request Sep 8, 2025
This reverts commit 548fe0c.

Reason for reland: Enough time has passed, we didn't get any new complaints.

Bug: 40747109
Original change's description:
> Revert "Cleanup after EscapeLtGtInAttributes experiment"
>
> This reverts commit 8d717b4.
>
> Reason for revert: Because I failed to merge the original change in M138, it actually landed in M139. So I'm reverting this one to make sure that the cleanup will happen at M141 at the earliest.
>
> Bug: 40747109
> Original change's description:
> > Cleanup after EscapeLtGtInAttributes experiment
> >
> > The experiment to escape "<" and ">" in attributes has been successful
> > and after https://crrev.com/c/6597054 it's been launched to everyone (in
> > M138) and is a part of the spec (see
> > whatwg/html#6362).
> >
> > This CL does a cleanup changes the following: 1) Removes the use
> > counter which is no longer necessary. 2) Updates markup_formatter.{cc,h}
> > so that "<>" are always escaped. 3) Removes EscapeLtGtInAttributes as a
> > runtime enabled feature
> >
> > Note that removing the usecounter forced change to the signature of
> > `AppendAttributeValue` method -- it no longer needs to accept
> > `Document`. So this CL changes more files so all call sites are changed
> > accordingly.
> >
> > Because this has been launched in M138, it's now safe to remove in M140.
> >
> > Bug: 40747109
> > Change-Id: If14151908eb74643fcbd2fa1d461f6002da3ccd7
> > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6714366
> > Reviewed-by: Mason Freed <masonf@chromium.org>
> > Reviewed-by: Mike West <mkwst@chromium.org>
> > Commit-Queue: Michał Bentkowski <securitymb@google.com>
> > Cr-Commit-Position: refs/heads/main@{#1484826}
>
> Bug: 40747109
> Change-Id: I617c4a1159c33f4d30d52eea11eca52b58e64d26
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6769766
> Reviewed-by: Mason Freed <masonf@chromium.org>
> Commit-Queue: Michał Bentkowski <securitymb@google.com>
> Reviewed-by: Mike West <mkwst@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1489988}

Bug: 40747109
Change-Id: I5995b31c1f82dc48fb44e289b9d0d6444b294dbe
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6905283
Reviewed-by: Daniel Vogelheim <vogelheim@chromium.org>
Reviewed-by: Mason Freed <masonf@chromium.org>
Commit-Queue: Michał Bentkowski <securitymb@google.com>
Cr-Commit-Position: refs/heads/main@{#1512746}
scottmessinger pushed a commit to commoncurriculum/linkedom that referenced this pull request Sep 29, 2026
Since the HTML standard's change of 2025-05-20 (whatwg/html#6362), the
fragment serialization escapes < and > in attribute values, and browsers
ship it. The fork's own escaper replaces entities' escapeAttribute: text
escapes &, <, > and U+00A0; attribute values also escape ". Elements
write their attributes through one serializeAttribute, and the fork no
longer depends on entities directly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JCrHZsvZnDmQpSnRDjP1Jb
scottmessinger pushed a commit to commoncurriculum/tarnish that referenced this pull request Sep 29, 2026
… does

Since whatwg/html#6362 (2025-05-20), the fragment serialization escapes `<`
and `>` in attribute values as well as in text, against mutation XSS; Chrome
138, Firefox 140 and Safari 26 ship it, and html5ever's own serializer does
it. `escape_attribute` now escapes `&`, `"`, `<`, `>` and U+00A0.

The fork makes the same change. Until its fixtures are recorded again, seven
recorded outputs of the pinned fork differ, each only by `&lt;`/`&gt;` in an
attribute: the trees of two parse inputs, as a template and as a document;
two serialized documents; and one rendered spec.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JCrHZsvZnDmQpSnRDjP1Jb
gaborbernat added a commit to gaborbernat/turbohtml that referenced this pull request Sep 30, 2026
The serializer and the start tags rewrite() rebuilds for edited
elements left < and > literal inside attribute values. A value that
holds markup stays inert after one parse, but a serialize and reparse
through foreign content or <noscript> can move it into a raw-text
element, where its </style> or </noscript> ends the element and the
rest becomes live markup. whatwg/html#6362 added < and > to the
attribute mode of "escaping a string" for this reason.

Escape < and > in attribute values under every formatter and layout
and in rewrite() start tags, and drop the angle-bracket switch from the
serializer's escape scan.
dawnop added a commit to dawnop/dawn-lang that referenced this pull request Oct 1, 2026
packages/tea-dom/src/render.dawn adds to_html and to_document, a third
reader of the Node[M] that diff/apply and wire already read. No existing
module changes.

Two escapes. Text escapes & < >. Attribute values are always double-quoted
and escape & < > ". The < > in attributes follows the WHATWG serialisation
change of May 2025 (whatwg/html#6362, shipped in Chrome 138, Firefox 140 and
Safari 26), which closes a mutation XSS route. elm-html-string escaping only
the quote, and leptos 0.6 streaming SSR skipping text escapes (leptos#4885),
are the two failures the tests are written against.

The promise is that the string parses to the DOM the bridge builds from the
same tree on its first frame, so two rules copy js/dom.mjs rather than plain
serialisation. checked on a form control prints nothing for "" and "false"
and a bare checked otherwise, which is writeProperty; printing
checked="false" would check the box the bridge leaves unchecked. A repeated
prop prints once, at its first position with its last value, which is what
new Map(node.props) keeps; a parser keeps the first of two attributes, so
printing both would make the two readers disagree on one tree.

Each of four mutations reds the suite: dropping the quote from the
attribute escape, a void test that is always false, deleting the checked
rule, and printing text unescaped.

tea_dom goes from 0.1.0 to 0.2.0. The version had never moved since the
package was created, through on_value, foreign and the serve variants;
nothing polices it, but a new public module is the addition 0.x minor
versions are for, and this ends the habit of never bumping it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Escape "<" and ">" in attributes when serializing HTML

4 participants