# QA-15, MIM-12, MIM-16: mutation XSS. Markup that parses one way in the sanitiser and
# another way once serialised and re-parsed by the renderer: namespace confusion
# (svg/math), raw-text elements (noscript, style, xmp, iframe, noembed, title,
# textarea), template contents, and foster parenting. Patterns from the public mXSS
# literature (Heiderich et al., "mXSS attacks"; the 2019-2020 DOMPurify bypasses;
# PortSwigger's research); written for Hoarse, none copied verbatim.
#
# Every case is also run twice through the pipeline by the property test: the output
# of sanitise() fed back in must give the same structure (a mutation shows as drift).

== noscript-title-breakout
source: noscript parsed as raw text with scripting on, as markup with it off
expect: no-attr=onerror no-element=noscript keeps-text=SAFE
--
SAFE <noscript><p title="</noscript><img src=x onerror=alert(1)>">SAFE

== noscript-style-breakout
expect: no-attr=onerror no-element=noscript no-element=style keeps-text=SAFE
--
<noscript><style></noscript><img src=x onerror=alert(1)></style></noscript>SAFE

== math-mtext-mglyph-style
source: the 2020 DOMPurify math/mglyph/style namespace confusion
expect: no-attr=onerror no-element=math no-element=mglyph no-element=style keeps-text=SAFE
--
SAFE <math><mtext><table><mglyph><style><!--</style><img title="--&gt;&lt;img src=1 onerror=alert(1)&gt;">SAFE

== math-mtext-form-mglyph
source: form element nesting and namespace switching (DOMPurify 2.0.17 class)
expect: no-attr=onerror no-element=math no-element=form keeps-text=SAFE
--
SAFE <form><math><mtext></form><form><mglyph><style></math><img src onerror=alert(1)>SAFE

== math-annotation-xml-encoding
source: annotation-xml encoding="text/html" is an HTML integration point
expect: no-element=math no-element=annotation-xml no-element=script no-text=alert keeps-text=SAFE
--
<math><annotation-xml encoding="text/html"><script>alert(1)</script></annotation-xml></math>SAFE

== math-maction-xlink
source: html5sec MathML maction
expect: no-scheme=javascript no-element=math no-element=maction keeps-text=SAFE
--
<math><maction actiontype="statusline#http://e.test" xlink:href="javascript:alert(1)">CLICK</maction></math>SAFE

== svg-style-cdata
source: style inside svg is not raw text: its content is markup
expect: no-attr=onerror no-element=svg no-element=style keeps-text=SAFE
--
<svg><style><img src=x onerror=alert(1)></style></svg>SAFE

== svg-foreignobject
expect: no-attr=onerror no-element=svg no-element=foreignobject keeps-text=SAFE
--
<svg><foreignObject><img src=x onerror=alert(1)></foreignObject></svg>SAFE

== svg-script-entity
source: script inside svg decodes entities (XML-ish), unlike HTML script
expect: no-element=svg no-element=script no-text=alert keeps-text=SAFE
--
<svg><script>&#97;lert(1)</script></svg>SAFE

== svg-animate-href
source: html5sec SVG animate/set changing href
expect: no-scheme=javascript no-element=svg no-element=animate no-element=set keeps-text=SAFE
--
<svg><a><animate attributeName="href" values="javascript:alert(1)"/><set attributeName="href" to="javascript:alert(1)"/><text x="0" y="20">X</text></a></svg>SAFE

== svg-onload
source: OWASP
expect: no-attr=onload no-element=svg keeps-text=SAFE
--
SAFE <svg/onload=alert(1)>SAFE

== svg-uppercase-namespace
expect: no-element=svg no-attr=onload keeps-text=SAFE
--
SAFE <SVG ONLOAD=alert(1)><DESC><IMG SRC=x ONERROR=alert(1)></DESC></SVG>SAFE

== svg-in-p-closing
source: </p> inside foreign content breaks out of svg
expect: no-attr=onerror no-element=svg keeps-text=SAFE
--
SAFE <svg></p><style><a id="</style><img src=1 onerror=alert(1)>">SAFE

== template-content
source: template content is inert in the DOM but serialised back as markup
expect: no-element=template no-element=script no-text=alert keeps-text=SAFE
--
<template><script>alert(1)</script><img src=x onerror=alert(1)></template>SAFE

== template-nested-in-table
expect: no-element=template no-attr=onerror keeps-text=SAFE
--
<table><template><td><img src=x onerror=alert(1)></td></template></table>SAFE

== select-style-breakout
source: style inside select is not a raw-text context in some parsers
expect: no-element=select no-element=style no-attr=onerror keeps-text=SAFE
--
<select><style></select><img src=x onerror=alert(1)></style></select>SAFE

== select-template-style
expect: no-element=select no-element=template no-attr=onerror keeps-text=SAFE
--
SAFE <select><template><style><!--</style><a rel="--></style></template></select><img src=x onerror=alert(1)>">SAFE

== select-keygen-option
expect: no-element=select no-element=option no-element=keygen no-attr=onerror keeps-text=SAFE
--
<select><keygen autofocus onfocus=alert(1)><option><img src=x onerror=alert(1)></option></select>SAFE

== textarea-breakout
expect: no-element=textarea no-attr=onerror keeps-text=SAFE
--
<textarea><img title="</textarea><img src=x onerror=alert(1)>"></textarea>SAFE

