@@ -25,7 +25,7 @@ import semmle.code.java.dataflow.FlowSteps
2525import semmle.code.java.frameworks.Servlets
2626import semmle.code.java.dataflow.TaintTracking
2727import semmle.code.java.dataflow.TaintTracking2
28- import DataFlow :: PathGraph
28+ import MissingHttpOnlyFlow :: PathGraph
2929
3030/** Gets a regular expression for matching common names of sensitive cookies. */
3131string getSensitiveCookieNameRegex ( ) { result = "(?i).*(auth|session|token|key|credential).*" }
@@ -65,18 +65,18 @@ class SetCookieMethodAccess extends MethodAccess {
6565 * A taint configuration tracking flow from the text `httponly` to argument 1 of
6666 * `SetCookieMethodAccess`.
6767 */
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 ) {
7270 source .asExpr ( ) .( CompileTimeConstantExpr ) .getStringValue ( ) .toLowerCase ( ) .matches ( "%httponly%" )
7371 }
7472
75- override predicate isSink ( DataFlow:: Node sink ) {
73+ predicate isSink ( DataFlow:: Node sink ) {
7674 sink .asExpr ( ) = any ( SetCookieMethodAccess ma ) .getArgument ( 1 )
7775 }
7876}
7977
78+ module MatchesHttpOnlyFlow = TaintTracking:: Global< MatchesHttpOnlyConfig > ;
79+
8080/** A class descended from `javax.servlet.http.Cookie`. */
8181class CookieClass extends RefType {
8282 CookieClass ( ) { this .getAnAncestor ( ) .hasQualifiedName ( "javax.servlet.http" , "Cookie" ) }
@@ -126,20 +126,21 @@ predicate isTestMethod(MethodAccess ma) {
126126 * A taint configuration tracking flow of a method that sets the `HttpOnly` flag,
127127 * or one that removes a cookie, to a `ServletResponse.addCookie` call.
128128 */
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 ) {
133131 source .asExpr ( ) =
134132 any ( MethodAccess ma | setsCookieHttpOnly ( ma ) or removesCookie ( ma ) ) .getQualifier ( )
135133 }
136134
137- override predicate isSink ( DataFlow:: Node sink ) {
135+ predicate isSink ( DataFlow:: Node sink ) {
138136 sink .asExpr ( ) =
139137 any ( MethodAccess ma | ma .getMethod ( ) instanceof ResponseAddCookieMethod ) .getArgument ( 0 )
140138 }
141139}
142140
141+ module SetHttpOnlyOrRemovesCookieFlow =
142+ TaintTracking:: Global< SetHttpOnlyOrRemovesCookieConfiguration > ;
143+
143144/**
144145 * A cookie that is added to an HTTP response and which doesn't have `httpOnly` set, used as a sink
145146 * in `MissingHttpOnlyConfiguration`.
@@ -150,11 +151,11 @@ class CookieResponseSink extends DataFlow::ExprNode {
150151 (
151152 ma .getMethod ( ) instanceof ResponseAddCookieMethod and
152153 this .getExpr ( ) = ma .getArgument ( 0 ) and
153- not exists ( SetHttpOnlyOrRemovesCookieConfiguration cc | cc . hasFlowTo ( this ) )
154+ not SetHttpOnlyOrRemovesCookieFlow :: flowTo ( this )
154155 or
155156 ma instanceof SetCookieMethodAccess and
156157 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")
158159 ) and
159160 not isTestMethod ( ma ) // Test class or method
160161 )
@@ -181,21 +182,17 @@ predicate setsHttpOnlyInNewCookie(ClassInstanceExpr cie) {
181182 * A taint configuration tracking flow from a sensitive cookie without the `HttpOnly` flag
182183 * set to its HTTP response.
183184 */
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 }
186187
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 }
192189
193- override predicate isSanitizer ( DataFlow:: Node node ) {
190+ predicate isBarrier ( DataFlow:: Node node ) {
194191 // JAX-RS's `new NewCookie("session-access-key", accessKey, "/", null, null, 0, true, true)` and similar
195192 setsHttpOnlyInNewCookie ( node .asExpr ( ) )
196193 }
197194
198- override predicate isAdditionalTaintStep ( DataFlow:: Node pred , DataFlow:: Node succ ) {
195+ predicate isAdditionalFlowStep ( DataFlow:: Node pred , DataFlow:: Node succ ) {
199196 exists (
200197 ConstructorCall cc // new Cookie(...)
201198 |
@@ -215,7 +212,9 @@ class MissingHttpOnlyConfiguration extends TaintTracking::Configuration {
215212 }
216213}
217214
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 )
220219select sink .getNode ( ) , source , sink , "$@ doesn't have the HttpOnly flag set." , source .getNode ( ) ,
221220 "This sensitive cookie"
0 commit comments