9
9
10
10
import java
11
11
import semmle.code.java.frameworks.Servlets
12
- import semmle.code.java.dataflow.FlowSources
13
12
import semmle.code.java.dataflow.TaintTracking
14
13
import DataFlow:: PathGraph
15
14
@@ -18,16 +17,16 @@ string getSensitiveCookieNameRegex() { result = "(?i).*(auth|session|token|key|c
18
17
19
18
/** Holds if a string is concatenated with the name of a sensitive cookie. */
20
19
predicate isSensitiveCookieNameExpr ( Expr expr ) {
21
- expr .( StringLiteral )
22
- .getRepresentedString ( )
20
+ expr .( CompileTimeConstantExpr )
21
+ .getStringValue ( )
23
22
.toLowerCase ( )
24
23
.regexpMatch ( getSensitiveCookieNameRegex ( ) ) or
25
24
isSensitiveCookieNameExpr ( expr .( AddExpr ) .getAnOperand ( ) )
26
25
}
27
26
28
27
/** Holds if a string is concatenated with the `HttpOnly` flag. */
29
28
predicate hasHttpOnlyExpr ( Expr expr ) {
30
- expr .( StringLiteral ) . getRepresentedString ( ) .toLowerCase ( ) .matches ( "%httponly%" ) or
29
+ expr .( CompileTimeConstantExpr ) . getStringValue ( ) .toLowerCase ( ) .matches ( "%httponly%" ) or
31
30
hasHttpOnlyExpr ( expr .( AddExpr ) .getAnOperand ( ) )
32
31
}
33
32
@@ -38,37 +37,34 @@ class SetCookieMethodAccess extends MethodAccess {
38
37
this .getMethod ( ) instanceof ResponseAddHeaderMethod or
39
38
this .getMethod ( ) instanceof ResponseSetHeaderMethod
40
39
) and
41
- this .getArgument ( 0 ) .( StringLiteral ) . getRepresentedString ( ) .toLowerCase ( ) = "set-cookie"
40
+ this .getArgument ( 0 ) .( CompileTimeConstantExpr ) . getStringValue ( ) .toLowerCase ( ) = "set-cookie"
42
41
}
43
42
}
44
43
45
44
/** Sensitive cookie name used in a `Cookie` constructor or a `Set-Cookie` call. */
46
45
class SensitiveCookieNameExpr extends Expr {
47
46
SensitiveCookieNameExpr ( ) {
48
- isSensitiveCookieNameExpr ( this ) and
49
- (
50
- exists (
51
- ClassInstanceExpr cie // new Cookie("jwt_token", token)
52
- |
53
- (
54
- cie .getConstructor ( ) .getDeclaringType ( ) .hasQualifiedName ( "javax.servlet.http" , "Cookie" ) or
55
- cie .getConstructor ( )
56
- .getDeclaringType ( )
57
- .getASupertype * ( )
58
- .hasQualifiedName ( "javax.ws.rs.core" , "Cookie" ) or
59
- cie .getConstructor ( )
60
- .getDeclaringType ( )
61
- .getASupertype * ( )
62
- .hasQualifiedName ( "jakarta.ws.rs.core" , "Cookie" )
63
- ) and
64
- DataFlow:: localExprFlow ( this , cie .getArgument ( 0 ) )
65
- )
66
- or
67
- exists (
68
- SetCookieMethodAccess ma // response.addHeader("Set-Cookie: token=" +authId + ";HttpOnly;Secure")
69
- |
70
- DataFlow:: localExprFlow ( this , ma .getArgument ( 1 ) )
71
- )
47
+ exists (
48
+ ClassInstanceExpr cie , Expr e // new Cookie("jwt_token", token)
49
+ |
50
+ (
51
+ cie .getConstructor ( ) .getDeclaringType ( ) .hasQualifiedName ( "javax.servlet.http" , "Cookie" ) or
52
+ cie .getConstructor ( )
53
+ .getDeclaringType ( )
54
+ .getASupertype * ( )
55
+ .hasQualifiedName ( [ "javax.ws.rs.core" , "jakarta.ws.rs.core" ] , "Cookie" )
56
+ ) and
57
+ this = cie and
58
+ isSensitiveCookieNameExpr ( e ) and
59
+ DataFlow:: localExprFlow ( e , cie .getArgument ( 0 ) )
60
+ )
61
+ or
62
+ exists (
63
+ SetCookieMethodAccess ma , Expr e // response.addHeader("Set-Cookie: token=" +authId + ";HttpOnly;Secure")
64
+ |
65
+ this = ma .getArgument ( 1 ) and
66
+ isSensitiveCookieNameExpr ( e ) and
67
+ DataFlow:: localExprFlow ( e , ma .getArgument ( 1 ) )
72
68
)
73
69
}
74
70
}
@@ -78,15 +74,20 @@ class CookieResponseSink extends DataFlow::ExprNode {
78
74
CookieResponseSink ( ) {
79
75
exists ( MethodAccess ma |
80
76
(
81
- ma .getMethod ( ) instanceof ResponseAddCookieMethod or
82
- ma instanceof SetCookieMethodAccess
83
- ) and
84
- ma .getAnArgument ( ) = this .getExpr ( )
77
+ ma .getMethod ( ) instanceof ResponseAddCookieMethod and
78
+ this .getExpr ( ) = ma .getArgument ( 0 )
79
+ or
80
+ ma instanceof SetCookieMethodAccess and
81
+ this .getExpr ( ) = ma .getArgument ( 1 )
82
+ )
85
83
)
86
84
}
87
85
}
88
86
89
- /** Holds if the `node` is a method call of `setHttpOnly(true)` on a cookie. */
87
+ /**
88
+ * Holds if `node` is an access to a variable which has `setHttpOnly(true)` called on it and is also
89
+ * the first argument to a call to the method `addCookie` of `javax.servlet.http.HttpServletResponse`.
90
+ */
90
91
predicate setHttpOnlyMethodAccess ( DataFlow:: Node node ) {
91
92
exists (
92
93
MethodAccess addCookie , Variable cookie , MethodAccess m // jwtCookie.setHttpOnly(true)
@@ -100,28 +101,28 @@ predicate setHttpOnlyMethodAccess(DataFlow::Node node) {
100
101
)
101
102
}
102
103
103
- /** Holds if the `node` is a method call of `Set-Cookie` header with the `HttpOnly` flag whose cookie name is sensitive. */
104
+ /**
105
+ * Holds if `node` is a string that contains `httponly` and which flows to the second argument
106
+ * of a method to set a cookie.
107
+ */
104
108
predicate setHttpOnlyInSetCookie ( DataFlow:: Node node ) {
105
109
exists ( SetCookieMethodAccess sa |
106
110
hasHttpOnlyExpr ( node .asExpr ( ) ) and
107
111
DataFlow:: localExprFlow ( node .asExpr ( ) , sa .getArgument ( 1 ) )
108
112
)
109
113
}
110
114
111
- /** Holds if the `node` is an invocation of a JAX-RS `NewCookie` constructor that sets `HttpOnly` to true. */
112
- predicate setHttpOnlyInNewCookie ( DataFlow:: Node node ) {
113
- exists ( ClassInstanceExpr cie |
114
- cie .getConstructor ( ) .getDeclaringType ( ) .hasName ( "NewCookie" ) and
115
- DataFlow:: localExprFlow ( node .asExpr ( ) , cie .getArgument ( 0 ) ) and
116
- (
117
- cie .getNumArgument ( ) = 6 and cie .getArgument ( 5 ) .( BooleanLiteral ) .getBooleanValue ( ) = true // NewCookie(Cookie cookie, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
118
- or
119
- cie .getNumArgument ( ) = 8 and
120
- cie .getArgument ( 6 ) .getType ( ) instanceof BooleanType and
121
- cie .getArgument ( 7 ) .( BooleanLiteral ) .getBooleanValue ( ) = true // NewCookie(String name, String value, String path, String domain, String comment, int maxAge, boolean secure, boolean httpOnly)
122
- or
123
- 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)
124
- )
115
+ /** Holds if `cie` is an invocation of a JAX-RS `NewCookie` constructor that sets `HttpOnly` to true. */
116
+ predicate setHttpOnlyInNewCookie ( ClassInstanceExpr cie ) {
117
+ cie .getConstructedType ( ) .hasQualifiedName ( [ "javax.ws.rs.core" , "jakarta.ws.rs.core" ] , "NewCookie" ) and
118
+ (
119
+ cie .getNumArgument ( ) = 6 and cie .getArgument ( 5 ) .( BooleanLiteral ) .getBooleanValue ( ) = true // NewCookie(Cookie cookie, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
120
+ or
121
+ cie .getNumArgument ( ) = 8 and
122
+ cie .getArgument ( 6 ) .getType ( ) instanceof BooleanType and
123
+ cie .getArgument ( 7 ) .( BooleanLiteral ) .getBooleanValue ( ) = true // NewCookie(String name, String value, String path, String domain, String comment, int maxAge, boolean secure, boolean httpOnly)
124
+ or
125
+ 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)
125
126
)
126
127
}
127
128
@@ -145,7 +146,10 @@ predicate isTestMethod(DataFlow::Node node) {
145
146
)
146
147
}
147
148
148
- /** A taint configuration tracking flow from a sensitive cookie without HttpOnly flag set to its HTTP response. */
149
+ /**
150
+ * A taint configuration tracking flow from a sensitive cookie without the `HttpOnly` flag
151
+ * set to its HTTP response.
152
+ */
149
153
class MissingHttpOnlyConfiguration extends TaintTracking:: Configuration {
150
154
MissingHttpOnlyConfiguration ( ) { this = "MissingHttpOnlyConfiguration" }
151
155
@@ -159,25 +163,17 @@ class MissingHttpOnlyConfiguration extends TaintTracking::Configuration {
159
163
// cookie.setHttpOnly(true)
160
164
setHttpOnlyMethodAccess ( node )
161
165
or
162
- // response.addHeader("Set-Cookie: token=" +authId + ";HttpOnly;Secure")
166
+ // response.addHeader("Set-Cookie", " token=" +authId + ";HttpOnly;Secure")
163
167
setHttpOnlyInSetCookie ( node )
164
168
or
165
169
// new NewCookie("session-access-key", accessKey, "/", null, null, 0, true, true)
166
- setHttpOnlyInNewCookie ( node )
170
+ setHttpOnlyInNewCookie ( node . asExpr ( ) )
167
171
or
168
172
// Test class or method
169
173
isTestMethod ( node )
170
174
}
171
175
172
176
override predicate isAdditionalTaintStep ( DataFlow:: Node pred , DataFlow:: Node succ ) {
173
- exists (
174
- ClassInstanceExpr cie // `NewCookie` constructor
175
- |
176
- cie .getAnArgument ( ) = pred .asExpr ( ) and
177
- cie = succ .asExpr ( ) and
178
- cie .getConstructor ( ) .getDeclaringType ( ) .hasName ( "NewCookie" )
179
- )
180
- or
181
177
exists (
182
178
MethodAccess ma // `toString` call on a cookie object
183
179
|
0 commit comments