From 1092c8d22a2208f96a05c8ef44350eddc187ee0c Mon Sep 17 00:00:00 2001 From: narasimhan-lakshmi Date: Sun, 3 May 2026 16:25:44 +0530 Subject: [PATCH 1/5] added mxss warning about re-parsing sanitized html --- files/en-us/web/api/element/sethtml/index.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/files/en-us/web/api/element/sethtml/index.md b/files/en-us/web/api/element/sethtml/index.md index 1d17fc44c40abc2..426132aa069d029 100644 --- a/files/en-us/web/api/element/sethtml/index.md +++ b/files/en-us/web/api/element/sethtml/index.md @@ -66,6 +66,25 @@ It should also be used instead of {{domxref("Element.setHTMLUnsafe()")}}, unless Note that since this method always sanitizes input strings of XSS-unsafe entities, it is not secured or validated using the [Trusted Types API](/en-US/docs/Web/API/Trusted_Types_API). +## Re-parsing and mutated XSS (mXSS) + +Sanitizing HTML with the Sanitizer API or using `Element.prototype.setHTML()` helps remove unsafe nodes and attributes, but it does not eliminate the risk of mutated XSS (mXSS) when the sanitized HTML is serialized and later re-parsed. If sanitized HTML is serialized (for example via `innerHTML`) and later re-parsed by the browser, parsing-time transformations can re-introduce executable content or attributes that the sanitizer did not anticipate. + +Example — unsafe flow + +```js +// `code` comes from an untrusted source +div.setHTML(code); // Sanitizer runs here +other_div.innerHTML = div.innerHTML; // Re-parsing `innerHTML` — can trigger mXSS +``` + +Recommendations + +- Avoid round-tripping sanitized `innerHTML` as a string. If you must persist markup, re-sanitize on every parse before insertion. +- Prefer structured, safe representations (for example, store content as sanitized fragments or a safe data model) instead of raw HTML strings. +- Use defensive headers and policies: Content Security Policy (CSP), Trusted Types, and server-side validation. +- See also the WICG discussion on mutated XSS: https://wicg.github.io/sanitizer-api/#mutated-xss + ## Examples ### Basic usage From eeda31d79df4ad9dcd51c8b932fea5d732d77e56 Mon Sep 17 00:00:00 2001 From: Lakshminarasimhan <137640490+narasimhan-lakshmi@users.noreply.github.com> Date: Sun, 3 May 2026 16:41:00 +0530 Subject: [PATCH 2/5] Update files/en-us/web/api/element/sethtml/index.md Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- files/en-us/web/api/element/sethtml/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/en-us/web/api/element/sethtml/index.md b/files/en-us/web/api/element/sethtml/index.md index 426132aa069d029..dccdd44ef7b6135 100644 --- a/files/en-us/web/api/element/sethtml/index.md +++ b/files/en-us/web/api/element/sethtml/index.md @@ -74,7 +74,7 @@ Example — unsafe flow ```js // `code` comes from an untrusted source -div.setHTML(code); // Sanitizer runs here +div.setHTML(code); // Sanitizer runs here other_div.innerHTML = div.innerHTML; // Re-parsing `innerHTML` — can trigger mXSS ``` From 79feede467625a0210bc578697d7bb885192cc80 Mon Sep 17 00:00:00 2001 From: Lakshminarasimhan <137640490+narasimhan-lakshmi@users.noreply.github.com> Date: Mon, 4 May 2026 09:05:23 +0530 Subject: [PATCH 3/5] Update files/en-us/web/api/element/sethtml/index.md Co-authored-by: Hamish Willee --- files/en-us/web/api/element/sethtml/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/en-us/web/api/element/sethtml/index.md b/files/en-us/web/api/element/sethtml/index.md index dccdd44ef7b6135..c35b7cef653695e 100644 --- a/files/en-us/web/api/element/sethtml/index.md +++ b/files/en-us/web/api/element/sethtml/index.md @@ -68,7 +68,7 @@ Note that since this method always sanitizes input strings of XSS-unsafe entitie ## Re-parsing and mutated XSS (mXSS) -Sanitizing HTML with the Sanitizer API or using `Element.prototype.setHTML()` helps remove unsafe nodes and attributes, but it does not eliminate the risk of mutated XSS (mXSS) when the sanitized HTML is serialized and later re-parsed. If sanitized HTML is serialized (for example via `innerHTML`) and later re-parsed by the browser, parsing-time transformations can re-introduce executable content or attributes that the sanitizer did not anticipate. +Sanitizing HTML with the Sanitizer API or using `setHTML()` helps remove unsafe nodes and attributes, but it does not eliminate the risk of mutated XSS (mXSS) when the sanitized HTML is serialized and later re-parsed. If sanitized HTML is serialized (for example via `innerHTML`) and later re-parsed by the browser, parsing-time transformations can re-introduce executable content or attributes that the sanitizer did not anticipate. Example — unsafe flow From 2d5e3d8b94845edce46e5608655fa09686780251 Mon Sep 17 00:00:00 2001 From: Hamish Willee Date: Fri, 5 Jun 2026 15:08:39 +1000 Subject: [PATCH 4/5] Subedit --- files/en-us/web/api/element/sethtml/index.md | 34 ++++++++++++-------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/files/en-us/web/api/element/sethtml/index.md b/files/en-us/web/api/element/sethtml/index.md index c35b7cef653695e..365554ab9141ef4 100644 --- a/files/en-us/web/api/element/sethtml/index.md +++ b/files/en-us/web/api/element/sethtml/index.md @@ -66,24 +66,32 @@ It should also be used instead of {{domxref("Element.setHTMLUnsafe()")}}, unless Note that since this method always sanitizes input strings of XSS-unsafe entities, it is not secured or validated using the [Trusted Types API](/en-US/docs/Web/API/Trusted_Types_API). -## Re-parsing and mutated XSS (mXSS) +### Re-parsing and mutated XSS (mXSS) -Sanitizing HTML with the Sanitizer API or using `setHTML()` helps remove unsafe nodes and attributes, but it does not eliminate the risk of mutated XSS (mXSS) when the sanitized HTML is serialized and later re-parsed. If sanitized HTML is serialized (for example via `innerHTML`) and later re-parsed by the browser, parsing-time transformations can re-introduce executable content or attributes that the sanitizer did not anticipate. +Even after sanitizing HTML input with `setHTML()`, it is still not safe to serialize the HTML and reparse it using `innerHTML`. +For example, the following code is unsafe. -Example — unsafe flow - -```js -// `code` comes from an untrusted source -div.setHTML(code); // Sanitizer runs here -other_div.innerHTML = div.innerHTML; // Re-parsing `innerHTML` — can trigger mXSS +```js example-bad +div.setHTML(unsafeString); //Safe +const serializedHTML = div.innerHTML; //No longer sanitized! +other_element.innerHTML = serializedHTML; ``` -Recommendations +The reason for this is that sanitization is context-aware. +When you call `setHTML()` on a particular element the unsafe elements and attributes for that context are removed. +If you serialize the code and use it directly in another element, it may still contain elements that are unsafe in that element. + +This would be safe (if pointless): + +```js example-good +div.setHTML(unsafeString); // Safe +const serializedHTML = div.innerHTML; //No longer sanitized! +// This is safe +other_div.innerHTML = setHTML(serializedHTML); +``` -- Avoid round-tripping sanitized `innerHTML` as a string. If you must persist markup, re-sanitize on every parse before insertion. -- Prefer structured, safe representations (for example, store content as sanitized fragments or a safe data model) instead of raw HTML strings. -- Use defensive headers and policies: Content Security Policy (CSP), Trusted Types, and server-side validation. -- See also the WICG discussion on mutated XSS: https://wicg.github.io/sanitizer-api/#mutated-xss +There is a class of attacks that take advantage of this flaw, referred to as [mutated XSS](https://wicg.github.io/sanitizer-api/#mutated-xss). +The simple rule to avoid this problem is to only ever inject HTML strings using safe methods such as `setHTML()`. ## Examples From aaf9f3440e25521dacd3c6293530dc365e7e6184 Mon Sep 17 00:00:00 2001 From: Hamish Willee Date: Fri, 5 Jun 2026 15:27:36 +1000 Subject: [PATCH 5/5] Self-subedit --- files/en-us/web/api/element/sethtml/index.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/files/en-us/web/api/element/sethtml/index.md b/files/en-us/web/api/element/sethtml/index.md index 365554ab9141ef4..08b72d7a5f1e140 100644 --- a/files/en-us/web/api/element/sethtml/index.md +++ b/files/en-us/web/api/element/sethtml/index.md @@ -68,26 +68,25 @@ Note that since this method always sanitizes input strings of XSS-unsafe entitie ### Re-parsing and mutated XSS (mXSS) -Even after sanitizing HTML input with `setHTML()`, it is still not safe to serialize the HTML and reparse it using `innerHTML`. +Even after sanitizing HTML input with `setHTML()`, it is still not safe to serialize the HTML and re-parse it using `innerHTML`. For example, the following code is unsafe. ```js example-bad -div.setHTML(unsafeString); //Safe -const serializedHTML = div.innerHTML; //No longer sanitized! +div.setHTML(unsafeString); // Safe +const serializedHTML = div.innerHTML; // No longer sanitized! other_element.innerHTML = serializedHTML; ``` The reason for this is that sanitization is context-aware. -When you call `setHTML()` on a particular element the unsafe elements and attributes for that context are removed. -If you serialize the code and use it directly in another element, it may still contain elements that are unsafe in that element. +When you call `setHTML()` on a particular element, the unsafe elements and attributes for that context are removed. +If you serialize the HTML and use it directly in another element, it may still contain elements that are unsafe in that element. This would be safe (if pointless): ```js example-good div.setHTML(unsafeString); // Safe -const serializedHTML = div.innerHTML; //No longer sanitized! -// This is safe -other_div.innerHTML = setHTML(serializedHTML); +const serializedHTML = div.innerHTML; // Serialized as a plain string +other_div.setHTML(serializedHTML); // Safe — re-sanitized by setHTML() ``` There is a class of attacks that take advantage of this flaw, referred to as [mutated XSS](https://wicg.github.io/sanitizer-api/#mutated-xss).