@@ -56,6 +56,46 @@ module CookieWrites {
5656 string httpOnly ( ) { result = "httpOnly" }
5757}
5858
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+
5999/**
60100 * A model of the `js-cookie` library (https://github.com/js-cookie/js-cookie).
61101 */
@@ -90,11 +130,7 @@ private module JsCookie {
90130 this .getOptionArgument ( 2 , CookieWrites:: secure ( ) ) .mayHaveBooleanValue ( true )
91131 }
92132
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 ) ) }
98134 }
99135}
100136
@@ -133,11 +169,7 @@ private module BrowserCookies {
133169 exists ( DataFlow:: moduleMember ( "browser-cookies" , "defaults" ) .getAPropertyWrite ( "secure" ) )
134170 }
135171
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 ) ) }
141173 }
142174}
143175
@@ -173,11 +205,7 @@ private module LibCookie {
173205 this .getOptionArgument ( 2 , CookieWrites:: secure ( ) ) .mayHaveBooleanValue ( true )
174206 }
175207
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 ) ) }
181209 }
182210}
183211
@@ -198,13 +226,7 @@ private module ExpressCookies {
198226 this .getOptionArgument ( 2 , CookieWrites:: secure ( ) ) .mayHaveBooleanValue ( true )
199227 }
200228
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 ) ) }
208230
209231 override predicate isHttpOnly ( ) {
210232 // 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 {
307329 hasCookieAttribute ( header , CookieWrites:: httpOnly ( ) )
308330 }
309331
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 ) }
337333}
338334
339335/**
340336 * A write to `document.cookie`.
341337 */
342338private class DocumentCookieWrite extends CookieWrites:: ClientSideCookieWrite {
343339 string cookie ;
340+ DataFlow:: PropWrite write ;
344341
345342 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+ ]
354350 }
355351
356352 override predicate isSecure ( ) {
@@ -359,7 +355,5 @@ private class DocumentCookieWrite extends CookieWrites::ClientSideCookieWrite {
359355 hasCookieAttribute ( cookie , CookieWrites:: secure ( ) )
360356 }
361357
362- override predicate isSensitive ( ) {
363- HeuristicNames:: nameIndicatesSensitiveData ( getCookieName ( cookie ) , _)
364- }
358+ override predicate isSensitive ( ) { canHaveSensitiveCookie ( write .getRhs ( ) ) }
365359}
0 commit comments