@@ -33,7 +33,23 @@ predicate isSensitiveCookieNameExpr(Expr expr) {
3333
3434/** Holds if a string is concatenated with the `HttpOnly` flag. */
3535predicate hasHttpOnlyExpr ( Expr expr ) {
36- expr .( CompileTimeConstantExpr ) .getStringValue ( ) .toLowerCase ( ) .matches ( "%httponly%" ) or
36+ (
37+ expr .( CompileTimeConstantExpr ) .getStringValue ( ) .toLowerCase ( ) .matches ( "%httponly%" )
38+ or
39+ exists (
40+ StaticMethodAccess ma // String.format("%s=%s;HttpOnly", "sessionkey", sessionKey)
41+ |
42+ ma .getType ( ) .getName ( ) = "String" and
43+ ma .getMethod ( ) .getName ( ) = "format" and
44+ ma .getArgument ( 0 )
45+ .( CompileTimeConstantExpr )
46+ .getStringValue ( )
47+ .toLowerCase ( )
48+ .matches ( "%httponly%" ) and
49+ expr = ma
50+ )
51+ )
52+ or
3753 hasHttpOnlyExpr ( expr .( AddExpr ) .getAnOperand ( ) )
3854}
3955
@@ -56,6 +72,40 @@ class CookieClass extends RefType {
5672 }
5773}
5874
75+ /** Holds if the `Expr` expr is evaluated to boolean true. */
76+ predicate isBooleanTrue ( Expr expr ) {
77+ expr .( CompileTimeConstantExpr ) .getBooleanValue ( ) = true or
78+ expr .( VarAccess ) .getVariable ( ) .getAnAssignedValue ( ) .( CompileTimeConstantExpr ) .getBooleanValue ( ) =
79+ true
80+ }
81+
82+ /** Holds if the method or a wrapper method sets the `HttpOnly` flag. */
83+ predicate setHttpOnlyInCookie ( MethodAccess ma ) {
84+ ma .getMethod ( ) .getName ( ) = "setHttpOnly" and
85+ (
86+ isBooleanTrue ( ma .getArgument ( 0 ) ) // boolean literal true
87+ or
88+ exists (
89+ MethodAccess mpa , int i // runtime assignment of boolean value true
90+ |
91+ TaintTracking:: localTaint ( DataFlow:: parameterNode ( mpa .getMethod ( ) .getParameter ( i ) ) ,
92+ DataFlow:: exprNode ( ma .getArgument ( 0 ) ) ) and
93+ isBooleanTrue ( mpa .getArgument ( i ) )
94+ )
95+ )
96+ or
97+ exists ( MethodAccess mca |
98+ ma .getMethod ( ) .calls ( mca .getMethod ( ) ) and
99+ setHttpOnlyInCookie ( mca )
100+ )
101+ }
102+
103+ /** Holds if the method or a wrapper method removes a cookie. */
104+ predicate removeCookie ( MethodAccess ma ) {
105+ ma .getMethod ( ) .getName ( ) = "setMaxAge" and
106+ ma .getArgument ( 0 ) .( IntegerLiteral ) .getIntValue ( ) = 0
107+ }
108+
59109/** Sensitive cookie name used in a `Cookie` constructor or a `Set-Cookie` call. */
60110class SensitiveCookieNameExpr extends Expr {
61111 SensitiveCookieNameExpr ( ) { isSensitiveCookieNameExpr ( this ) }
@@ -69,11 +119,16 @@ class CookieResponseSink extends DataFlow::ExprNode {
69119 ma .getMethod ( ) instanceof ResponseAddCookieMethod and
70120 this .getExpr ( ) = ma .getArgument ( 0 ) and
71121 not exists (
72- MethodAccess ma2 // cookie.setHttpOnly(true)
122+ MethodAccess ma2 // a method or wrapper method that invokes cookie.setHttpOnly(true)
73123 |
74- ma2 .getMethod ( ) .getName ( ) = "setHttpOnly" and
75- ma2 .getArgument ( 0 ) .( BooleanLiteral ) .getBooleanValue ( ) = true and
76- DataFlow:: localExprFlow ( ma2 .getQualifier ( ) , this .getExpr ( ) )
124+ (
125+ setHttpOnlyInCookie ( ma2 ) or
126+ removeCookie ( ma2 )
127+ ) and
128+ (
129+ DataFlow:: localExprFlow ( ma2 .getQualifier ( ) , this .getExpr ( ) ) or
130+ DataFlow:: localExprFlow ( ma2 , this .getExpr ( ) )
131+ )
77132 )
78133 or
79134 ma instanceof SetCookieMethodAccess and
@@ -92,13 +147,15 @@ class CookieResponseSink extends DataFlow::ExprNode {
92147predicate setHttpOnlyInNewCookie ( ClassInstanceExpr cie ) {
93148 cie .getConstructedType ( ) .hasQualifiedName ( [ "javax.ws.rs.core" , "jakarta.ws.rs.core" ] , "NewCookie" ) and
94149 (
95- cie .getNumArgument ( ) = 6 and cie .getArgument ( 5 ) .( BooleanLiteral ) .getBooleanValue ( ) = true // NewCookie(Cookie cookie, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
150+ cie .getNumArgument ( ) = 6 and
151+ isBooleanTrue ( cie .getArgument ( 5 ) ) // NewCookie(Cookie cookie, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
96152 or
97153 cie .getNumArgument ( ) = 8 and
98154 cie .getArgument ( 6 ) .getType ( ) instanceof BooleanType and
99- cie .getArgument ( 7 ) . ( BooleanLiteral ) . getBooleanValue ( ) = true // NewCookie(String name, String value, String path, String domain, String comment, int maxAge, boolean secure, boolean httpOnly)
155+ isBooleanTrue ( cie .getArgument ( 7 ) ) // NewCookie(String name, String value, String path, String domain, String comment, int maxAge, boolean secure, boolean httpOnly)
100156 or
101- cie .getNumArgument ( ) = 10 and cie .getArgument ( 9 ) .( BooleanLiteral ) .getBooleanValue ( ) = true // NewCookie(String name, String value, String path, String domain, int version, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
157+ cie .getNumArgument ( ) = 10 and
158+ 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)
102159 )
103160}
104161
0 commit comments