Implement cookie attributes for cases in which a raw header is set

This commit is contained in:
Joe Farebrother
2024-07-08 22:37:54 +01:00
parent 2df09f6194
commit 9ad6c8c5eb
3 changed files with 56 additions and 90 deletions

View File

@@ -1250,17 +1250,70 @@ module Http {
/**
* Holds if the `Secure` flag of the cookie is known to have a value of `b`.
*/
predicate hasSecureFlag(boolean b) { none() }
predicate hasSecureFlag(boolean b) {
exists(this.getHeaderArg()) and
(
exists(StringLiteral sl |
sl.getText().regexpMatch("(?i).*;\\s*secure;.*") and
TaintTracking::localTaint(DataFlow::exprNode(sl), this.getHeaderArg()) and
b = true
)
or
exists(StringLiteral sl |
not sl.getText().regexpMatch("(?i).*;\\s*secure;.*") and
DataFlow::localFlow(DataFlow::exprNode(sl), this.getHeaderArg()) and
b = false
)
)
}
/**
* Holds if the `HttpOnly` flag of the cookie is known to have a value of `b`.
*/
predicate hasHttpOnlyFlag(boolean b) { none() }
predicate hasHttpOnlyFlag(boolean b) {
exists(this.getHeaderArg()) and
(
exists(StringLiteral sl |
sl.getText().regexpMatch("(?i).*;\\s*httponly;.*") and
TaintTracking::localTaint(DataFlow::exprNode(sl), this.getHeaderArg()) and
b = true
)
or
exists(StringLiteral sl |
not sl.getText().regexpMatch("(?i).*;\\s*httponly;.*") and
DataFlow::localFlow(DataFlow::exprNode(sl), this.getHeaderArg()) and
b = false
)
)
}
/**
* Holds if the `SameSite` flag of the cookie is known to have a value of `b`.
*/
predicate hasSameSiteFlag(boolean b) { none() }
// TODO: b could be a newtype with 3 values indicating Strict,Lax,or None
// currently, Strict and Lax are represented with true and None is represented with false.
predicate hasSameSiteFlag(boolean b) {
exists(this.getHeaderArg()) and
(
exists(StringLiteral sl |
sl.getText().regexpMatch("(?i).*;\\s*samesite=(strict|lax);.*") and
TaintTracking::localTaint(DataFlow::exprNode(sl), this.getHeaderArg()) and
b = true
)
or
exists(StringLiteral sl |
sl.getText().regexpMatch("(?i).*;\\s*samesite=none;.*") and
TaintTracking::localTaint(DataFlow::exprNode(sl), this.getHeaderArg()) and
b = false
)
or
exists(StringLiteral sl |
not sl.getText().regexpMatch("(?i).*;\\s*samesite=(strict|lax|none);.*") and
DataFlow::localFlow(DataFlow::exprNode(sl), this.getHeaderArg()) and
b = true // Lax is the default
)
)
}
}
}

View File

@@ -17,7 +17,6 @@ import python
import semmle.python.dataflow.new.DataFlow
import experimental.semmle.python.Concepts
import semmle.python.Concepts
import experimental.semmle.python.CookieHeader
from Http::Server::CookieWrite cookie, string alert
where

View File

@@ -1,86 +0,0 @@
/**
* Temporary: provides a class to extend current cookies to header declarations
*/
import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.TaintTracking
import experimental.semmle.python.Concepts
import semmle.python.Concepts
/**
* Gets a header setting a cookie.
*
* Given the following example:
*
* ```py
* @app.route("/")
* def flask_make_response():
* resp = make_response("")
* resp.headers['Set-Cookie'] = "name=value; Secure;"
* return resp
* ```
*
* * `this` would be `resp.headers['Set-Cookie'] = "name=value; Secure;"`.
* * `isSecure()` predicate would succeed.
* * `isHttpOnly()` predicate would fail.
* * `isSameSite()` predicate would fail.
* * `getName()` and `getValue()` results would be `"name=value; Secure;"`.
*/
class CookieHeader extends Http::Server::CookieWrite::Range instanceof Http::Server::ResponseHeaderWrite
{
CookieHeader() {
exists(StringLiteral str |
str.getText() = "Set-Cookie" and
DataFlow::exprNode(str)
.(DataFlow::LocalSourceNode)
.flowsTo(this.(Http::Server::ResponseHeaderWrite).getNameArg())
)
}
override predicate hasSecureFlag(boolean b) {
if
exists(StringLiteral str |
str.getText().regexpMatch(".*; *Secure;.*") and
DataFlow::exprNode(str)
.(DataFlow::LocalSourceNode)
.flowsTo(this.(Http::Server::ResponseHeaderWrite).getValueArg())
)
then b = true
else b = false
}
override predicate hasHttpOnlyFlag(boolean b) {
if
exists(StringLiteral str |
str.getText().regexpMatch(".*; *HttpOnly;.*") and
DataFlow::exprNode(str)
.(DataFlow::LocalSourceNode)
.flowsTo(this.(Http::Server::ResponseHeaderWrite).getValueArg())
)
then b = true
else b = false
}
override predicate hasSameSiteFlag(boolean b) {
if
exists(StringLiteral str |
str.getText().regexpMatch(".*; *SameSite=(Strict|Lax);.*") and
DataFlow::exprNode(str)
.(DataFlow::LocalSourceNode)
.flowsTo(this.(Http::Server::ResponseHeaderWrite).getValueArg())
)
then b = true
else b = false
}
override DataFlow::Node getNameArg() {
result = this.(Http::Server::ResponseHeaderWrite).getValueArg()
}
override DataFlow::Node getValueArg() {
result = this.(Http::Server::ResponseHeaderWrite).getValueArg()
}
override DataFlow::Node getHeaderArg() { none() }
}