mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Merge branch 'main' into stdlib-http-source-modeling
This commit is contained in:
@@ -43,7 +43,8 @@ In the second example, it appears that the user is restricted to opening a file
|
||||
special characters. For example, the string <code>"../../../etc/passwd"</code> will result in the code
|
||||
reading the file located at <code>"/server/static/images/../../../etc/passwd"</code>, which is the system's
|
||||
password file. This file would then be sent back to the user, giving them access to all the
|
||||
system's passwords.
|
||||
system's passwords. Note that a user could also use an absolute path here, since the result of
|
||||
<code>os.path.join("/server/static/images/", "/etc/passwd")</code> is <code>"/etc/passwd"</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -7,8 +7,9 @@ private import semmle.python.frameworks.Django
|
||||
private import semmle.python.frameworks.Fabric
|
||||
private import semmle.python.frameworks.Flask
|
||||
private import semmle.python.frameworks.Invoke
|
||||
private import semmle.python.frameworks.MySQLdb
|
||||
private import semmle.python.frameworks.MysqlConnectorPython
|
||||
private import semmle.python.frameworks.MySQLdb
|
||||
private import semmle.python.frameworks.Psycopg2
|
||||
private import semmle.python.frameworks.PyMySQL
|
||||
private import semmle.python.frameworks.Stdlib
|
||||
private import semmle.python.frameworks.Yaml
|
||||
|
||||
@@ -17,7 +17,7 @@ private import PEP249
|
||||
* - https://mysqlclient.readthedocs.io/index.html
|
||||
* - https://pypi.org/project/MySQL-python/
|
||||
*/
|
||||
module MySQLdb {
|
||||
private module MySQLdb {
|
||||
// ---------------------------------------------------------------------------
|
||||
// MySQLdb
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -17,7 +17,7 @@ private import PEP249
|
||||
* - https://dev.mysql.com/doc/connector-python/en/
|
||||
* - https://dev.mysql.com/doc/connector-python/en/connector-python-example-connecting.html
|
||||
*/
|
||||
module MysqlConnectorPython {
|
||||
private module MysqlConnectorPython {
|
||||
// ---------------------------------------------------------------------------
|
||||
// mysql
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -62,11 +62,11 @@ module Connection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for the `db.Connection.cursor` method.
|
||||
* Provides models for the `cursor` method on a connection.
|
||||
* See https://www.python.org/dev/peps/pep-0249/#cursor.
|
||||
*/
|
||||
module cursor {
|
||||
/** Gets a reference to the `db.connection.cursor` method. */
|
||||
/** Gets a reference to the `cursor` method on a connection. */
|
||||
private DataFlow::Node methodRef(DataFlow::TypeTracker t) {
|
||||
t.startInAttr("cursor") and
|
||||
result = Connection::instance()
|
||||
@@ -74,10 +74,10 @@ module cursor {
|
||||
exists(DataFlow::TypeTracker t2 | result = methodRef(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to the `db.connection.cursor` metod. */
|
||||
/** Gets a reference to the `cursor` method on a connection. */
|
||||
DataFlow::Node methodRef() { result = methodRef(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/** Gets a reference to a result of calling `db.connection.cursor`. */
|
||||
/** Gets a reference to a result of calling the `cursor` method on a connection. */
|
||||
private DataFlow::Node methodResult(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result.asCfgNode().(CallNode).getFunction() = methodRef().asCfgNode()
|
||||
@@ -85,31 +85,40 @@ module cursor {
|
||||
exists(DataFlow::TypeTracker t2 | result = methodResult(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to a result of calling `db.connection.cursor`. */
|
||||
/** Gets a reference to a result of calling the `cursor` method on a connection. */
|
||||
DataFlow::Node methodResult() { result = methodResult(DataFlow::TypeTracker::end()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the `db.Connection.Cursor.execute` function.
|
||||
* Gets a reference to the `execute` method on a cursor (or on a connection).
|
||||
*
|
||||
* Note: while `execute` method on a connection is not part of PEP249, if it is used, we
|
||||
* recognize it as an alias for constructing a cursor and calling `execute` on it.
|
||||
*
|
||||
* See https://www.python.org/dev/peps/pep-0249/#id15.
|
||||
*/
|
||||
private DataFlow::Node execute(DataFlow::TypeTracker t) {
|
||||
t.startInAttr("execute") and
|
||||
result = cursor::methodResult()
|
||||
result in [cursor::methodResult(), Connection::instance()]
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = execute(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the `db.Connection.Cursor.execute` function.
|
||||
* Gets a reference to the `execute` method on a cursor (or on a connection).
|
||||
*
|
||||
* Note: while `execute` method on a connection is not part of PEP249, if it is used, we
|
||||
* recognize it as an alias for constructing a cursor and calling `execute` on it.
|
||||
*
|
||||
* See https://www.python.org/dev/peps/pep-0249/#id15.
|
||||
*/
|
||||
DataFlow::Node execute() { result = execute(DataFlow::TypeTracker::end()) }
|
||||
|
||||
private class DbConnectionExecute extends SqlExecution::Range, DataFlow::CfgNode {
|
||||
/** A call to the `execute` method on a cursor (or on a connection). */
|
||||
private class ExecuteCall extends SqlExecution::Range, DataFlow::CfgNode {
|
||||
override CallNode node;
|
||||
|
||||
DbConnectionExecute() { node.getFunction() = execute().asCfgNode() }
|
||||
ExecuteCall() { node.getFunction() = execute().asCfgNode() }
|
||||
|
||||
override DataFlow::Node getSql() {
|
||||
result.asCfgNode() in [node.getArg(0), node.getArgByName("sql")]
|
||||
|
||||
@@ -17,7 +17,7 @@ private import PEP249
|
||||
* - https://www.psycopg.org/docs/
|
||||
* - https://pypi.org/project/psycopg2/
|
||||
*/
|
||||
module Psycopg2 {
|
||||
private module Psycopg2 {
|
||||
// ---------------------------------------------------------------------------
|
||||
// Psycopg
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
32
python/ql/src/semmle/python/frameworks/PyMySQL.qll
Normal file
32
python/ql/src/semmle/python/frameworks/PyMySQL.qll
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `PyMySQL` PyPI package.
|
||||
* See https://pypi.org/project/PyMySQL/
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.Concepts
|
||||
private import PEP249
|
||||
|
||||
/**
|
||||
* Provides models for the `PyMySQL` PyPI package.
|
||||
* See https://pypi.org/project/PyMySQL/
|
||||
*/
|
||||
private module PyMySQL {
|
||||
/** Gets a reference to the `pymysql` module. */
|
||||
private DataFlow::Node pymysql(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = DataFlow::importNode("pymysql")
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = pymysql(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to the `pymysql` module. */
|
||||
DataFlow::Node pymysql() { result = pymysql(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/** PyMySQL implements PEP 249, providing ways to execute SQL statements against a database. */
|
||||
class PyMySQLPEP249 extends PEP249Module {
|
||||
PyMySQLPEP249() { this = pymysql() }
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.Concepts
|
||||
private import PEP249
|
||||
|
||||
/** Provides models for the Python standard library. */
|
||||
private module Stdlib {
|
||||
@@ -91,7 +92,7 @@ private module Stdlib {
|
||||
* For example, using `attr_name = "join"` will get all uses of `os.path.join`.
|
||||
*/
|
||||
private DataFlow::Node path_attr(DataFlow::TypeTracker t, string attr_name) {
|
||||
attr_name in ["join", "normpath"] and
|
||||
attr_name in ["join", "normpath", "realpath", "abspath"] and
|
||||
(
|
||||
t.start() and
|
||||
result = DataFlow::importNode("os.path." + attr_name)
|
||||
@@ -157,6 +158,54 @@ private module Stdlib {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `os.path.abspath`.
|
||||
* See https://docs.python.org/3/library/os.path.html#os.path.abspath
|
||||
*/
|
||||
private class OsPathAbspathCall extends Path::PathNormalization::Range, DataFlow::CfgNode {
|
||||
override CallNode node;
|
||||
|
||||
OsPathAbspathCall() { node.getFunction() = os::path::path_attr("abspath").asCfgNode() }
|
||||
|
||||
DataFlow::Node getPathArg() {
|
||||
result.asCfgNode() in [node.getArg(0), node.getArgByName("path")]
|
||||
}
|
||||
}
|
||||
|
||||
/** An additional taint step for calls to `os.path.abspath` */
|
||||
private class OsPathAbspathCallAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
exists(OsPathAbspathCall call |
|
||||
nodeTo = call and
|
||||
nodeFrom = call.getPathArg()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `os.path.realpath`.
|
||||
* See https://docs.python.org/3/library/os.path.html#os.path.realpath
|
||||
*/
|
||||
private class OsPathRealpathCall extends Path::PathNormalization::Range, DataFlow::CfgNode {
|
||||
override CallNode node;
|
||||
|
||||
OsPathRealpathCall() { node.getFunction() = os::path::path_attr("realpath").asCfgNode() }
|
||||
|
||||
DataFlow::Node getPathArg() {
|
||||
result.asCfgNode() in [node.getArg(0), node.getArgByName("path")]
|
||||
}
|
||||
}
|
||||
|
||||
/** An additional taint step for calls to `os.path.realpath` */
|
||||
private class OsPathRealpathCallAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
exists(OsPathRealpathCall call |
|
||||
nodeTo = call and
|
||||
nodeFrom = call.getPathArg()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `os.system`.
|
||||
* See https://docs.python.org/3/library/os.html#os.system
|
||||
@@ -1571,6 +1620,29 @@ private module Stdlib {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// sqlite3
|
||||
// ---------------------------------------------------------------------------
|
||||
/** Gets a reference to the `sqlite3` module. */
|
||||
private DataFlow::Node sqlite3(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = DataFlow::importNode("sqlite3")
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = sqlite3(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to the `sqlite3` module. */
|
||||
DataFlow::Node sqlite3() { result = sqlite3(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/**
|
||||
* sqlite3 implements PEP 249, providing ways to execute SQL statements against a database.
|
||||
*
|
||||
* See https://devdocs.io/python~3.9/library/sqlite3
|
||||
*/
|
||||
class Sqlite3 extends PEP249Module {
|
||||
Sqlite3() { this = sqlite3() }
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
import python
|
||||
import experimental.meta.ConceptsTest
|
||||
@@ -0,0 +1,5 @@
|
||||
import pymysql
|
||||
connection = pymysql.connect(host="localhost", user="user", password="passwd")
|
||||
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("some sql", (42,)) # $ getSql="some sql"
|
||||
@@ -0,0 +1,8 @@
|
||||
import sqlite3
|
||||
db = sqlite3.connect("example.db")
|
||||
|
||||
# non standard
|
||||
db.execute("some sql", (42,)) # $ getSql="some sql"
|
||||
|
||||
cursor = db.cursor()
|
||||
cursor.execute("some sql", (42,)) # $ getSql="some sql"
|
||||
@@ -1,10 +1,23 @@
|
||||
edges
|
||||
| path_injection.py:9:12:9:23 | ControlFlowNode for Attribute | path_injection.py:10:14:10:44 | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:15:12:15:23 | ControlFlowNode for Attribute | path_injection.py:16:13:16:61 | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:16:13:16:61 | ControlFlowNode for Attribute() | path_injection.py:17:14:17:18 | ControlFlowNode for npath |
|
||||
| path_injection.py:24:12:24:23 | ControlFlowNode for Attribute | path_injection.py:25:13:25:61 | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:25:13:25:61 | ControlFlowNode for Attribute() | path_injection.py:28:14:28:18 | ControlFlowNode for npath |
|
||||
| path_injection.py:33:12:33:23 | ControlFlowNode for Attribute | path_injection.py:34:13:34:61 | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:12:16:12:27 | ControlFlowNode for Attribute | path_injection.py:13:14:13:47 | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:19:16:19:27 | ControlFlowNode for Attribute | path_injection.py:20:13:20:64 | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:20:13:20:64 | ControlFlowNode for Attribute() | path_injection.py:21:14:21:18 | ControlFlowNode for npath |
|
||||
| path_injection.py:27:16:27:27 | ControlFlowNode for Attribute | path_injection.py:28:13:28:64 | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:28:13:28:64 | ControlFlowNode for Attribute() | path_injection.py:31:14:31:18 | ControlFlowNode for npath |
|
||||
| path_injection.py:37:16:37:27 | ControlFlowNode for Attribute | path_injection.py:38:13:38:64 | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:46:16:46:27 | ControlFlowNode for Attribute | path_injection.py:47:13:47:64 | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:47:13:47:64 | ControlFlowNode for Attribute() | path_injection.py:48:14:48:18 | ControlFlowNode for npath |
|
||||
| path_injection.py:54:16:54:27 | ControlFlowNode for Attribute | path_injection.py:55:13:55:64 | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:63:16:63:27 | ControlFlowNode for Attribute | path_injection.py:64:13:64:63 | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:64:13:64:63 | ControlFlowNode for Attribute() | path_injection.py:65:14:65:18 | ControlFlowNode for npath |
|
||||
| path_injection.py:71:16:71:27 | ControlFlowNode for Attribute | path_injection.py:72:13:72:63 | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:78:20:78:25 | ControlFlowNode for foo_id | path_injection.py:81:14:81:17 | ControlFlowNode for path |
|
||||
| path_injection.py:85:20:85:22 | ControlFlowNode for foo | path_injection.py:89:14:89:17 | ControlFlowNode for path |
|
||||
| path_injection.py:94:16:94:27 | ControlFlowNode for Attribute | path_injection.py:100:14:100:17 | ControlFlowNode for path |
|
||||
| path_injection.py:105:16:105:27 | ControlFlowNode for Attribute | path_injection.py:111:14:111:17 | ControlFlowNode for path |
|
||||
| path_injection.py:116:16:116:27 | ControlFlowNode for Attribute | path_injection.py:119:14:119:22 | ControlFlowNode for sanitized |
|
||||
| path_injection.py:125:16:125:27 | ControlFlowNode for Attribute | path_injection.py:127:30:127:51 | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:125:16:125:27 | ControlFlowNode for Attribute | path_injection.py:129:14:129:17 | ControlFlowNode for path |
|
||||
| test.py:9:12:9:23 | ControlFlowNode for Attribute | test.py:9:12:9:39 | ControlFlowNode for Attribute() |
|
||||
| test.py:9:12:9:23 | ControlFlowNode for Attribute | test.py:9:12:9:39 | ControlFlowNode for Attribute() |
|
||||
| test.py:9:12:9:39 | ControlFlowNode for Attribute() | test.py:18:9:18:16 | ControlFlowNode for source() |
|
||||
@@ -39,16 +52,40 @@ edges
|
||||
| test_chaining.py:41:9:41:16 | ControlFlowNode for source() | test_chaining.py:42:9:42:19 | ControlFlowNode for normpath() |
|
||||
| test_chaining.py:44:13:44:23 | ControlFlowNode for normpath() | test_chaining.py:45:14:45:14 | ControlFlowNode for z |
|
||||
nodes
|
||||
| path_injection.py:9:12:9:23 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| path_injection.py:10:14:10:44 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:15:12:15:23 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| path_injection.py:16:13:16:61 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:17:14:17:18 | ControlFlowNode for npath | semmle.label | ControlFlowNode for npath |
|
||||
| path_injection.py:24:12:24:23 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| path_injection.py:25:13:25:61 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:28:14:28:18 | ControlFlowNode for npath | semmle.label | ControlFlowNode for npath |
|
||||
| path_injection.py:33:12:33:23 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| path_injection.py:34:13:34:61 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:12:16:12:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| path_injection.py:13:14:13:47 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:19:16:19:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| path_injection.py:20:13:20:64 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:21:14:21:18 | ControlFlowNode for npath | semmle.label | ControlFlowNode for npath |
|
||||
| path_injection.py:27:16:27:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| path_injection.py:28:13:28:64 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:31:14:31:18 | ControlFlowNode for npath | semmle.label | ControlFlowNode for npath |
|
||||
| path_injection.py:37:16:37:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| path_injection.py:38:13:38:64 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:46:16:46:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| path_injection.py:47:13:47:64 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:48:14:48:18 | ControlFlowNode for npath | semmle.label | ControlFlowNode for npath |
|
||||
| path_injection.py:54:16:54:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| path_injection.py:55:13:55:64 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:63:16:63:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| path_injection.py:64:13:64:63 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:65:14:65:18 | ControlFlowNode for npath | semmle.label | ControlFlowNode for npath |
|
||||
| path_injection.py:71:16:71:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| path_injection.py:72:13:72:63 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:78:20:78:25 | ControlFlowNode for foo_id | semmle.label | ControlFlowNode for foo_id |
|
||||
| path_injection.py:81:14:81:17 | ControlFlowNode for path | semmle.label | ControlFlowNode for path |
|
||||
| path_injection.py:85:20:85:22 | ControlFlowNode for foo | semmle.label | ControlFlowNode for foo |
|
||||
| path_injection.py:89:14:89:17 | ControlFlowNode for path | semmle.label | ControlFlowNode for path |
|
||||
| path_injection.py:94:16:94:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| path_injection.py:100:14:100:17 | ControlFlowNode for path | semmle.label | ControlFlowNode for path |
|
||||
| path_injection.py:105:16:105:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| path_injection.py:111:14:111:17 | ControlFlowNode for path | semmle.label | ControlFlowNode for path |
|
||||
| path_injection.py:116:16:116:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| path_injection.py:119:14:119:22 | ControlFlowNode for sanitized | semmle.label | ControlFlowNode for sanitized |
|
||||
| path_injection.py:125:16:125:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| path_injection.py:125:16:125:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| path_injection.py:127:30:127:51 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| path_injection.py:129:14:129:17 | ControlFlowNode for path | semmle.label | ControlFlowNode for path |
|
||||
| test.py:9:12:9:23 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| test.py:9:12:9:23 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| test.py:9:12:9:39 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
@@ -84,9 +121,17 @@ nodes
|
||||
| test_chaining.py:44:13:44:23 | ControlFlowNode for normpath() | semmle.label | ControlFlowNode for normpath() |
|
||||
| test_chaining.py:45:14:45:14 | ControlFlowNode for z | semmle.label | ControlFlowNode for z |
|
||||
#select
|
||||
| path_injection.py:10:14:10:44 | ControlFlowNode for Attribute() | path_injection.py:9:12:9:23 | ControlFlowNode for Attribute | path_injection.py:10:14:10:44 | ControlFlowNode for Attribute() | This path depends on $@. | path_injection.py:9:12:9:23 | ControlFlowNode for Attribute | a user-provided value |
|
||||
| path_injection.py:17:14:17:18 | ControlFlowNode for npath | path_injection.py:15:12:15:23 | ControlFlowNode for Attribute | path_injection.py:17:14:17:18 | ControlFlowNode for npath | This path depends on $@. | path_injection.py:15:12:15:23 | ControlFlowNode for Attribute | a user-provided value |
|
||||
| path_injection.py:28:14:28:18 | ControlFlowNode for npath | path_injection.py:24:12:24:23 | ControlFlowNode for Attribute | path_injection.py:28:14:28:18 | ControlFlowNode for npath | This path depends on $@. | path_injection.py:24:12:24:23 | ControlFlowNode for Attribute | a user-provided value |
|
||||
| path_injection.py:13:14:13:47 | ControlFlowNode for Attribute() | path_injection.py:12:16:12:27 | ControlFlowNode for Attribute | path_injection.py:13:14:13:47 | ControlFlowNode for Attribute() | This path depends on $@. | path_injection.py:12:16:12:27 | ControlFlowNode for Attribute | a user-provided value |
|
||||
| path_injection.py:21:14:21:18 | ControlFlowNode for npath | path_injection.py:19:16:19:27 | ControlFlowNode for Attribute | path_injection.py:21:14:21:18 | ControlFlowNode for npath | This path depends on $@. | path_injection.py:19:16:19:27 | ControlFlowNode for Attribute | a user-provided value |
|
||||
| path_injection.py:31:14:31:18 | ControlFlowNode for npath | path_injection.py:27:16:27:27 | ControlFlowNode for Attribute | path_injection.py:31:14:31:18 | ControlFlowNode for npath | This path depends on $@. | path_injection.py:27:16:27:27 | ControlFlowNode for Attribute | a user-provided value |
|
||||
| path_injection.py:48:14:48:18 | ControlFlowNode for npath | path_injection.py:46:16:46:27 | ControlFlowNode for Attribute | path_injection.py:48:14:48:18 | ControlFlowNode for npath | This path depends on $@. | path_injection.py:46:16:46:27 | ControlFlowNode for Attribute | a user-provided value |
|
||||
| path_injection.py:65:14:65:18 | ControlFlowNode for npath | path_injection.py:63:16:63:27 | ControlFlowNode for Attribute | path_injection.py:65:14:65:18 | ControlFlowNode for npath | This path depends on $@. | path_injection.py:63:16:63:27 | ControlFlowNode for Attribute | a user-provided value |
|
||||
| path_injection.py:81:14:81:17 | ControlFlowNode for path | path_injection.py:78:20:78:25 | ControlFlowNode for foo_id | path_injection.py:81:14:81:17 | ControlFlowNode for path | This path depends on $@. | path_injection.py:78:20:78:25 | ControlFlowNode for foo_id | a user-provided value |
|
||||
| path_injection.py:89:14:89:17 | ControlFlowNode for path | path_injection.py:85:20:85:22 | ControlFlowNode for foo | path_injection.py:89:14:89:17 | ControlFlowNode for path | This path depends on $@. | path_injection.py:85:20:85:22 | ControlFlowNode for foo | a user-provided value |
|
||||
| path_injection.py:100:14:100:17 | ControlFlowNode for path | path_injection.py:94:16:94:27 | ControlFlowNode for Attribute | path_injection.py:100:14:100:17 | ControlFlowNode for path | This path depends on $@. | path_injection.py:94:16:94:27 | ControlFlowNode for Attribute | a user-provided value |
|
||||
| path_injection.py:111:14:111:17 | ControlFlowNode for path | path_injection.py:105:16:105:27 | ControlFlowNode for Attribute | path_injection.py:111:14:111:17 | ControlFlowNode for path | This path depends on $@. | path_injection.py:105:16:105:27 | ControlFlowNode for Attribute | a user-provided value |
|
||||
| path_injection.py:119:14:119:22 | ControlFlowNode for sanitized | path_injection.py:116:16:116:27 | ControlFlowNode for Attribute | path_injection.py:119:14:119:22 | ControlFlowNode for sanitized | This path depends on $@. | path_injection.py:116:16:116:27 | ControlFlowNode for Attribute | a user-provided value |
|
||||
| path_injection.py:129:14:129:17 | ControlFlowNode for path | path_injection.py:125:16:125:27 | ControlFlowNode for Attribute | path_injection.py:129:14:129:17 | ControlFlowNode for path | This path depends on $@. | path_injection.py:125:16:125:27 | ControlFlowNode for Attribute | a user-provided value |
|
||||
| test.py:19:10:19:10 | ControlFlowNode for x | test.py:9:12:9:23 | ControlFlowNode for Attribute | test.py:19:10:19:10 | ControlFlowNode for x | This path depends on $@. | test.py:9:12:9:23 | ControlFlowNode for Attribute | a user-provided value |
|
||||
| test.py:26:10:26:10 | ControlFlowNode for y | test.py:9:12:9:23 | ControlFlowNode for Attribute | test.py:26:10:26:10 | ControlFlowNode for y | This path depends on $@. | test.py:9:12:9:23 | ControlFlowNode for Attribute | a user-provided value |
|
||||
| test.py:33:14:33:14 | ControlFlowNode for x | test.py:9:12:9:23 | ControlFlowNode for Attribute | test.py:33:14:33:14 | ControlFlowNode for x | This path depends on $@. | test.py:9:12:9:23 | ControlFlowNode for Attribute | a user-provided value |
|
||||
|
||||
@@ -1,36 +1,129 @@
|
||||
|
||||
import os.path
|
||||
import os
|
||||
|
||||
from flask import Flask, request
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
STATIC_DIR = "/server/static/"
|
||||
|
||||
|
||||
@app.route("/path1")
|
||||
def path_injection():
|
||||
path = request.args.get('path', '')
|
||||
f = open(os.path.join(os.getcwd(), path))
|
||||
filename = request.args.get('filename', '')
|
||||
f = open(os.path.join(STATIC_DIR, filename)) # NOT OK
|
||||
|
||||
|
||||
@app.route("/path2")
|
||||
def path_injection():
|
||||
# Normalized, but not checked
|
||||
path = request.args.get('path', '')
|
||||
npath = os.path.normpath(os.path.join(os.getcwd(), path))
|
||||
f = open(npath) # Path not found
|
||||
filename = request.args.get('filename', '')
|
||||
npath = os.path.normpath(os.path.join(STATIC_DIR, filename))
|
||||
f = open(npath) # NOT OK
|
||||
|
||||
SAFE = "/tmp/scratch_area/"
|
||||
|
||||
@app.route("/path3")
|
||||
def safe_path():
|
||||
# Normalized, but check doesn't reach open().
|
||||
path = request.args.get('path', '')
|
||||
npath = os.path.normpath(os.path.join(os.getcwd(), path))
|
||||
if npath.startswith(SAFE):
|
||||
def unsafe_path_normpath():
|
||||
# Normalized, but `open()` is not guarded by `startswith` check
|
||||
filename = request.args.get('filename', '')
|
||||
npath = os.path.normpath(os.path.join(STATIC_DIR, filename))
|
||||
if npath.startswith(STATIC_DIR):
|
||||
pass
|
||||
f = open(npath) # Path not found
|
||||
f = open(npath) # NOT OK
|
||||
|
||||
|
||||
@app.route("/path4")
|
||||
def safe_path():
|
||||
def safe_path_normpath():
|
||||
# Normalized, and checked properly
|
||||
path = request.args.get('path', '')
|
||||
npath = os.path.normpath(os.path.join(os.getcwd(), path))
|
||||
if npath.startswith(SAFE):
|
||||
f = open(npath)
|
||||
filename = request.args.get('filename', '')
|
||||
npath = os.path.normpath(os.path.join(STATIC_DIR, filename))
|
||||
if npath.startswith(STATIC_DIR):
|
||||
f = open(npath) # OK
|
||||
|
||||
|
||||
@app.route("/path5")
|
||||
def unsafe_path_realpath():
|
||||
# Normalized (by `realpath` that also follows symlinks), but not checked properly
|
||||
filename = request.args.get('filename', '')
|
||||
npath = os.path.realpath(os.path.join(STATIC_DIR, filename))
|
||||
f = open(npath) # NOT OK
|
||||
|
||||
|
||||
@app.route("/path6")
|
||||
def safe_path_realpath():
|
||||
# Normalized (by `realpath` that also follows symlinks), and checked properly
|
||||
filename = request.args.get('filename', '')
|
||||
npath = os.path.realpath(os.path.join(STATIC_DIR, filename))
|
||||
if npath.startswith(STATIC_DIR):
|
||||
f = open(npath) # OK
|
||||
|
||||
|
||||
@app.route("/path6")
|
||||
def unsafe_path_abspath():
|
||||
# Normalized (by `abspath`), but not checked properly
|
||||
filename = request.args.get('filename', '')
|
||||
npath = os.path.abspath(os.path.join(STATIC_DIR, filename))
|
||||
f = open(npath) # NOT OK
|
||||
|
||||
|
||||
@app.route("/path7")
|
||||
def safe_path_abspath():
|
||||
# Normalized (by `abspath`), and checked properly
|
||||
filename = request.args.get('filename', '')
|
||||
npath = os.path.abspath(os.path.join(STATIC_DIR, filename))
|
||||
if npath.startswith(STATIC_DIR):
|
||||
f = open(npath) # OK
|
||||
|
||||
|
||||
@app.route("/int-only/<int:foo_id>")
|
||||
def flask_int_only(foo_id):
|
||||
# This is OK, since the flask routing ensures that `foo_id` MUST be an integer.
|
||||
path = os.path.join(STATIC_DIR, foo_id)
|
||||
f = open(path) # OK TODO: FP
|
||||
|
||||
|
||||
@app.route("/not-path/<foo>")
|
||||
def flask_not_path(foo):
|
||||
# On UNIX systems, this is OK, since without being marked as `<path:foo>`, flask
|
||||
# routing ensures that `foo` cannot contain forward slashes (not by using %2F either).
|
||||
path = os.path.join(STATIC_DIR, foo)
|
||||
f = open(path) # OK if only running on UNIX systems, NOT OK if could be running on windows
|
||||
|
||||
|
||||
@app.route("/no-dot-dot")
|
||||
def no_dot_dot():
|
||||
filename = request.args.get('filename', '')
|
||||
path = os.path.join(STATIC_DIR, filename)
|
||||
# Note: even for UNIX-only programs, this check is not good enough, since it doesn't
|
||||
# handle if `filename` is an absolute path
|
||||
if '../' in path:
|
||||
return "not this time"
|
||||
f = open(path) # NOT OK
|
||||
|
||||
|
||||
@app.route("/no-dot-dot-with-prefix")
|
||||
def no_dot_dot_with_prefix():
|
||||
filename = request.args.get('filename', '')
|
||||
path = os.path.join(STATIC_DIR, "img-"+filename)
|
||||
# Note: Since `filename` has a prefix, it's not possible to use an absolute path.
|
||||
# Therefore, for UNIX-only programs, the `../` check is enough to stop path injections.
|
||||
if '../' in path:
|
||||
return "not this time"
|
||||
f = open(path) # OK if only running on UNIX systems, NOT OK if could be running on windows
|
||||
|
||||
|
||||
@app.route("/replace-slash")
|
||||
def replace_slash():
|
||||
filename = request.args.get('filename', '')
|
||||
path = os.path.join(STATIC_DIR, filename)
|
||||
sanitized = path.replace("/", "_")
|
||||
f = open(sanitized) # OK if only running on UNIX systems, NOT OK if could be running on windows
|
||||
|
||||
|
||||
@app.route("/stackoverflow-solution")
|
||||
def stackoverflow_solution():
|
||||
# Solution provided in https://stackoverflow.com/a/45188896
|
||||
filename = request.args.get('filename', '')
|
||||
path = os.path.join(STATIC_DIR, filename)
|
||||
if os.path.commonprefix((os.path.realpath(path), STATIC_DIR)) != STATIC_DIR:
|
||||
return "not this time"
|
||||
f = open(path) # OK TODO: FP
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
edges
|
||||
| reflected_xss.py:8:18:8:29 | ControlFlowNode for Attribute | reflected_xss.py:9:26:9:53 | ControlFlowNode for BinaryExpr |
|
||||
| reflected_xss.py:9:18:9:29 | ControlFlowNode for Attribute | reflected_xss.py:10:26:10:53 | ControlFlowNode for BinaryExpr |
|
||||
| reflected_xss.py:21:23:21:34 | ControlFlowNode for Attribute | reflected_xss.py:22:26:22:41 | ControlFlowNode for Attribute() |
|
||||
| reflected_xss.py:27:23:27:34 | ControlFlowNode for Attribute | reflected_xss.py:28:26:28:41 | ControlFlowNode for Attribute() |
|
||||
nodes
|
||||
| reflected_xss.py:8:18:8:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| reflected_xss.py:9:26:9:53 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr |
|
||||
| reflected_xss.py:9:18:9:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| reflected_xss.py:10:26:10:53 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr |
|
||||
| reflected_xss.py:21:23:21:34 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| reflected_xss.py:22:26:22:41 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| reflected_xss.py:27:23:27:34 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| reflected_xss.py:28:26:28:41 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
#select
|
||||
| reflected_xss.py:9:26:9:53 | ControlFlowNode for BinaryExpr | reflected_xss.py:8:18:8:29 | ControlFlowNode for Attribute | reflected_xss.py:9:26:9:53 | ControlFlowNode for BinaryExpr | Cross-site scripting vulnerability due to $@. | reflected_xss.py:8:18:8:29 | ControlFlowNode for Attribute | a user-provided value |
|
||||
| reflected_xss.py:10:26:10:53 | ControlFlowNode for BinaryExpr | reflected_xss.py:9:18:9:29 | ControlFlowNode for Attribute | reflected_xss.py:10:26:10:53 | ControlFlowNode for BinaryExpr | Cross-site scripting vulnerability due to $@. | reflected_xss.py:9:18:9:29 | ControlFlowNode for Attribute | a user-provided value |
|
||||
| reflected_xss.py:22:26:22:41 | ControlFlowNode for Attribute() | reflected_xss.py:21:23:21:34 | ControlFlowNode for Attribute | reflected_xss.py:22:26:22:41 | ControlFlowNode for Attribute() | Cross-site scripting vulnerability due to $@. | reflected_xss.py:21:23:21:34 | ControlFlowNode for Attribute | a user-provided value |
|
||||
| reflected_xss.py:28:26:28:41 | ControlFlowNode for Attribute() | reflected_xss.py:27:23:27:34 | ControlFlowNode for Attribute | reflected_xss.py:28:26:28:41 | ControlFlowNode for Attribute() | Cross-site scripting vulnerability due to $@. | reflected_xss.py:27:23:27:34 | ControlFlowNode for Attribute | a user-provided value |
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import json
|
||||
from flask import Flask, request, make_response, escape
|
||||
|
||||
app = Flask(__name__)
|
||||
@@ -13,3 +14,15 @@ def unsafe():
|
||||
def safe():
|
||||
first_name = request.args.get("name", "")
|
||||
return make_response("Your name is " + escape(first_name)) # OK
|
||||
|
||||
|
||||
@app.route("/unsafe/json")
|
||||
def unsafe_json():
|
||||
data = json.loads(request.data)
|
||||
return make_response(json.dumps(data)) # NOT OK
|
||||
|
||||
|
||||
@app.route("/safe/json")
|
||||
def safe_json():
|
||||
data = json.loads(request.data)
|
||||
return make_response(json.dumps(data), 200, {'Content-Type': 'application/json'}) # OK, FP
|
||||
|
||||
Reference in New Issue
Block a user