# QA-15, MIM-14: scheme smuggling. Entity encodings, control characters, case and
# whitespace, in href and src and in every other attribute a browser reads as a URL.
# Written for Hoarse from the classic public catalogues (OWASP XSS filter-evasion
# cheat sheet, html5sec.org, PortSwigger's XSS cheat sheet); none copied verbatim.
# The full attribute x spelling matrix is generated in test_payload_corpus.py.

== js-href-plain
source: OWASP filter evasion
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="javascript:alert(1)">SAFE</a>

== js-href-mixed-case
source: OWASP filter evasion
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="JaVaScRiPt:alert(1)">SAFE</a>

== js-href-unquoted
expect: no-scheme=javascript keeps-text=SAFE
--
<a href=javascript:alert(1)>SAFE</a>

== js-href-backtick
source: OWASP filter evasion (IE backticks)
expect: no-scheme=javascript keeps-text=SAFE
--
<a href=`javascript:alert(1)`>SAFE</a>

== js-href-dec-entities
source: OWASP filter evasion
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;alert(1)">SAFE</a>

== js-href-dec-entities-no-semicolon
source: OWASP filter evasion
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="&#106&#97&#118&#97&#115&#99&#114&#105&#112&#116&#58alert(1)">SAFE</a>

== js-href-dec-entities-zero-padded
source: OWASP filter evasion
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058alert(1)">SAFE</a>

== js-href-hex-entities
source: OWASP filter evasion
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="&#x6A;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;&#x3A;alert(1)">SAFE</a>

== js-href-hex-entities-no-semicolon-upper
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="&#X6A&#X61&#X76&#X61&#X73&#X63&#X72&#X69&#X70&#X74&#X3Aalert(1)">SAFE</a>

== js-href-named-colon
source: html5sec (HTML5 named character references)
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="javascript&colon;alert(1)">SAFE</a>

== js-href-named-newline
source: html5sec
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="java&NewLine;script&colon;alert(1)">SAFE</a>

== js-href-named-tab
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="java&Tab;script:alert(1)">SAFE</a>

== js-href-tab-entity
source: OWASP filter evasion (embedded tab)
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="jav&#x09;ascript:alert(1)">SAFE</a>

== js-href-newline-entity
source: OWASP filter evasion (embedded newline)
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="jav&#x0A;ascript:alert(1)">SAFE</a>

== js-href-cr-entity
source: OWASP filter evasion (embedded carriage return)
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="jav&#x0D;ascript:alert(1)">SAFE</a>

== js-href-raw-tab
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="jav	ascript:alert(1)">SAFE</a>

== js-href-raw-newline
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="jav
ascript:alert(1)">SAFE</a>

== js-href-raw-cr
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="jav\x{d}ascript:alert(1)">SAFE</a>

== js-href-leading-space-and-meta
source: OWASP filter evasion (spaces and meta chars before the scheme)
expect: no-scheme=javascript keeps-text=SAFE
--
<a href=" &#14;  javascript:alert(1)">SAFE</a>

== js-href-leading-c0
source: PortSwigger cheat sheet (leading control characters)
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="\x{1}\x{2}\x{3}\x{1f}javascript:alert(1)">SAFE</a>

== js-href-leading-nul
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="\x{0}javascript:alert(1)">SAFE</a>

== js-href-trailing-c0
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="javascript:alert(1)\x{1}\x{20}">SAFE</a>

== js-href-all-strippables
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="&#x20;&#x09;\x{1f}j&#x0a;a&#x0d;v	a\x{d}s
c&Tab;r&NewLine;ipt&colon;alert(1)&#x20;">SAFE</a>

== js-href-double-encoded
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="&amp;#106;avascript:alert(1)">SAFE</a>

== js-href-percent-encoded
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="%6A%61%76%61%73%63%72%69%70%74:alert(1)">SAFE</a>

== js-href-fullwidth-colon
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="javascript&#xFF1A;alert(1)">SAFE</a>

== js-href-comment-break
source: html5sec (scheme with a JS comment after the colon)
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="javascript://%0Aalert(1)">SAFE</a>

== js-href-two-hrefs
source: duplicate attributes: the first one wins in HTML
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="javascript:alert(1)" href="https://example.test/">SAFE</a>

== js-href-second-href
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="https://example.test/" href="javascript:alert(1)">SAFE</a>

