11/**
22 * @name Sensitive cookies without the HttpOnly response header set
3- * @description Sensitive cookies without 'HttpOnly' leaves session cookies vulnerable to an XSS attack.
3+ * @description Sensitive cookies without the 'HttpOnly' flag set leaves session cookies vulnerable to
4+ * an XSS attack. This query checks whether 'HttpOnly' is not set in a Java Cookie object
5+ * or the 'Set-Cookie' HTTP header.
46 * @kind path-problem
57 * @id java/sensitive-cookie-not-httponly
68 * @tags security
@@ -25,14 +27,14 @@ string getCsrfCookieNameRegex() { result = "(?i).*(csrf).*" }
2527 * they are special cookies implementing the Synchronizer Token Pattern that can be used in JavaScript.
2628 */
2729predicate isSensitiveCookieNameExpr ( Expr expr ) {
28- exists ( string s | s = expr .( CompileTimeConstantExpr ) .getStringValue ( ) . toLowerCase ( ) |
30+ exists ( string s | s = expr .( CompileTimeConstantExpr ) .getStringValue ( ) |
2931 s .regexpMatch ( getSensitiveCookieNameRegex ( ) ) and not s .regexpMatch ( getCsrfCookieNameRegex ( ) )
3032 )
3133 or
3234 isSensitiveCookieNameExpr ( expr .( AddExpr ) .getAnOperand ( ) )
3335}
3436
35- /** The method call `Set-Cookie` of `addHeader` or `setHeader` . */
37+ /** A method call that sets a `Set-Cookie` header . */
3638class SetCookieMethodAccess extends MethodAccess {
3739 SetCookieMethodAccess ( ) {
3840 (
@@ -59,33 +61,31 @@ class MatchesHttpOnlyConfiguration extends TaintTracking2::Configuration {
5961 }
6062}
6163
62- /** The cookie class of Java EE . */
64+ /** A class descended from `javax.servlet.http.Cookie` or `javax/jakarta.ws.rs.core.Cookie` . */
6365class CookieClass extends RefType {
6466 CookieClass ( ) {
6567 this .getASupertype * ( )
6668 .hasQualifiedName ( [ "javax.servlet.http" , "javax.ws.rs.core" , "jakarta.ws.rs.core" ] , "Cookie" )
6769 }
6870}
6971
70- /** Holds if the `Expr` expr is evaluated to boolean true. */
71- predicate isBooleanTrue ( Expr expr ) {
72+ /** Holds if the `expr` is `true` or a variable that is ever assigned ` true` . */
73+ predicate mayBeBooleanTrue ( Expr expr ) {
7274 expr .( CompileTimeConstantExpr ) .getBooleanValue ( ) = true or
7375 expr .( VarAccess ) .getVariable ( ) .getAnAssignedValue ( ) .( CompileTimeConstantExpr ) .getBooleanValue ( ) =
7476 true
7577}
7678
7779/** Holds if the method call sets the `HttpOnly` flag. */
78- predicate setHttpOnlyInCookie ( MethodAccess ma ) {
80+ predicate setsCookieHttpOnly ( MethodAccess ma ) {
7981 ma .getMethod ( ) .getName ( ) = "setHttpOnly" and
8082 (
81- isBooleanTrue ( ma .getArgument ( 0 ) ) // boolean literal true
83+ ma .getArgument ( 0 ) . ( CompileTimeConstantExpr ) . getBooleanValue ( ) = true
8284 or
83- exists (
84- MethodAccess mpa , int i // runtime assignment of boolean value true
85- |
86- TaintTracking:: localTaint ( DataFlow:: parameterNode ( mpa .getMethod ( ) .getParameter ( i ) ) ,
87- DataFlow:: exprNode ( ma .getArgument ( 0 ) ) ) and
88- isBooleanTrue ( mpa .getArgument ( i ) )
85+ // any use of setHttpOnly(x) where x isn't false is probably safe
86+ not exists ( VarAccess va |
87+ ma .getArgument ( 0 ) .( VarAccess ) .getVariable ( ) .getAnAccess ( ) = va and
88+ va .getVariable ( ) .getAnAssignedValue ( ) .( CompileTimeConstantExpr ) .getBooleanValue ( ) = false
8989 )
9090 )
9191}
@@ -99,7 +99,7 @@ class SetHttpOnlyInCookieConfiguration extends TaintTracking2::Configuration {
9999
100100 override predicate isSource ( DataFlow:: Node source ) {
101101 source .asExpr ( ) =
102- any ( MethodAccess ma | setHttpOnlyInCookie ( ma ) or removeCookie ( ma ) ) .getQualifier ( )
102+ any ( MethodAccess ma | setsCookieHttpOnly ( ma ) or removeCookie ( ma ) ) .getQualifier ( )
103103 }
104104
105105 override predicate isSink ( DataFlow:: Node sink ) {
@@ -108,18 +108,18 @@ class SetHttpOnlyInCookieConfiguration extends TaintTracking2::Configuration {
108108 }
109109}
110110
111- /** Holds if the method call removes a cookie. */
111+ /** Holds if `ma` removes a cookie. */
112112predicate removeCookie ( MethodAccess ma ) {
113113 ma .getMethod ( ) .getName ( ) = "setMaxAge" and
114114 ma .getArgument ( 0 ) .( IntegerLiteral ) .getIntValue ( ) = 0
115115}
116116
117- /** Sensitive cookie name used in a `Cookie` constructor or a `Set-Cookie` call . */
117+ /** A sensitive cookie name. */
118118class SensitiveCookieNameExpr extends Expr {
119119 SensitiveCookieNameExpr ( ) { isSensitiveCookieNameExpr ( this ) }
120120}
121121
122- /** Sink of adding a cookie to the HTTP response. */
122+ /** A cookie that is added to an HTTP response, used as a sink in `MissingHttpOnlyConfiguration` . */
123123class CookieResponseSink extends DataFlow:: ExprNode {
124124 CookieResponseSink ( ) {
125125 exists ( MethodAccess ma |
@@ -145,14 +145,14 @@ predicate setHttpOnlyInNewCookie(ClassInstanceExpr cie) {
145145 cie .getConstructedType ( ) .hasQualifiedName ( [ "javax.ws.rs.core" , "jakarta.ws.rs.core" ] , "NewCookie" ) and
146146 (
147147 cie .getNumArgument ( ) = 6 and
148- isBooleanTrue ( cie .getArgument ( 5 ) ) // NewCookie(Cookie cookie, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
148+ mayBeBooleanTrue ( cie .getArgument ( 5 ) ) // NewCookie(Cookie cookie, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
149149 or
150150 cie .getNumArgument ( ) = 8 and
151151 cie .getArgument ( 6 ) .getType ( ) instanceof BooleanType and
152- isBooleanTrue ( cie .getArgument ( 7 ) ) // NewCookie(String name, String value, String path, String domain, String comment, int maxAge, boolean secure, boolean httpOnly)
152+ mayBeBooleanTrue ( cie .getArgument ( 7 ) ) // NewCookie(String name, String value, String path, String domain, String comment, int maxAge, boolean secure, boolean httpOnly)
153153 or
154154 cie .getNumArgument ( ) = 10 and
155- isBooleanTrue ( cie .getArgument ( 9 ) ) // NewCookie(String name, String value, String path, String domain, int version, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
155+ mayBeBooleanTrue ( cie .getArgument ( 9 ) ) // NewCookie(String name, String value, String path, String domain, int version, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
156156 )
157157}
158158
0 commit comments