== title-breakout
expect: no-element=title no-attr=onerror keeps-text=SAFE
--
<title><img title="</title><img src=x onerror=alert(1)>"></title>SAFE

== xmp-breakout
source: xmp is raw text
expect: no-element=xmp no-attr=onerror keeps-text=SAFE
--
<xmp><img title="</xmp><img src=x onerror=alert(1)>"></xmp>SAFE

== iframe-raw-text
expect: no-element=iframe no-attr=onerror keeps-text=SAFE
--
<iframe><img title="</iframe><img src=x onerror=alert(1)>"></iframe>SAFE

== noembed-noframes-breakout
expect: no-element=noembed no-element=noframes no-attr=onerror keeps-text=SAFE
--
<noembed><img title="</noembed><img src=x onerror=alert(1)>"></noembed><noframes><img title="</noframes><img src=x onerror=alert(1)>"></noframes>SAFE

== plaintext-swallow
source: plaintext never closes; everything after is text
expect: no-element=plaintext keeps-text=SAFE
--
SAFE SAFE<plaintext><img src=x onerror=alert(1)>

== listing-leading-newline
source: listing/pre drop one leading newline on parse, which changes serialisation
expect: no-element=listing no-attr=onerror keeps-text=SAFE
--
<listing>&lt;img src=x onerror=alert(1)&gt;</listing>SAFE

== comment-breakout-attr
source: a comment end inside an attribute value
expect: no-attr=onerror keeps-text=SAFE
--
<!--<img title="--><img src=x onerror=alert(1)>">SAFE

== comment-bang-close
source: --!> closes a comment in HTML5
expect: no-attr=onerror keeps-text=SAFE
--
<!-- --!><img src=x onerror=alert(1)> -->SAFE

== comment-ie-conditional
source: IE conditional comments and downlevel-revealed
expect: no-element=script no-raw=[if keeps-text=SAFE
--
<!--[if gte IE 4]><script>alert(1)</script><![endif]--><![if !IE]><b>SAFE</b><![endif]>

== cdata-in-html
source: CDATA outside foreign content is a bogus comment
expect: no-attr=onerror keeps-text=SAFE
--
<![CDATA[<img src=x onerror=alert(1)>]]>SAFE

== cdata-in-svg
expect: no-element=svg no-attr=onerror keeps-text=SAFE
--
<svg><![CDATA[><img src=x onerror=alert(1)>]]></svg>SAFE

== processing-instruction
expect: no-attr=onerror keeps-text=SAFE
--
<?xml version="1.0"?><?xml-stylesheet href="javascript:alert(1)"?><img src=x onerror=alert(1)>SAFE

== foster-parenting
source: content in a table but outside a cell is moved before the table
expect: no-attr=onerror keeps-text=SAFE
--
<table><img src=x onerror=alert(1)><tr><td>SAFE</td></tr></table>

== backtick-attribute-ie
source: IE treated backticks as attribute quotes when serialising
expect: no-attr=onerror keeps-text=SAFE
--
<img src="cid:a@b" alt="``onerror=alert(1)">SAFE

== attr-name-with-quote
expect: no-attr=onerror keeps-text=SAFE
--
<img src="cid:a@b" "onerror=alert(1)" x"onerror=alert(1)>SAFE

== attr-no-space-separator
source: html5sec (attributes with no separating whitespace)
expect: no-attr=onerror keeps-text=SAFE
--
<img src="cid:a@b"onerror=alert(1)>SAFE

== tag-name-null
expect: no-element=script keeps-text=SAFE
--
<scr\x{0}ipt>alert(1)</scr\x{0}ipt>SAFE

== nested-script-tag-split
source: OWASP (a filter that deletes <script> once)
expect: no-element=script keeps-text=SAFE
--
<scr<script>ipt>alert(1)</scr</script>ipt>SAFE

== script-in-attribute-value
source: a tag inside an attribute value is text, not markup
expect: no-element=script keeps-text=SAFE
--
<p title="<script>alert(1)</script>">SAFE</p>

== unclosed-script
expect: no-element=script keeps-text=SAFE
--
SAFE<script>alert(1)

== script-uppercase-with-attrs
expect: no-element=script no-text=alert keeps-text=SAFE
--
<SCRIPT/XSS SRC="http://e.test/x.js"></SCRIPT><SCRIPT a=">" SRC="http://e.test/x.js">alert(1)</SCRIPT>SAFE

== html-entity-in-raw-text
source: entities do not decode inside raw text: a sanitiser that decodes then re-emits as text must escape
expect: no-attr=onerror no-element=style keeps-text=SAFE
--
<style>&lt;/style&gt;&lt;img src=x onerror=alert(1)&gt;</style>SAFE

== text-entity-reemit
source: escaped markup in text must stay escaped through the pipeline
expect: no-attr=onerror no-element=img keeps-text=SAFE
--
&lt;img src=x onerror=alert(1)&gt;SAFE

== double-escaped-text
expect: no-attr=onerror no-element=img keeps-text=SAFE
--
&amp;lt;img src=x onerror=alert(1)&amp;gt;SAFE

== is-attribute-custom-element
expect: no-attr=is no-attr=onclick keeps-text=SAFE
--
<p is="x-evil" onclick="alert(1)">SAFE</p>

== dom-clobber-name
source: DOM clobbering through name/id (matters to the later WebEngine renderer)
expect: no-element=form keeps-text=SAFE
--
SAFE <form name="body"><img name="cookie"><a name="defaultView" href="cid:x">SAFE</a></form>