== js-href-slash-attr-separator
source: html5sec (slash as attribute separator)
expect: no-scheme=javascript keeps-text=SAFE
--
<a/href="javascript:alert(1)"/>SAFE</a>

== js-href-uppercase-attr
expect: no-scheme=javascript keeps-text=SAFE
--
<A HREF="JAVASCRIPT:alert(1)">SAFE</A>

== vbscript-href
source: OWASP filter evasion
expect: no-scheme=vbscript keeps-text=SAFE
--
<a href="vbscript:msgbox(1)">SAFE</a>

== file-href
expect: no-scheme=file keeps-text=SAFE
--
<a href="file:///etc/passwd">SAFE</a>

== about-href
expect: no-scheme=about keeps-text=SAFE
--
<a href="about:blank">SAFE</a>

== livescript-mocha-href
source: historical Netscape schemes
expect: no-scheme=livescript no-scheme=mocha keeps-text=SAFE
--
<a href="livescript:alert(1)">SAFE</a><a href="mocha:alert(1)">SAFE</a>

== jar-viewsource-mhtml-href
expect: no-scheme=jar no-scheme=view-source no-scheme=mhtml no-scheme=ms-its keeps-text=SAFE
--
<a href="jar:https://e.test/a.jar!/x.html">SAFE</a>
<a href="view-source:https://e.test/">SAFE</a>
<a href="mhtml:https://e.test/a.mht!x">SAFE</a>
<a href="ms-its:mhtml:file://C:/a.mht!x">SAFE</a>

== feed-js-href
source: Firefox feed: wrapper
expect: no-scheme=feed no-scheme=javascript keeps-text=SAFE
--
<a href="feed:javascript:alert(1)">SAFE</a>

== data-html-href
source: data: navigation to text/html
expect: no-scheme=data keeps-text=SAFE
--
<a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">SAFE</a>

== data-href-entity
expect: no-scheme=data keeps-text=SAFE
--
<a href="&#100;ata:text/html,<script>alert(1)</script>">SAFE</a>

== js-img-src
expect: no-scheme=javascript keeps-text=SAFE
--
<img src="javascript:alert(1)">SAFE

== js-img-src-entity-tab
expect: no-scheme=javascript keeps-text=SAFE
--
<img src="jav&#x09;ascript:alert(1)">SAFE

== js-img-src-unquoted-grave
source: OWASP filter evasion
expect: no-scheme=javascript keeps-text=SAFE
--
<IMG SRC=`javascript:alert("RSnake says, 'XSS'")`>SAFE

