Merge pull request #7721 from erik-krogh/CWE-1275

JS: add a js/samesite-none-cookie cookie
This commit is contained in:
Erik Krogh Kristensen
2022-01-25 13:28:08 +01:00
committed by GitHub
10 changed files with 293 additions and 3 deletions

View File

@@ -27,6 +27,12 @@ module CookieWrites {
*/
abstract predicate isSensitive();
/**
* Gets the SameSite attribute of the cookie if present.
* Either "Strict", "Lax" or "None".
*/
abstract string getSameSite();
/**
* Holds if the cookie write happens on a server, i.e. the `httpOnly` flag is relevant.
*/
@@ -95,6 +101,31 @@ private predicate hasCookieAttribute(string s, string attribute) {
s.regexpMatch("(?i).*;\\s*" + attribute + "\\b\\s*;?.*$")
}
/**
* Gets the value for a `Set-Cookie` header attribute.
*/
bindingset[s, attribute]
private string getCookieValue(string s, string attribute) {
result = s.regexpCapture("(?i).*;\\s*" + attribute + "=(\\w+)\\b\\s*;?.*$", 1)
}
/**
* Gets the "SameSite" value for a given `node`.
* Converts boolean values to the corresponding string value.
*
* Not all libraries support boolean values for the `SameSite` attribute,
* but here we assume that they do.
*/
private string getSameSiteValue(DataFlow::Node node) {
node.mayHaveStringValue(result)
or
node.mayHaveBooleanValue(true) and
result = "Strict"
or
node.mayHaveBooleanValue(false) and
result = "Lax"
}
/**
* A model of the `js-cookie` library (https://github.com/js-cookie/js-cookie).
*/
@@ -105,7 +136,9 @@ private module JsCookie {
DataFlow::CallNode libMemberCall(string name) {
result = DataFlow::globalVarRef("Cookie").getAMemberCall(name) or
result = DataFlow::globalVarRef("Cookie").getAMemberCall("noConflict").getAMemberCall(name) or
result = DataFlow::moduleMember("js-cookie", name).getACall()
result = DataFlow::moduleMember("js-cookie", name).getACall() or
// es-cookie behaves basically the same as js-cookie
result = DataFlow::moduleMember("es-cookie", name).getACall()
}
class ReadAccess extends PersistentReadAccess, DataFlow::CallNode {
@@ -132,6 +165,10 @@ private module JsCookie {
}
override predicate isSensitive() { canHaveSensitiveCookie(this.getArgument(0)) }
override string getSameSite() {
result = getSameSiteValue(this.getOptionArgument(2, "sameSite"))
}
}
}
@@ -173,6 +210,15 @@ private module BrowserCookies {
}
override predicate isSensitive() { canHaveSensitiveCookie(this.getArgument(0)) }
override string getSameSite() {
result = getSameSiteValue(this.getOptionArgument(2, "samesite"))
or
// or, an explicit default has been set
DataFlow::moduleMember("browser-cookies", "defaults")
.getAPropertyWrite("samesite")
.mayHaveStringValue(result)
}
}
}
@@ -211,6 +257,10 @@ private module LibCookie {
}
override predicate isSensitive() { canHaveSensitiveCookie(this.getArgument(0)) }
override string getSameSite() {
result = getSameSiteValue(this.getOptionArgument(2, "sameSite"))
}
}
}
@@ -242,6 +292,10 @@ private module ExpressCookies {
not value.mayHaveBooleanValue(false) // anything but `false` is accepted as being maybe true
)
}
override string getSameSite() {
result = getSameSiteValue(this.getOptionArgument(2, "sameSite"))
}
}
/**
@@ -268,6 +322,8 @@ private module ExpressCookies {
// A cookie is httpOnly if the `httpOnly` flag is not explicitly set to `false`.
not this.getCookieFlagValue(CookieWrites::httpOnly()).mayHaveBooleanValue(false)
}
override string getSameSite() { result = getSameSiteValue(this.getCookieFlagValue("sameSite")) }
}
/**
@@ -297,6 +353,8 @@ private module ExpressCookies {
// A cookie is httpOnly if the `httpOnly` flag is not explicitly set to `false`.
not this.getCookieFlagValue(CookieWrites::httpOnly()).mayHaveBooleanValue(false)
}
override string getSameSite() { result = getSameSiteValue(this.getCookieFlagValue("sameSite")) }
}
}
@@ -339,6 +397,8 @@ private class HTTPCookieWrite extends CookieWrites::CookieWrite {
}
override predicate isSensitive() { canHaveSensitiveCookie(this) }
override string getSameSite() { result = getCookieValue(header, "SameSite") }
}
/**
@@ -365,4 +425,6 @@ private class DocumentCookieWrite extends CookieWrites::ClientSideCookieWrite {
}
override predicate isSensitive() { canHaveSensitiveCookie(write.getRhs()) }
override string getSameSite() { result = getCookieValue(cookie, "SameSite") }
}