Merge branch 'main' into python-cookie-concept-promote

This commit is contained in:
Joe Farebrother
2024-07-29 10:26:03 +01:00
302 changed files with 78830 additions and 1821 deletions

View File

@@ -1,3 +1,9 @@
## 1.0.4
### Minor Analysis Improvements
* Additional modelling to detect direct writes to the `Set-Cookie` header has been added for several web frameworks.
## 1.0.3
### Minor Analysis Improvements

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Additional modelling has been added to detect cookie writes from direct writes to the `Set-Cookie` header have been added for several web frameworks.

View File

@@ -0,0 +1,5 @@
## 1.0.4
### Minor Analysis Improvements
* Additional modelling to detect direct writes to the `Set-Cookie` header has been added for several web frameworks.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.0.3
lastReleaseVersion: 1.0.4

View File

@@ -1,5 +1,5 @@
name: codeql/python-all
version: 1.0.4-dev
version: 1.0.5-dev
groups: python
dbscheme: semmlecode.python.dbscheme
extractor: python

View File

@@ -534,7 +534,7 @@ newtype TDataFlowType = TAnyFlow()
class DataFlowType extends TDataFlowType {
/** Gets a textual representation of this element. */
string toString() { result = "DataFlowType" }
string toString() { result = "" }
}
/** A node that performs a type cast. */
@@ -578,9 +578,6 @@ DataFlowType getNodeType(Node node) {
exists(node)
}
/** Gets a string representation of a type returned by `getErasedRepr`. */
string ppReprType(DataFlowType t) { none() }
//--------
// Extra flow
//--------

View File

@@ -0,0 +1,48 @@
/**
* Provides default sources, sinks and sanitizers for detecting
* "cookie injection"
* vulnerabilities, as well as extension points for adding your own.
*/
private import python
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Concepts
private import semmle.python.dataflow.new.RemoteFlowSources
/**
* Provides default sources, sinks and sanitizers for detecting
* "cookie injection"
* vulnerabilities, as well as extension points for adding your own.
*/
module CookieInjection {
/**
* A data flow source for "cookie injection" vulnerabilities.
*/
abstract class Source extends DataFlow::Node { }
/**
* A data flow sink for "cookie injection" vulnerabilities.
*/
abstract class Sink extends DataFlow::Node { }
/**
* A sanitizer for "cookie injection" vulnerabilities.
*/
abstract class Sanitizer extends DataFlow::Node { }
/**
* A source of remote user input, considered as a flow source.
*/
class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { }
/**
* A write to a cookie, considered as a sink.
*/
class CookieWriteSink extends Sink {
CookieWriteSink() {
exists(Http::Server::CookieWrite cw |
this = [cw.getNameArg(), cw.getValueArg(), cw.getHeaderArg()]
)
}
}
}

View File

@@ -0,0 +1,26 @@
/**
* Provides a taint-tracking configuration for detecting "cookie injection" vulnerabilities.
*
* Note, for performance reasons: only import this file if
* `CookieInjectionFlow` is needed, otherwise
* `CookieInjectionCustomizations` should be imported instead.
*/
private import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.TaintTracking
import CookieInjectionCustomizations::CookieInjection
/**
* A taint-tracking configuration for detecting "cookie injection" vulnerabilities.
*/
module CookieInjectionConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}
/** Global taint-tracking for detecting "cookie injection" vulnerabilities. */
module CookieInjectionFlow = TaintTracking::Global<CookieInjectionConfig>;

View File

@@ -1,3 +1,7 @@
## 1.0.4
No user-facing changes.
## 1.0.3
### Minor Analysis Improvements

View File

@@ -0,0 +1,27 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Constructing cookies from user input can allow an attacker to control a user's cookie.
This may lead to a session fixation attack. Additionally, client code may not expect a cookie to contain attacker-controlled data, and fail to sanitize it for common vulnerabilities such as Cross Site Scripting (XSS).
An attacker manipulating the raw cookie header may additionally be able to set cookie attributes such as <code>HttpOnly</code> to insecure values.
</p>
</overview>
<recommendation>
<p>Do not use raw user input to construct cookies.</p>
</recommendation>
<example>
<p>In the following cases, a cookie is constructed for a Flask response using user input. The first uses <code>set_cookie</code>,
and the second sets a cookie's raw value through the <code>set-cookie</code> header.</p>
<sample src="examples/CookieInjection.py" />
</example>
<references>
<li>Wikipedia - <a href="https://en.wikipedia.org/wiki/Session_fixation">Session Fixation</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,20 @@
/**
* @name Construction of a cookie using user-supplied input.
* @description Constructing cookies from user input may allow an attacker to perform a Cookie Poisoning attack.
* @kind path-problem
* @problem.severity warning
* @precision high
* @security-severity 5.0
* @id py/cookie-injection
* @tags security
* external/cwe/cwe-20
*/
import python
import semmle.python.security.dataflow.CookieInjectionQuery
import CookieInjectionFlow::PathGraph
from CookieInjectionFlow::PathNode source, CookieInjectionFlow::PathNode sink
where CookieInjectionFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "Cookie is constructed from a $@.", source.getNode(),
"user-supplied input"

