Skip to content

Commit bd99114

Browse files
author
edvraa
committed
Comments added
1 parent a24c1c8 commit bd99114

File tree

1 file changed

+48
-16
lines changed

1 file changed

+48
-16
lines changed

javascript/ql/src/semmle/javascript/security/InsecureCookie.qll

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,10 @@ module Cookie {
169169
}
170170

171171
/**
172-
* A cookie set using `Set-Cookie` header of an `HTTP` response (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie).
172+
* A cookie set using `Set-Cookie` header of an `HTTP` response.
173+
* (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie).
174+
* In case an array is passed `setHeader("Set-Cookie", [...]` it sets multiple cookies.
175+
* Each array element has its own attributes.
173176
*/
174177
class InsecureSetCookieHeader extends Cookie {
175178
InsecureSetCookieHeader() {
@@ -184,38 +187,67 @@ module Cookie {
184187
else result.asExpr() = this.asExpr()
185188
}
186189

187-
override predicate isSecure() {
188-
// A cookie is secure if the 'secure' flag is specified in the cookie definition.
189-
// The default is `false`.
190+
/**
191+
* A cookie is secure if the `secure` flag is specified in the cookie definition.
192+
* The default is `false`.
193+
*/
194+
override predicate isSecure() { allHaveCookieAttribute("secure") }
195+
196+
/**
197+
* A cookie is httpOnly if the `httpOnly` flag is specified in the cookie definition.
198+
* The default is `false`.
199+
*/
200+
override predicate isHttpOnly() { allHaveCookieAttribute("httponly") }
201+
202+
/**
203+
* The predicate holds only if all elements have the specified attribute.
204+
*/
205+
bindingset[attribute]
206+
private predicate allHaveCookieAttribute(string attribute) {
190207
forall(DataFlow::Node n | n = getCookieOptionsArgument() |
191208
exists(string s |
192209
n.mayHaveStringValue(s) and
193-
s.regexpMatch("(?i).*;\\s*secure\\s*;?.*$")
210+
hasCookieAttribute(s, attribute)
194211
)
195212
)
196213
}
197214

215+
/**
216+
* The predicate holds only if any element has a sensitive name and
217+
* doesn't have the `httpOnly` flag.
218+
*/
198219
override predicate isAuthNotHttpOnly() {
199220
exists(DataFlow::Node n | n = getCookieOptionsArgument() |
200221
exists(string s |
201222
n.mayHaveStringValue(s) and
202223
(
203-
not s.regexpMatch("(?i).*;\\s*httponly\\s*;?.*$") and
204-
regexpMatchAuth(s.regexpCapture("\\s*([^=\\s]*)\\s*=.*", 1))
224+
not hasCookieAttribute(s, "httponly") and
225+
regexpMatchAuth(getCookieName(s))
205226
)
206227
)
207228
)
208229
}
209230

210-
override predicate isHttpOnly() {
211-
// A cookie is httpOnly if the 'httpOnly' flag is specified in the cookie definition.
212-
// The default is `false`.
213-
forall(DataFlow::Node n | n = getCookieOptionsArgument() |
214-
exists(string s |
215-
n.mayHaveStringValue(s) and
216-
s.regexpMatch("(?i).*;\\s*httponly\\s*;?.*$")
217-
)
218-
)
231+
/**
232+
* Gets cookie name from a `Set-Cookie` header value.
233+
* The header value always starts with `<cookie-name>=<cookie-value>` optionally followed by attributes:
234+
* `<cookie-name>=<cookie-value>; Domain=<domain-value>; Secure; HttpOnly`
235+
*/
236+
bindingset[s]
237+
private string getCookieName(string s) { result = s.regexpCapture("\\s*([^=\\s]*)\\s*=.*", 1) }
238+
239+
/**
240+
* Holds if the `Set-Cookie` header value contains the specified attribute
241+
* 1. The attribute is case insensitive
242+
* 2. It always starts with a pair `<cookie-name>=<cookie-value>`.
243+
* If the attribute is present there must be `;` after the pair.
244+
* Other attributes like `Domain=`, `Path=`, etc. may come after the pair:
245+
* `<cookie-name>=<cookie-value>; Domain=<domain-value>; Secure; HttpOnly`
246+
* See `https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie`
247+
*/
248+
bindingset[s, attribute]
249+
private predicate hasCookieAttribute(string s, string attribute) {
250+
s.regexpMatch("(?i).*;\\s*" + attribute + "\\s*;?.*$")
219251
}
220252
}
221253

0 commit comments

Comments
 (0)