Skip to content

FF140 escaping < and > to &lt; and &gt; in attributes when serializing HTML #39639

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 0 additions & 43 deletions files/en-us/mozilla/firefox/experimental_features/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -1098,49 +1098,6 @@ The [HTML Sanitizer API](/en-US/docs/Web/API/HTML_Sanitizer_API) allow developer
</tbody>
</table>

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

Firefox replaces the `<` and `>` characters with `&lt;` and `&gt;` (respectively) in attributes when serializing HTML.
This prevents certain exploits where HTML is serialized and then injected back into the DOM.
The affected methods and properties are: {{domxref("Element.innerHTML")}}, {{domxref("Element.outerHTML")}}, {{domxref("Element.getHTML()")}}, {{domxref("ShadowRoot.innerHTML")}}, and {{domxref("ShadowRoot.getHTML()")}}.
([Firefox bug 1941347](https://bugzil.la/1941347)).

<table>
<thead>
<tr>
<th>Release channel</th>
<th>Version added</th>
<th>Enabled by default?</th>
</tr>
</thead>
<tbody>
<tr>
<th>Nightly</th>
<td>139</td>
<td>Yes</td>
</tr>
<tr>
<th>Developer Edition</th>
<td>139</td>
<td>No</td>
</tr>
<tr>
<th>Beta</th>
<td>139</td>
<td>No</td>
</tr>
<tr>
<th>Release</th>
<td>139</td>
<td>No</td>
</tr>
<tr>
<th>Preference name</th>
<td colspan="2"><code>dom.security.html_serialization_escape_lt_gt</code></td>
</tr>
</tbody>
</table>

### Removal of MutationEvent

{{domxref("MutationEvent")}} and its associated events (`DOMSubtreeModified`, `DOMNodeInserted`, `DOMNodeRemoved`, `DOMCharacterDataModified`, `DOMAttrModified`) are on the path for removal, and have been disabled on nightly.
Expand Down
3 changes: 3 additions & 0 deletions files/en-us/web/api/element/gethtml/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ The options can be used to include nested shadow roots that have been set as {{d

Without arguments, child nodes that are shadow roots are not serialized, and this method behaves in the same way as reading the value of {{domxref("Element.innerHTML")}}.

Note that some browsers serialize the `<` and `>` characters as `&lt;` and `&gt;` when they appear in attribute values (see [Browser compatibility](#browser_compatibility)).
This is to prevent a potential security vulnerability ([mutation XSS](https://research.securitum.com/dompurify-bypass-using-mxss/)) in which an attacker can craft input that bypasses a [sanitization function](/en-US/docs/Web/Security/Attacks/XSS#sanitization), enabling a cross-site scripting (XSS) attack.

## Syntax

```js-nolint
Expand Down
7 changes: 5 additions & 2 deletions files/en-us/web/api/element/innerhtml/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ browser-compat: api.Element.innerHTML

{{APIRef("DOM")}}

The {{domxref("Element")}} property **`innerHTML`** gets or sets the HTML or XML markup contained within the element.
The **`innerHTML`** property of the {{domxref("Element")}} interface gets or sets the HTML or XML markup contained within the element.

More precisely, `innerHTML` gets a serialization of the nested child DOM elements within the element, or sets HTML or XML that should be parsed to replace the DOM tree within the element.

Expand All @@ -20,6 +20,9 @@ Similarly, when setting element content using `innerHTML`, the HTML string is pa
So for example [`<template>`](/en-US/docs/Web/HTML/Reference/Elements/template) is parsed into as {{domxref("HTMLTemplateElement")}}, whether or not the [`shadowrootmode`](/en-US/docs/Web/HTML/Reference/Elements/template#shadowrootmode) attribute is specified
In order to set an element's contents from an HTML string that includes declarative shadow roots, you must use either {{domxref("Element.setHTMLUnsafe()")}} or {{domxref("ShadowRoot.setHTMLUnsafe()")}}.

Note that some browsers serialize `<` and `>` in attributes as `&lt;` and `&gt;` when reading the HTML (see [Browser compatibility](#browser_compatibility)).
This prevents certain exploits where code becomes executable when serialized and then deserialized into HTML.

## Value

A string containing the HTML serialization of the element's descendants.
Expand All @@ -42,7 +45,7 @@ Reading `innerHTML` causes the user agent to serialize the HTML or XML fragment
The resulting string is returned.

```js
let contents = myElement.innerHTML;
const contents = myElement.innerHTML;
```

This lets you look at the HTML markup of the element's content nodes.
Expand Down
25 changes: 10 additions & 15 deletions files/en-us/web/api/element/outerhtml/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,27 @@ browser-compat: api.Element.outerHTML
{{APIRef("DOM")}}

The **`outerHTML`** attribute of the {{ domxref("Element") }}
DOM interface gets the serialized HTML fragment describing the element including its
descendants. It can also be set to replace the element with nodes parsed from the given
string.
DOM interface gets the serialized HTML fragment describing the element including its descendants.
It can also be set to replace the element with nodes parsed from the given string.

To only obtain the HTML representation of the contents of an element, or to replace the
contents of an element, use the {{domxref("Element.innerHTML", "innerHTML")}} property
instead.
To only obtain the HTML representation of the contents of an element, or to replace the contents of an element, use the {{domxref("Element.innerHTML", "innerHTML")}} property instead.

Note that some browsers serialize `<` and `>` in attributes as `&lt;` and `&gt;` when reading the HTML (see [Browser compatibility](#browser_compatibility)).
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is the only actual change in this page.

This prevents certain exploits where code becomes executable when serialized and then deserialized into HTML.

## Value

Reading the value of `outerHTML` returns a string
containing an HTML serialization of the `element` and its descendants.
Setting the value of `outerHTML` replaces the element and all of its
descendants with a new DOM tree constructed by parsing the specified
`htmlString`.
Reading the value of `outerHTML` returns a string containing an HTML serialization of the `element` and its descendants.
Setting the value of `outerHTML` replaces the element and all of its descendants with a new DOM tree constructed by parsing the specified `htmlString`.

When set to the `null` value, that `null` value is converted to the empty string (`""`), so `elt.outerHTML = null` is equivalent to `elt.outerHTML = ""`.

### Exceptions

- `SyntaxError` {{domxref("DOMException")}}
- : Thrown if an attempt was made to set `outerHTML` using an HTML string which is not
valid.
- : Thrown if an attempt was made to set `outerHTML` using an HTML string which is not valid.
- `NoModificationAllowedError` {{domxref("DOMException")}}
- : Thrown if an attempt was made to set `outerHTML` on an element which is a direct
child of a {{domxref("Document")}}, such as {{domxref("Document.documentElement")}}.
- : Thrown if an attempt was made to set `outerHTML` on an element which is a direct child of a {{domxref("Document")}}, such as {{domxref("Document.documentElement")}}.

## Examples

Expand Down
3 changes: 3 additions & 0 deletions files/en-us/web/api/shadowroot/gethtml/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ The options can be used to include nested shadow roots that have been set as {{d

Without arguments, child nodes that are shadow roots are not serialized, and this method behaves in the same way as reading the value of {{domxref("Element.innerHTML")}}.

Note that some browsers serialize `<` and `>` in attributes as `&lt;` and `&gt;` in the returned HTML (see [Browser compatibility](#browser_compatibility)).
This prevents certain exploits where code becomes executable when serialized and then deserialized into HTML.

## Syntax

```js-nolint
Expand Down
9 changes: 6 additions & 3 deletions files/en-us/web/api/shadowroot/innerhtml/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ browser-compat: api.ShadowRoot.innerHTML

{{APIRef("Shadow DOM")}}

The **`innerHTML`** property of the {{domxref("ShadowRoot")}}
interface sets or returns a reference to the DOM tree inside the
`ShadowRoot`.
The **`innerHTML`** property of the {{domxref("ShadowRoot")}} interface sets gets or sets the HTML markup to the DOM tree inside the `ShadowRoot`.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The text here seemed completely wrong - it isn't a reference. There is a lot more that can be said.

I don't want to say it in this PR because that would be out of scope for this task. I do expect to be back here though, as I am currently doing work on the HTLM sanitization API which tanjentially affects this. I will also be looking at it as I add docs on the injection sinks for TrustedTypes.


Note that some browsers serialize `<` and `>` in attributes as `&lt;` and `&gt;` when reading the HTML (see [Browser compatibility](#browser_compatibility)).
This prevents certain exploits where code becomes executable when serialized and then deserialized into HTML.

## Value

Expand All @@ -20,6 +21,8 @@ When set to the `null` value, that `null` value is converted to the empty string

## Examples

### Setting the innerHTML of a Shadow root

```js
let customElem = document.querySelector("my-shadow-dom-element");
let shadow = customElem.shadowRoot;
Expand Down