View File

@@ -2,15 +2,15 @@ from flask import request, make_response
@app.route("/1")
def true():
def set_cookie():
resp = make_response()
resp.set_cookie(request.args["name"],
resp.set_cookie(request.args["name"], # BAD: User input is used to set the cookie's name and value
value=request.args["name"])
return resp
@app.route("/2")
def flask_make_response():
resp = make_response("hello")
resp.headers['Set-Cookie'] = f"{request.args['name']}={request.args['name']};"
def set_cookie_header():
resp = make_response()
resp.headers['Set-Cookie'] = f"{request.args['name']}={request.args['name']};" # BAD: User input is used to set the raw cookie header.
return resp

View File

@@ -0,0 +1,4 @@
---
category: newQuery
---
* The `py/cookie-injection` query, originally contributed to the experimental query pack by @jorgectf, has been promoted to the main query pack. This query finds instances of cookies being constructed from user input.

View File

@@ -0,0 +1,3 @@
## 1.0.4
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.0.3
lastReleaseVersion: 1.0.4

View File

@@ -1,28 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Constructing cookies from user input may allow an attacker to perform a Cookie Poisoning attack.
It is possible, however, to perform other parameter-like attacks through cookie poisoning techniques,
such as SQL Injection, Directory Traversal, or Stealth Commanding, etc. Additionally,
cookie injection may relate to attempts to perform Access of Administrative Interface.
</p>
</overview>
<recommendation>
<p>Do not use raw user input to construct cookies.</p>
</recommendation>
<example>
<p>This example shows two ways of adding a cookie to a Flask response. The first way uses <code>set_cookie</code>'s
and the second sets a cookie's raw value through a header, both using user-supplied input.</p>
<sample src="CookieInjection.py" />
</example>
<references>
<li>Imperva: <a href="https://docs.imperva.com/bundle/on-premises-knowledgebase-reference-guide/page/cookie_injection.htm">Cookie injection</a>.</li>
</references>
</qhelp>

View File

@@ -1,26 +0,0 @@
/**
* @name Construction of a cookie using user-supplied input.
* @description Constructing cookies from user input may allow an attacker to perform a Cookie Poisoning attack.
* @kind path-problem
* @problem.severity error
* @id py/cookie-injection
* @tags security
* experimental
* external/cwe/cwe-614
*/
// determine precision above
import python
import semmle.python.dataflow.new.DataFlow
import experimental.semmle.python.Concepts
import experimental.semmle.python.security.injection.CookieInjection
import CookieInjectionFlow::PathGraph
from CookieInjectionFlow::PathNode source, CookieInjectionFlow::PathNode sink, string insecure
where
CookieInjectionFlow::flowPath(source, sink) and
if exists(sink.getNode().(CookieSink))
then insecure = ",and its " + sink.getNode().(CookieSink).getFlag() + " flag is not properly set."
else insecure = "."
select sink.getNode(), source, sink, "Cookie is constructed from a $@" + insecure, source.getNode(),
"user-supplied input"

View File

@@ -1,44 +0,0 @@
import python
import experimental.semmle.python.Concepts
import semmle.python.Concepts
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.TaintTracking
import semmle.python.dataflow.new.RemoteFlowSources
class CookieSink extends DataFlow::Node {
string flag;
CookieSink() {
exists(Http::Server::CookieWrite cookie |
this in [cookie.getNameArg(), cookie.getValueArg(), cookie.getHeaderArg()] and
(
cookie.hasSecureFlag(false) and
flag = "secure"
or
cookie.hasHttpOnlyFlag(false) and
flag = "httponly"
or
cookie.hasSameSiteAttribute(any(Http::Server::CookieWrite::SameSiteNone v)) and
flag = "samesite"
)
)
}
string getFlag() { result = flag }
}
/**
* A taint-tracking configuration for detecting Cookie injections.
*/
private module CookieInjectionConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
predicate isSink(DataFlow::Node sink) {
exists(Http::Server::CookieWrite c |
sink in [c.getNameArg(), c.getValueArg(), c.getHeaderArg()]
)
}
}
/** Global taint-tracking for detecting "Cookie injections" vulnerabilities. */
module CookieInjectionFlow = TaintTracking::Global<CookieInjectionConfig>;

View File

@@ -1,5 +1,5 @@
name: codeql/python-queries
version: 1.0.4-dev
version: 1.0.5-dev
groups:
- python
- queries

View File

