@@ -25,7 +25,7 @@ import semmle.code.java.dataflow.FlowSteps
25
25
import semmle.code.java.frameworks.Servlets
26
26
import semmle.code.java.dataflow.TaintTracking
27
27
import semmle.code.java.dataflow.TaintTracking2
28
- import DataFlow :: PathGraph
28
+ import MissingHttpOnlyFlow :: PathGraph
29
29
30
30
/** Gets a regular expression for matching common names of sensitive cookies. */
31
31
string getSensitiveCookieNameRegex ( ) { result = "(?i).*(auth|session|token|key|credential).*" }
@@ -65,18 +65,18 @@ class SetCookieMethodAccess extends MethodAccess {
65
65
* A taint configuration tracking flow from the text `httponly` to argument 1 of
66
66
* `SetCookieMethodAccess`.
67
67
*/
68
- class MatchesHttpOnlyConfiguration extends TaintTracking2:: Configuration {
69
- MatchesHttpOnlyConfiguration ( ) { this = "MatchesHttpOnlyConfiguration" }
70
-
71
- override predicate isSource ( DataFlow:: Node source ) {
68
+ module MatchesHttpOnlyConfig implements DataFlow:: ConfigSig {
69
+ predicate isSource ( DataFlow:: Node source ) {
72
70
source .asExpr ( ) .( CompileTimeConstantExpr ) .getStringValue ( ) .toLowerCase ( ) .matches ( "%httponly%" )
73
71
}
74
72
75
- override predicate isSink ( DataFlow:: Node sink ) {
73
+ predicate isSink ( DataFlow:: Node sink ) {
76
74
sink .asExpr ( ) = any ( SetCookieMethodAccess ma ) .getArgument ( 1 )
77
75
}
78
76
}
79
77
78
+ module MatchesHttpOnlyFlow = TaintTracking:: Global< MatchesHttpOnlyConfig > ;
79
+
80
80
/** A class descended from `javax.servlet.http.Cookie`. */
81
81
class CookieClass extends RefType {
82
82
CookieClass ( ) { this .getAnAncestor ( ) .hasQualifiedName ( "javax.servlet.http" , "Cookie" ) }
@@ -126,20 +126,21 @@ predicate isTestMethod(MethodAccess ma) {
126
126
* A taint configuration tracking flow of a method that sets the `HttpOnly` flag,
127
127
* or one that removes a cookie, to a `ServletResponse.addCookie` call.
128
128
*/
129
- class SetHttpOnlyOrRemovesCookieConfiguration extends TaintTracking2:: Configuration {
130
- SetHttpOnlyOrRemovesCookieConfiguration ( ) { this = "SetHttpOnlyOrRemovesCookieConfiguration" }
131
-
132
- override predicate isSource ( DataFlow:: Node source ) {
129
+ module SetHttpOnlyOrRemovesCookieConfiguration implements DataFlow:: ConfigSig {
130
+ predicate isSource ( DataFlow:: Node source ) {
133
131
source .asExpr ( ) =
134
132
any ( MethodAccess ma | setsCookieHttpOnly ( ma ) or removesCookie ( ma ) ) .getQualifier ( )
135
133
}
136
134
137
- override predicate isSink ( DataFlow:: Node sink ) {
135
+ predicate isSink ( DataFlow:: Node sink ) {
138
136
sink .asExpr ( ) =
139
137
any ( MethodAccess ma | ma .getMethod ( ) instanceof ResponseAddCookieMethod ) .getArgument ( 0 )
140
138
}
141
139
}
142
140
141
+ module SetHttpOnlyOrRemovesCookieFlow =
142
+ TaintTracking:: Global< SetHttpOnlyOrRemovesCookieConfiguration > ;
143
+
143
144
/**
144
145
* A cookie that is added to an HTTP response and which doesn't have `httpOnly` set, used as a sink
145
146
* in `MissingHttpOnlyConfiguration`.
@@ -150,11 +151,11 @@ class CookieResponseSink extends DataFlow::ExprNode {
150
151
(
151
152
ma .getMethod ( ) instanceof ResponseAddCookieMethod and
152
153
this .getExpr ( ) = ma .getArgument ( 0 ) and
153
- not exists ( SetHttpOnlyOrRemovesCookieConfiguration cc | cc . hasFlowTo ( this ) )
154
+ not SetHttpOnlyOrRemovesCookieFlow :: flowTo ( this )
154
155
or
155
156
ma instanceof SetCookieMethodAccess and
156
157
this .getExpr ( ) = ma .getArgument ( 1 ) and
157
- not exists ( MatchesHttpOnlyConfiguration cc | cc . hasFlowTo ( this ) ) // response.addHeader("Set-Cookie", "token=" +authId + ";HttpOnly;Secure")
158
+ not MatchesHttpOnlyFlow :: flowTo ( this ) // response.addHeader("Set-Cookie", "token=" +authId + ";HttpOnly;Secure")
158
159
) and
159
160
not isTestMethod ( ma ) // Test class or method
160
161
)
@@ -181,21 +182,17 @@ predicate setsHttpOnlyInNewCookie(ClassInstanceExpr cie) {
181
182
* A taint configuration tracking flow from a sensitive cookie without the `HttpOnly` flag
182
183
* set to its HTTP response.
183
184
*/
184
- class MissingHttpOnlyConfiguration extends TaintTracking :: Configuration {
185
- MissingHttpOnlyConfiguration ( ) { this = "MissingHttpOnlyConfiguration" }
185
+ module MissingHttpOnlyConfig implements DataFlow :: ConfigSig {
186
+ predicate isSource ( DataFlow :: Node source ) { source . asExpr ( ) instanceof SensitiveCookieNameExpr }
186
187
187
- override predicate isSource ( DataFlow:: Node source ) {
188
- source .asExpr ( ) instanceof SensitiveCookieNameExpr
189
- }
190
-
191
- override predicate isSink ( DataFlow:: Node sink ) { sink instanceof CookieResponseSink }
188
+ predicate isSink ( DataFlow:: Node sink ) { sink instanceof CookieResponseSink }
192
189
193
- override predicate isSanitizer ( DataFlow:: Node node ) {
190
+ predicate isBarrier ( DataFlow:: Node node ) {
194
191
// JAX-RS's `new NewCookie("session-access-key", accessKey, "/", null, null, 0, true, true)` and similar
195
192
setsHttpOnlyInNewCookie ( node .asExpr ( ) )
196
193
}
197
194
198
- override predicate isAdditionalTaintStep ( DataFlow:: Node pred , DataFlow:: Node succ ) {
195
+ predicate isAdditionalFlowStep ( DataFlow:: Node pred , DataFlow:: Node succ ) {
199
196
exists (
200
197
ConstructorCall cc // new Cookie(...)
201
198
|
@@ -215,7 +212,9 @@ class MissingHttpOnlyConfiguration extends TaintTracking::Configuration {
215
212
}
216
213
}
217
214
218
- from DataFlow:: PathNode source , DataFlow:: PathNode sink , MissingHttpOnlyConfiguration c
219
- where c .hasFlowPath ( source , sink )
215
+ module MissingHttpOnlyFlow = TaintTracking:: Global< MissingHttpOnlyConfig > ;
216
+
217
+ from MissingHttpOnlyFlow:: PathNode source , MissingHttpOnlyFlow:: PathNode sink
218
+ where MissingHttpOnlyFlow:: flowPath ( source , sink )
220
219
select sink .getNode ( ) , source , sink , "$@ doesn't have the HttpOnly flag set." , source .getNode ( ) ,
221
220
"This sensitive cookie"
0 commit comments