@@ -25,13 +25,13 @@ module ReflectedXss {
2525 * is to prevent us from flagging plain-text or JSON responses as vulnerable.
2626 */
2727 class HttpResponseSink extends Sink instanceof Http:: ResponseSendArgument {
28- HttpResponseSink ( ) { not exists ( getANonHtmlHeaderDefinition ( this ) ) }
28+ HttpResponseSink ( ) { not exists ( getAXssSafeHeaderDefinition ( this ) ) }
2929 }
3030
3131 /**
32- * Gets a HeaderDefinition that defines a non-html content-type for `send`.
32+ * DEPRECATED: Gets a HeaderDefinition that defines a non-html content-type for `send`.
3333 */
34- Http:: HeaderDefinition getANonHtmlHeaderDefinition ( Http:: ResponseSendArgument send ) {
34+ deprecated Http:: HeaderDefinition getANonHtmlHeaderDefinition ( Http:: ResponseSendArgument send ) {
3535 exists ( Http:: RouteHandler h |
3636 send .getRouteHandler ( ) = h and
3737 result = nonHtmlContentTypeHeader ( h )
@@ -42,13 +42,49 @@ module ReflectedXss {
4242 }
4343
4444 /**
45- * Holds if `h` may send a response with a content type other than HTML.
45+ * DEPRECATED: Holds if `h` may send a response with a content type other than HTML.
4646 */
47- Http:: HeaderDefinition nonHtmlContentTypeHeader ( Http:: RouteHandler h ) {
47+ deprecated Http:: HeaderDefinition nonHtmlContentTypeHeader ( Http:: RouteHandler h ) {
4848 result = h .getAResponseHeader ( "content-type" ) and
4949 not exists ( string tp | result .defines ( "content-type" , tp ) | tp .regexpMatch ( "(?i).*html.*" ) )
5050 }
5151
52+ /**
53+ * Gets a HeaderDefinition that defines a XSS safe content-type for `send`.
54+ */
55+ Http:: HeaderDefinition getAXssSafeHeaderDefinition ( Http:: ResponseSendArgument send ) {
56+ exists ( Http:: RouteHandler h |
57+ send .getRouteHandler ( ) = h and
58+ result = xssSafeContentTypeHeader ( h )
59+ |
60+ // The HeaderDefinition affects a response sent at `send`.
61+ headerAffects ( result , send )
62+ )
63+ }
64+
65+ /**
66+ * Gets a content-type that may lead to javascript code being executed in the browser.
67+ * ref: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet#content-types
68+ */
69+ string xssUnsafeContentType ( ) {
70+ result =
71+ [
72+ "text/html" , "application/xhtml+xml" , "application/xml" , "text/xml" , "image/svg+xml" ,
73+ "text/xsl" , "application/vnd.wap.xhtml+xml" , "text/rdf" , "application/rdf+xml" ,
74+ "application/mathml+xml" , "text/vtt" , "text/cache-manifest"
75+ ]
76+ }
77+
78+ /**
79+ * Holds if `h` may send a response with a content type that is safe for XSS.
80+ */
81+ Http:: HeaderDefinition xssSafeContentTypeHeader ( Http:: RouteHandler h ) {
82+ result = h .getAResponseHeader ( "content-type" ) and
83+ not exists ( string tp | result .defines ( "content-type" , tp ) |
84+ tp .toLowerCase ( ) .matches ( xssUnsafeContentType ( ) + "%" )
85+ )
86+ }
87+
5288 /**
5389 * Holds if a header set in `header` is likely to affect a response sent at `sender`.
5490 */
@@ -61,6 +97,7 @@ module ReflectedXss {
6197 // There is no dominating header, and `header` is non-local.
6298 not isLocalHeaderDefinition ( header ) and
6399 not exists ( Http:: HeaderDefinition dominatingHeader |
100+ dominatingHeader .getAHeaderName ( ) = "content-type" and
64101 dominatingHeader .getBasicBlock ( ) .( ReachableBasicBlock ) .dominates ( sender .getBasicBlock ( ) )
65102 )
66103 )
0 commit comments