@@ -1,45 +0,0 @@
edges
| django_bad.py:27:33:27:67 | ControlFlowNode for Attribute() | django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | provenance | |
| django_bad.py:27:71:27:106 | ControlFlowNode for Attribute() | django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | provenance | |
| flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | flask_bad.py:1:26:1:32 | ControlFlowNode for request | provenance | |
| flask_bad.py:1:26:1:32 | ControlFlowNode for request | flask_bad.py:24:21:24:27 | ControlFlowNode for request | provenance | |
| flask_bad.py:1:26:1:32 | ControlFlowNode for request | flask_bad.py:24:49:24:55 | ControlFlowNode for request | provenance | |
| flask_bad.py:1:26:1:32 | ControlFlowNode for request | flask_bad.py:32:37:32:43 | ControlFlowNode for request | provenance | |
| flask_bad.py:1:26:1:32 | ControlFlowNode for request | flask_bad.py:32:60:32:66 | ControlFlowNode for request | provenance | |
| flask_bad.py:24:21:24:27 | ControlFlowNode for request | flask_bad.py:24:21:24:40 | ControlFlowNode for Subscript | provenance | AdditionalTaintStep |
| flask_bad.py:24:21:24:27 | ControlFlowNode for request | flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | provenance | AdditionalTaintStep |
| flask_bad.py:24:49:24:55 | ControlFlowNode for request | flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | provenance | AdditionalTaintStep |
| flask_bad.py:32:37:32:43 | ControlFlowNode for request | flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | provenance | AdditionalTaintStep |
| flask_bad.py:32:60:32:66 | ControlFlowNode for request | flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | provenance | AdditionalTaintStep |
nodes
| django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | semmle.label | ControlFlowNode for Fstring |
| django_bad.py:27:33:27:67 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| django_bad.py:27:71:27:106 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember |
| flask_bad.py:1:26:1:32 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| flask_bad.py:24:21:24:27 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| flask_bad.py:24:21:24:40 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| flask_bad.py:24:49:24:55 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | semmle.label | ControlFlowNode for Fstring |
| flask_bad.py:32:37:32:43 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| flask_bad.py:32:60:32:66 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
subpaths
#select
| django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | Cookie is constructed from a $@,and its httponly flag is not properly set. | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | user-supplied input |
| django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | Cookie is constructed from a $@,and its samesite flag is not properly set. | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | user-supplied input |
| django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | Cookie is constructed from a $@,and its secure flag is not properly set. | django_bad.py:19:21:19:55 | ControlFlowNode for Attribute() | user-supplied input |
| django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | Cookie is constructed from a $@,and its httponly flag is not properly set. | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | user-supplied input |
| django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | Cookie is constructed from a $@,and its samesite flag is not properly set. | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | user-supplied input |
| django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | Cookie is constructed from a $@,and its secure flag is not properly set. | django_bad.py:20:21:20:56 | ControlFlowNode for Attribute() | user-supplied input |
| django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | django_bad.py:27:33:27:67 | ControlFlowNode for Attribute() | django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | Cookie is constructed from a $@,and its samesite flag is not properly set. | django_bad.py:27:33:27:67 | ControlFlowNode for Attribute() | user-supplied input |
| django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | django_bad.py:27:71:27:106 | ControlFlowNode for Attribute() | django_bad.py:27:30:27:124 | ControlFlowNode for Fstring | Cookie is constructed from a $@,and its samesite flag is not properly set. | django_bad.py:27:71:27:106 | ControlFlowNode for Attribute() | user-supplied input |
| flask_bad.py:24:21:24:40 | ControlFlowNode for Subscript | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | flask_bad.py:24:21:24:40 | ControlFlowNode for Subscript | Cookie is constructed from a $@,and its httponly flag is not properly set. | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | user-supplied input |
| flask_bad.py:24:21:24:40 | ControlFlowNode for Subscript | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | flask_bad.py:24:21:24:40 | ControlFlowNode for Subscript | Cookie is constructed from a $@,and its samesite flag is not properly set. | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | user-supplied input |
| flask_bad.py:24:21:24:40 | ControlFlowNode for Subscript | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | flask_bad.py:24:21:24:40 | ControlFlowNode for Subscript | Cookie is constructed from a $@,and its secure flag is not properly set. | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | user-supplied input |
| flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | Cookie is constructed from a $@,and its httponly flag is not properly set. | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | user-supplied input |
| flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | Cookie is constructed from a $@,and its samesite flag is not properly set. | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | user-supplied input |
| flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | flask_bad.py:24:49:24:69 | ControlFlowNode for Subscript | Cookie is constructed from a $@,and its secure flag is not properly set. | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | user-supplied input |
| flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | flask_bad.py:32:34:32:98 | ControlFlowNode for Fstring | Cookie is constructed from a $@,and its samesite flag is not properly set. | flask_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | user-supplied input |

