@@ -56,6 +56,46 @@ module CookieWrites {
56
56
string httpOnly ( ) { result = "httpOnly" }
57
57
}
58
58
59
+ /**
60
+ * Holds if `node` looks like it can contain a sensitive cookie.
61
+ * Either from `node` being a sensitive expression, or from `node` containing
62
+ * a string value that looks like a sensitive cookie name.
63
+ */
64
+ private predicate canHaveSensitiveCookie ( DataFlow:: Node node ) {
65
+ exists ( string s |
66
+ node .mayHaveStringValue ( s ) or
67
+ s = node .( StringOps:: ConcatenationRoot ) .getConstantStringParts ( )
68
+ |
69
+ HeuristicNames:: nameIndicatesSensitiveData ( [ s , getCookieName ( s ) ] , _)
70
+ )
71
+ or
72
+ node .asExpr ( ) instanceof SensitiveExpr
73
+ }
74
+
75
+ /**
76
+ * Gets cookie name from a `Set-Cookie` header value.
77
+ * The header value always starts with `<cookie-name>=<cookie-value>` optionally followed by attributes:
78
+ * `<cookie-name>=<cookie-value>; Domain=<domain-value>; Secure; HttpOnly`
79
+ */
80
+ bindingset [ s]
81
+ private string getCookieName ( string s ) {
82
+ result = s .regexpCapture ( "\\s*\\b([^=\\s]*)\\b\\s*=.*" , 1 )
83
+ }
84
+
85
+ /**
86
+ * Holds if the `Set-Cookie` header value contains the specified attribute
87
+ * 1. The attribute is case insensitive
88
+ * 2. It always starts with a pair `<cookie-name>=<cookie-value>`.
89
+ * If the attribute is present there must be `;` after the pair.
90
+ * Other attributes like `Domain=`, `Path=`, etc. may come after the pair:
91
+ * `<cookie-name>=<cookie-value>; Domain=<domain-value>; Secure; HttpOnly`
92
+ * See `https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie`
93
+ */
94
+ bindingset [ s, attribute]
95
+ private predicate hasCookieAttribute ( string s , string attribute ) {
96
+ s .regexpMatch ( "(?i).*;\\s*" + attribute + "\\b\\s*;?.*$" )
97
+ }
98
+
59
99
/**
60
100
* A model of the `js-cookie` library (https://github.com/js-cookie/js-cookie).
61
101
*/
@@ -90,11 +130,7 @@ private module JsCookie {
90
130
this .getOptionArgument ( 2 , CookieWrites:: secure ( ) ) .mayHaveBooleanValue ( true )
91
131
}
92
132
93
- override predicate isSensitive ( ) {
94
- HeuristicNames:: nameIndicatesSensitiveData ( any ( string s |
95
- this .getArgument ( 0 ) .mayHaveStringValue ( s )
96
- ) , _)
97
- }
133
+ override predicate isSensitive ( ) { canHaveSensitiveCookie ( this .getArgument ( 0 ) ) }
98
134
}
99
135
}
100
136
@@ -133,11 +169,7 @@ private module BrowserCookies {
133
169
exists ( DataFlow:: moduleMember ( "browser-cookies" , "defaults" ) .getAPropertyWrite ( "secure" ) )
134
170
}
135
171
136
- override predicate isSensitive ( ) {
137
- HeuristicNames:: nameIndicatesSensitiveData ( any ( string s |
138
- this .getArgument ( 0 ) .mayHaveStringValue ( s )
139
- ) , _)
140
- }
172
+ override predicate isSensitive ( ) { canHaveSensitiveCookie ( this .getArgument ( 0 ) ) }
141
173
}
142
174
}
143
175
@@ -173,11 +205,7 @@ private module LibCookie {
173
205
this .getOptionArgument ( 2 , CookieWrites:: secure ( ) ) .mayHaveBooleanValue ( true )
174
206
}
175
207
176
- override predicate isSensitive ( ) {
177
- HeuristicNames:: nameIndicatesSensitiveData ( any ( string s |
178
- this .getArgument ( 0 ) .mayHaveStringValue ( s )
179
- ) , _)
180
- }
208
+ override predicate isSensitive ( ) { canHaveSensitiveCookie ( this .getArgument ( 0 ) ) }
181
209
}
182
210
}
183
211
@@ -198,13 +226,7 @@ private module ExpressCookies {
198
226
this .getOptionArgument ( 2 , CookieWrites:: secure ( ) ) .mayHaveBooleanValue ( true )
199
227
}
200
228
201
- override predicate isSensitive ( ) {
202
- HeuristicNames:: nameIndicatesSensitiveData ( any ( string s |
203
- this .getArgument ( 0 ) .mayHaveStringValue ( s )
204
- ) , _)
205
- or
206
- this .getArgument ( 0 ) .asExpr ( ) instanceof SensitiveExpr
207
- }
229
+ override predicate isSensitive ( ) { canHaveSensitiveCookie ( this .getArgument ( 0 ) ) }
208
230
209
231
override predicate isHttpOnly ( ) {
210
232
// A cookie is httpOnly if there are cookie options with the `httpOnly` flag set to `true`.
@@ -307,50 +329,24 @@ private class HTTPCookieWrite extends CookieWrites::CookieWrite {
307
329
hasCookieAttribute ( header , CookieWrites:: httpOnly ( ) )
308
330
}
309
331
310
- override predicate isSensitive ( ) {
311
- HeuristicNames:: nameIndicatesSensitiveData ( getCookieName ( header ) , _)
312
- }
313
- }
314
-
315
- /**
316
- * Gets cookie name from a `Set-Cookie` header value.
317
- * The header value always starts with `<cookie-name>=<cookie-value>` optionally followed by attributes:
318
- * `<cookie-name>=<cookie-value>; Domain=<domain-value>; Secure; HttpOnly`
319
- */
320
- bindingset [ s]
321
- private string getCookieName ( string s ) {
322
- result = s .regexpCapture ( "\\s*\\b([^=\\s]*)\\b\\s*=.*" , 1 )
323
- }
324
-
325
- /**
326
- * Holds if the `Set-Cookie` header value contains the specified attribute
327
- * 1. The attribute is case insensitive
328
- * 2. It always starts with a pair `<cookie-name>=<cookie-value>`.
329
- * If the attribute is present there must be `;` after the pair.
330
- * Other attributes like `Domain=`, `Path=`, etc. may come after the pair:
331
- * `<cookie-name>=<cookie-value>; Domain=<domain-value>; Secure; HttpOnly`
332
- * See `https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie`
333
- */
334
- bindingset [ s, attribute]
335
- private predicate hasCookieAttribute ( string s , string attribute ) {
336
- s .regexpMatch ( "(?i).*;\\s*" + attribute + "\\b\\s*;?.*$" )
332
+ override predicate isSensitive ( ) { canHaveSensitiveCookie ( this ) }
337
333
}
338
334
339
335
/**
340
336
* A write to `document.cookie`.
341
337
*/
342
338
private class DocumentCookieWrite extends CookieWrites:: ClientSideCookieWrite {
343
339
string cookie ;
340
+ DataFlow:: PropWrite write ;
344
341
345
342
DocumentCookieWrite ( ) {
346
- exists ( DataFlow:: PropWrite write | this = write |
347
- write = DOM:: documentRef ( ) .getAPropertyWrite ( "cookie" ) and
348
- cookie =
349
- [
350
- any ( string s | write .getRhs ( ) .mayHaveStringValue ( s ) ) ,
351
- write .getRhs ( ) .( StringOps:: ConcatenationRoot ) .getConstantStringParts ( )
352
- ]
353
- )
343
+ this = write and
344
+ write = DOM:: documentRef ( ) .getAPropertyWrite ( "cookie" ) and
345
+ cookie =
346
+ [
347
+ any ( string s | write .getRhs ( ) .mayHaveStringValue ( s ) ) ,
348
+ write .getRhs ( ) .( StringOps:: ConcatenationRoot ) .getConstantStringParts ( )
349
+ ]
354
350
}
355
351
356
352
override predicate isSecure ( ) {
@@ -359,7 +355,5 @@ private class DocumentCookieWrite extends CookieWrites::ClientSideCookieWrite {
359
355
hasCookieAttribute ( cookie , CookieWrites:: secure ( ) )
360
356
}
361
357
362
- override predicate isSensitive ( ) {
363
- HeuristicNames:: nameIndicatesSensitiveData ( getCookieName ( cookie ) , _)
364
- }
358
+ override predicate isSensitive ( ) { canHaveSensitiveCookie ( write .getRhs ( ) ) }
365
359
}
0 commit comments