== js-img-src-quote-break
source: OWASP malformed IMG
expect: no-scheme=javascript no-element=script no-text=alert keeps-text=SAFE
--
<IMG """><SCRIPT>alert("XSS")</SCRIPT>">SAFE

== js-img-lowsrc-dynsrc
source: OWASP (IE lowsrc/dynsrc)
expect: no-scheme=javascript no-attr=lowsrc no-attr=dynsrc keeps-text=SAFE
--
<img lowsrc="javascript:alert(1)" dynsrc="javascript:alert(1)">SAFE

== js-cite
source: ammonia checks href/src only; cite is URL-bearing too (MIM-14 says every one)
expect: no-scheme=javascript keeps-text=SAFE
--
<blockquote cite="javascript:alert(1)">SAFE</blockquote><q cite="javascript:alert(1)">q</q><ins cite="vbscript:x">i</ins><del cite="file:///x">d</del>

== js-longdesc
expect: no-scheme=javascript no-attr=longdesc keeps-text=SAFE
--
<img src="cid:a@b" longdesc="javascript:alert(1)">SAFE

== js-background
source: OWASP (TABLE/TD BACKGROUND)
expect: no-scheme=javascript keeps-text=SAFE
--
<table background="javascript:alert(1)"><tr><td background="javascript:alert(1)">SAFE</td></tr></table>

== js-body-background
expect: no-scheme=javascript no-element=body keeps-text=SAFE
--
<body background="javascript:alert(1)">SAFE</body>

== js-area-href
expect: no-scheme=javascript keeps-text=SAFE
--
<map name="m"><area shape="rect" coords="0,0,9,9" href="javascript:alert(1)"></map><img src="cid:a@b" usemap="#m">SAFE

== js-usemap
expect: no-scheme=javascript keeps-text=SAFE
--
<img src="cid:a@b" usemap="javascript:alert(1)">SAFE

== js-form-action
expect: no-scheme=javascript no-element=form no-attr=action no-attr=formaction keeps-text=SAFE
--
<form action="javascript:alert(1)"><button formaction="javascript:alert(1)">x</button></form>SAFE

== js-object-data
source: OWASP (OBJECT data)
expect: no-scheme=javascript no-element=object no-attr=data keeps-text=SAFE
--
<object data="javascript:alert(1)" codebase="javascript:alert(1)" classid="clsid:x"></object>SAFE

== js-embed-iframe-frame
expect: no-scheme=javascript no-element=embed no-element=iframe no-element=frame keeps-text=SAFE
--
<embed src="javascript:alert(1)"><iframe src="javascript:alert(1)"></iframe><frameset><frame src="javascript:alert(1)"></frameset>SAFE

== js-video-poster
expect: no-scheme=javascript no-attr=poster no-element=video keeps-text=SAFE
--
<video poster="javascript:alert(1)"><source src="javascript:alert(1)"></video>SAFE

== js-a-ping
expect: no-scheme=javascript no-attr=ping keeps-text=SAFE
--
<a href="https://example.test/" ping="javascript:alert(1)">SAFE</a>

== js-xlink-href
source: html5sec (SVG xlink:href)
expect: no-scheme=javascript no-attr=xlink:href no-element=svg keeps-text=SAFE
--
SAFE <svg><a xlink:href="javascript:alert(1)"><text x="20" y="20">X</text></a></svg>SAFE

== js-math-href
source: html5sec (MathML href on any element)
expect: no-scheme=javascript no-element=math keeps-text=SAFE
--
SAFE <math href="javascript:alert(1)"><mtext>CLICK</mtext></math>SAFE

== js-srcset
expect: no-scheme=javascript no-attr=srcset keeps-text=SAFE
--
<img srcset="javascript:alert(1) 1x, https://e.test/a.png 2x">SAFE

== js-input-image
expect: no-scheme=javascript no-element=input keeps-text=SAFE
--
<input type="image" src="javascript:alert(1)">SAFE

== js-isindex-action
source: html5sec (isindex)
expect: no-scheme=javascript no-element=isindex no-attr=action keeps-text=SAFE
--
SAFE <isindex type=image src=1 onerror=alert(1) action="javascript:alert(1)">SAFE

== js-html-manifest-head-profile
expect: no-scheme=javascript no-attr=manifest no-attr=profile keeps-text=SAFE
--
<html manifest="javascript:alert(1)"><head profile="javascript:alert(1)"></head><body>SAFE</body></html>

== js-mailto-confusion
source: a mailto: that smuggles nothing (must survive as mailto)
expect: no-scheme=javascript keeps-text=SAFE has-attr=href
--
<a href="mailto:someone@example.test?subject=javascript:alert(1)">SAFE</a>

== js-in-https-path
source: a scheme name in the path is not a scheme
expect: keeps-text=SAFE has-attr=href
--
<a href="https://example.test/javascript:alert(1)">SAFE</a>

== utf7-meta-charset
source: UTF-7 XSS (OWASP); the charset is resolved before sanitising
expect: no-element=script keeps-text=SAFE
--
<meta charset="utf-7">+ADw-script+AD4-alert(1)+ADw-/script+AD4-SAFE

== utf7-http-equiv
expect: no-element=script keeps-text=SAFE
--
<meta http-equiv="Content-Type" content="text/html; charset=UTF-7">+ADw-script+AD4-alert(1)+ADw-/script+AD4-SAFE

== overlong-utf8-lt
source: overlong UTF-8 encoding of '<' (C0 BC) must not decode to '<'
expect: no-element=script keeps-text=SAFE
--
\b{C0}\b{BC}script\b{C0}\b{BE}alert(1)\b{C0}\b{BC}/script\b{C0}\b{BE}SAFE

== invalid-utf8-in-scheme
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="java\b{FF}script:alert(1)">SAFE</a><a href="\b{C2}\b{A0}javascript:alert(1)">SAFE</a>

== nbsp-before-scheme
source: U+00A0 is not C0: a conformant parser does not strip it
expect: no-scheme=javascript keeps-text=SAFE
--
<a href="&nbsp;javascript:alert(1)">SAFE</a><a href="\x{2028}javascript:alert(1)">SAFE</a><a href="\x{feff}javascript:alert(1)">SAFE</a>