View File

@@ -1 +0,0 @@
experimental/Security/CWE-614/CookieInjection.ql

View File

@@ -0,0 +1,4 @@
argumentToEnsureNotTaintedNotMarkedAsSpurious
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
testFailures
failures

View File

@@ -0,0 +1,2 @@
import experimental.meta.InlineTaintTest
import MakeInlineTaintTest<TestTaintTrackingConfig>

View File

@@ -0,0 +1,11 @@
def impossible_flow(cond: bool):
TAINTED_STRING = "ts"
x = (TAINTED_STRING, 42) if cond else "SAFE"
if isinstance(x, str):
# tainted-flow to here is impossible, replicated from path-flow seen in a real
# repo.
ensure_not_tainted(x) # $ SPURIOUS: tainted
else:
ensure_tainted(x) # $ tainted
ensure_tainted(x[0]) # $ tainted

View File

@@ -0,0 +1,28 @@
edges
| django_tests.py:4:25:4:31 | ControlFlowNode for request | django_tests.py:6:21:6:31 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
| django_tests.py:4:25:4:31 | ControlFlowNode for request | django_tests.py:7:21:7:31 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
| django_tests.py:6:21:6:31 | ControlFlowNode for Attribute | django_tests.py:6:21:6:43 | ControlFlowNode for Attribute() | provenance | dict.get |
| django_tests.py:7:21:7:31 | ControlFlowNode for Attribute | django_tests.py:7:21:7:44 | ControlFlowNode for Attribute() | provenance | dict.get |
| django_tests.py:11:26:11:32 | ControlFlowNode for request | django_tests.py:13:33:13:43 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
| django_tests.py:11:26:11:32 | ControlFlowNode for request | django_tests.py:13:59:13:69 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
| django_tests.py:13:33:13:43 | ControlFlowNode for Attribute | django_tests.py:13:33:13:55 | ControlFlowNode for Attribute() | provenance | dict.get |
| django_tests.py:13:33:13:55 | ControlFlowNode for Attribute() | django_tests.py:13:30:13:100 | ControlFlowNode for Fstring | provenance | |
| django_tests.py:13:59:13:69 | ControlFlowNode for Attribute | django_tests.py:13:59:13:82 | ControlFlowNode for Attribute() | provenance | dict.get |
| django_tests.py:13:59:13:82 | ControlFlowNode for Attribute() | django_tests.py:13:30:13:100 | ControlFlowNode for Fstring | provenance | |
nodes
| django_tests.py:4:25:4:31 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| django_tests.py:6:21:6:31 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| django_tests.py:6:21:6:43 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| django_tests.py:7:21:7:31 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| django_tests.py:7:21:7:44 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| django_tests.py:11:26:11:32 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| django_tests.py:13:30:13:100 | ControlFlowNode for Fstring | semmle.label | ControlFlowNode for Fstring |
| django_tests.py:13:33:13:43 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| django_tests.py:13:33:13:55 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| django_tests.py:13:59:13:69 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| django_tests.py:13:59:13:82 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
subpaths
#select
| django_tests.py:6:21:6:43 | ControlFlowNode for Attribute() | django_tests.py:4:25:4:31 | ControlFlowNode for request | django_tests.py:6:21:6:43 | ControlFlowNode for Attribute() | Cookie is constructed from a $@. | django_tests.py:4:25:4:31 | ControlFlowNode for request | user-supplied input |
| django_tests.py:7:21:7:44 | ControlFlowNode for Attribute() | django_tests.py:4:25:4:31 | ControlFlowNode for request | django_tests.py:7:21:7:44 | ControlFlowNode for Attribute() | Cookie is constructed from a $@. | django_tests.py:4:25:4:31 | ControlFlowNode for request | user-supplied input |
| django_tests.py:13:30:13:100 | ControlFlowNode for Fstring | django_tests.py:11:26:11:32 | ControlFlowNode for request | django_tests.py:13:30:13:100 | ControlFlowNode for Fstring | Cookie is constructed from a $@. | django_tests.py:11:26:11:32 | ControlFlowNode for request | user-supplied input |

View File

@@ -0,0 +1 @@
Security/CWE-020/CookieInjection.ql

View File

@@ -0,0 +1,20 @@
import django.http
from django.urls import path
def django_response_bad(request):
resp = django.http.HttpResponse()
resp.set_cookie(request.GET.get("name"), # BAD: Cookie is constructed from user input
request.GET.get("value"))
return resp
def django_response_bad2(request):
response = django.http.HttpResponse()
response['Set-Cookie'] = f"{request.GET.get('name')}={request.GET.get('value')}; SameSite=None;" # BAD: Cookie header is constructed from user input.
return response
# fake setup, you can't actually run this
urlpatterns = [
path("response_bad", django_response_bad),
path("response_bd2", django_response_bad2)
]