mirror of
https://github.com/github/codeql.git
synced 2026-06-14 09:21:07 +02:00
Compare commits
1 Commits
copilot/in
...
python/cla
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7126b95b16 |
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Python type tracking now follows values stored in instance attributes such as `self.attr` across instance methods on the same class. As a result, analysis is more likely to recognize user-defined objects that are stored on `self` and used later in other methods, which may produce additional results.
|
||||
@@ -167,15 +167,11 @@ module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
|
||||
}
|
||||
|
||||
/** Holds if there is a level step from `nodeFrom` to `nodeTo`, which may depend on the call graph. */
|
||||
predicate levelStepCall(Node nodeFrom, LocalSourceNode nodeTo) {
|
||||
instanceFieldStep(nodeFrom, nodeTo)
|
||||
}
|
||||
predicate levelStepCall(Node nodeFrom, LocalSourceNode nodeTo) { none() }
|
||||
|
||||
/** Holds if there is a level step from `nodeFrom` to `nodeTo`, which does not depend on the call graph. */
|
||||
predicate levelStepNoCall(Node nodeFrom, LocalSourceNode nodeTo) {
|
||||
TypeTrackerSummaryFlow::levelStepNoCall(nodeFrom, nodeTo)
|
||||
or
|
||||
localFieldStep(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -321,87 +317,6 @@ module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ref` accesses attribute `attr` of `self`, where `self` is the first
|
||||
* parameter of an instance method of `cls` (i.e. an access of the form `self.attr`).
|
||||
*
|
||||
* Static methods and class methods are excluded, since their first parameter is not a
|
||||
* `self` instance reference.
|
||||
*/
|
||||
private predicate selfAttrRef(Class cls, string attr, DataFlowPublic::AttrRef ref) {
|
||||
exists(Function method, Name selfUse |
|
||||
method = cls.getAMethod() and
|
||||
not DataFlowDispatch::isStaticmethod(method) and
|
||||
not DataFlowDispatch::isClassmethod(method) and
|
||||
selfUse.getVariable() = method.getArg(0).(Name).getVariable() and
|
||||
ref.getObject().asCfgNode().getNode() = selfUse and
|
||||
ref.mayHaveAttributeName(attr)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `read` reads attribute `attr` from an instance of `cls`, where the instance
|
||||
* is referred to from outside the methods of `cls` (i.e. an access of the form
|
||||
* `instance.attr`, where `instance` is a reference to an instance of `cls`).
|
||||
*
|
||||
* This complements `selfAttrRef`, which only handles `self.attr` accesses inside the
|
||||
* methods of `cls`. Unlike `selfAttrRef`, this depends on the call graph (via
|
||||
* `classInstanceTracker`), so steps using it must be reported as `levelStepCall`.
|
||||
*/
|
||||
private predicate instanceAttrRead(Class cls, string attr, DataFlowPublic::AttrRead read) {
|
||||
read.getObject() = DataFlowDispatch::classInstanceTracker(cls) and
|
||||
read.mayHaveAttributeName(attr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `nodeFrom` is written to attribute `self.attr` in some instance method of a
|
||||
* class, and `nodeTo` reads attribute `self.attr` in some (possibly different) instance
|
||||
* method of the same class.
|
||||
*
|
||||
* This models flow through instance attributes (`self.foo`): a value stored into
|
||||
* `self.foo` in one method can be read from `self.foo` in another method. Type-tracking
|
||||
* handles the store and read steps via `AttrWrite`/`AttrRead`, but on its own it cannot
|
||||
* relate the `self` of the writing method to the `self` of the reading method. Following
|
||||
* the approach used for Ruby and JavaScript, we model this directly as a level step from
|
||||
* the written value to the read reference, for any pair of methods on the class (not
|
||||
* just from `__init__`).
|
||||
*
|
||||
* This is an over-approximation: it is instance-insensitive (it does not distinguish
|
||||
* between different instances of the same class) and order-insensitive (it does not
|
||||
* require the write to happen before the read), matching the precision of
|
||||
* instance-attribute handling for Ruby and JavaScript.
|
||||
*/
|
||||
private predicate localFieldStep(Node nodeFrom, LocalSourceNode nodeTo) {
|
||||
exists(Class cls, string attr, DataFlowPublic::AttrWrite write, DataFlowPublic::AttrRead read |
|
||||
selfAttrRef(cls, attr, write) and
|
||||
nodeFrom = write.getValue() and
|
||||
selfAttrRef(cls, attr, read) and
|
||||
nodeTo = read
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `nodeFrom` is written to attribute `self.attr` in some instance method of a
|
||||
* class, and `nodeTo` reads attribute `attr` from an instance of the same class outside
|
||||
* its methods (e.g. `instance.attr`).
|
||||
*
|
||||
* This is the cross-instance counterpart of `localFieldStep`: it relates a write of
|
||||
* `self.attr` inside the class to a read of `attr` on a reference to an instance of the
|
||||
* class. Identifying instances relies on the call graph (via `classInstanceTracker`), so
|
||||
* this step is reported as `levelStepCall` rather than `levelStepNoCall`.
|
||||
*
|
||||
* Like `localFieldStep`, this is an over-approximation: it is both instance-insensitive
|
||||
* and order-insensitive.
|
||||
*/
|
||||
private predicate instanceFieldStep(Node nodeFrom, LocalSourceNode nodeTo) {
|
||||
exists(Class cls, string attr, DataFlowPublic::AttrWrite write, DataFlowPublic::AttrRead read |
|
||||
selfAttrRef(cls, attr, write) and
|
||||
nodeFrom = write.getValue() and
|
||||
instanceAttrRead(cls, attr, read) and
|
||||
nodeTo = read
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in a way that discards call contexts.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
testFailures
|
||||
debug_callableNotUnique
|
||||
pointsTo_found_typeTracker_notFound
|
||||
| code/class_attr_assign.py:10:9:10:27 | ControlFlowNode for Attribute() | my_func |
|
||||
| code/class_attr_assign.py:11:9:11:25 | ControlFlowNode for Attribute() | my_func |
|
||||
| code/class_attr_assign.py:26:9:26:25 | ControlFlowNode for Attribute() | DummyObject.method |
|
||||
| code/class_super.py:50:1:50:6 | ControlFlowNode for Attribute() | outside_def |
|
||||
| code/conditional_in_argument.py:18:5:18:11 | ControlFlowNode for Attribute() | X.bar |
|
||||
| code/func_defined_outside_class.py:21:1:21:11 | ControlFlowNode for Attribute() | A.foo |
|
||||
|
||||
@@ -7,8 +7,8 @@ class Foo(object):
|
||||
self.direct_ref = my_func
|
||||
|
||||
def later(self):
|
||||
self.indirect_ref() # $ pt=my_func tt=my_func
|
||||
self.direct_ref() # $ pt=my_func tt=my_func
|
||||
self.indirect_ref() # $ pt=my_func MISSING: tt=my_func
|
||||
self.direct_ref() # $ pt=my_func MISSING: tt=my_func
|
||||
|
||||
foo = Foo(my_func) # $ tt=Foo.__init__
|
||||
foo.later() # $ pt,tt=Foo.later
|
||||
@@ -23,7 +23,7 @@ class Bar(object):
|
||||
self.obj = DummyObject()
|
||||
|
||||
def later(self):
|
||||
self.obj.method() # $ pt=DummyObject.method tt=DummyObject.method
|
||||
self.obj.method() # $ pt=DummyObject.method MISSING: tt=DummyObject.method
|
||||
|
||||
|
||||
bar = Bar(my_func) # $ tt=Bar.__init__
|
||||
|
||||
@@ -151,10 +151,10 @@ class MyClass2(object):
|
||||
self.foo = tracked # $ tracked=foo tracked
|
||||
|
||||
def print_foo(self): # $ MISSING: tracked=foo
|
||||
print(self.foo) # $ tracked MISSING: tracked=foo
|
||||
print(self.foo) # $ MISSING: tracked=foo tracked
|
||||
|
||||
def possibly_uncalled_method(self): # $ MISSING: tracked=foo
|
||||
print(self.foo) # $ tracked MISSING: tracked=foo
|
||||
print(self.foo) # $ MISSING: tracked=foo tracked
|
||||
|
||||
instance = MyClass2()
|
||||
print(instance.foo) # $ MISSING: tracked=foo tracked
|
||||
|
||||
@@ -9,28 +9,47 @@ cursor.executemany("some sql", (42,)) # $ getSql="some sql"
|
||||
cursor.close()
|
||||
|
||||
|
||||
# Connection stored in a class attribute (`self._conn`) and used in another method.
|
||||
#
|
||||
# This is detected because type tracking includes a level step modelling flow through
|
||||
# instance attributes: a value written to `self._conn` in one method (here `__init__`) can
|
||||
# be read back from `self._conn` (directly or via a getter) in any other method on the same
|
||||
# class. This follows the same approach used for instance fields in Ruby and JavaScript.
|
||||
class Database:
|
||||
# ---------------------------------------------------------------------------
|
||||
# Connection stored in a class attribute and accessed via various patterns
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class WrapperA:
|
||||
def __init__(self):
|
||||
self._conn = dbapi.connect(address="hostname", port=300, user="username")
|
||||
self._conn = dbapi.connect(address="hostname", port=300, user="username", pass_arg="testpass")
|
||||
|
||||
def get_connection(self):
|
||||
return self._conn
|
||||
|
||||
def run_via_getter(self):
|
||||
conn = self.get_connection()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("getter sql") # $ getSql="getter sql"
|
||||
|
||||
def run_direct(self):
|
||||
self._conn.execute("direct sql") # $ getSql="direct sql"
|
||||
# Getter called on a fresh constructor result
|
||||
conn_a1 = WrapperA().get_connection()
|
||||
cursor_a1 = conn_a1.cursor()
|
||||
cursor_a1.execute("some sql", (42,)) # $ MISSING: getSql="some sql"
|
||||
|
||||
# Getter called via a stored wrapper instance
|
||||
wrapper_instance = WrapperA()
|
||||
conn_a2 = wrapper_instance.get_connection()
|
||||
cursor_a2 = conn_a2.cursor()
|
||||
cursor_a2.execute("some sql", (42,)) # $ MISSING: getSql="some sql"
|
||||
|
||||
# Direct attribute access on a fresh constructor result
|
||||
conn_b = WrapperA()._conn
|
||||
cursor_b = conn_b.cursor()
|
||||
cursor_b.execute("some sql", (42,)) # $ MISSING: getSql="some sql"
|
||||
|
||||
|
||||
db = Database()
|
||||
db.run_via_getter()
|
||||
db.run_direct()
|
||||
class WrapperB:
|
||||
"""Stores the connection under a different attribute name."""
|
||||
|
||||
def __init__(self):
|
||||
self._hana = dbapi.connect(address="hostname", port=300, user="username", pass_arg="testpass")
|
||||
|
||||
def cursor(self):
|
||||
return self._hana.cursor()
|
||||
|
||||
|
||||
# Direct attribute access on a stored instance (mirrors hdb_con3 in the issue)
|
||||
conn_c = WrapperB()._hana
|
||||
cursor_c = conn_c.cursor()
|
||||
cursor_c.execute("some sql", (42,)) # $ MISSING: getSql="some sql"
|
||||
|
||||
@@ -1,36 +1,4 @@
|
||||
#select
|
||||
| app.py:23:20:23:24 | ControlFlowNode for query | app.py:20:18:20:21 | ControlFlowNode for name | app.py:23:20:23:24 | ControlFlowNode for query | This SQL query depends on a $@. | app.py:20:18:20:21 | ControlFlowNode for name | user-provided value |
|
||||
| app.py:30:20:30:24 | ControlFlowNode for query | app.py:27:19:27:22 | ControlFlowNode for name | app.py:30:20:30:24 | ControlFlowNode for query | This SQL query depends on a $@. | app.py:27:19:27:22 | ControlFlowNode for name | user-provided value |
|
||||
| app.py:37:20:37:24 | ControlFlowNode for query | app.py:34:19:34:22 | ControlFlowNode for name | app.py:37:20:37:24 | ControlFlowNode for query | This SQL query depends on a $@. | app.py:34:19:34:22 | ControlFlowNode for name | user-provided value |
|
||||
| app.py:44:20:44:24 | ControlFlowNode for query | app.py:41:19:41:22 | ControlFlowNode for name | app.py:44:20:44:24 | ControlFlowNode for query | This SQL query depends on a $@. | app.py:41:19:41:22 | ControlFlowNode for name | user-provided value |
|
||||
| app.py:51:20:51:24 | ControlFlowNode for query | app.py:48:19:48:22 | ControlFlowNode for name | app.py:51:20:51:24 | ControlFlowNode for query | This SQL query depends on a $@. | app.py:48:19:48:22 | ControlFlowNode for name | user-provided value |
|
||||
| sql_injection.py:21:24:21:77 | ControlFlowNode for BinaryExpr | sql_injection.py:14:15:14:22 | ControlFlowNode for username | sql_injection.py:21:24:21:77 | ControlFlowNode for BinaryExpr | This SQL query depends on a $@. | sql_injection.py:14:15:14:22 | ControlFlowNode for username | user-provided value |
|
||||
| sql_injection.py:24:38:24:95 | ControlFlowNode for BinaryExpr | sql_injection.py:14:15:14:22 | ControlFlowNode for username | sql_injection.py:24:38:24:95 | ControlFlowNode for BinaryExpr | This SQL query depends on a $@. | sql_injection.py:14:15:14:22 | ControlFlowNode for username | user-provided value |
|
||||
| sql_injection.py:25:26:25:83 | ControlFlowNode for BinaryExpr | sql_injection.py:14:15:14:22 | ControlFlowNode for username | sql_injection.py:25:26:25:83 | ControlFlowNode for BinaryExpr | This SQL query depends on a $@. | sql_injection.py:14:15:14:22 | ControlFlowNode for username | user-provided value |
|
||||
| sql_injection.py:26:28:26:85 | ControlFlowNode for BinaryExpr | sql_injection.py:14:15:14:22 | ControlFlowNode for username | sql_injection.py:26:28:26:85 | ControlFlowNode for BinaryExpr | This SQL query depends on a $@. | sql_injection.py:14:15:14:22 | ControlFlowNode for username | user-provided value |
|
||||
| sqlalchemy_textclause.py:27:28:27:87 | ControlFlowNode for Attribute() | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:27:28:27:87 | ControlFlowNode for Attribute() | This SQL query depends on a $@. | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | user-provided value |
|
||||
| sqlalchemy_textclause.py:31:50:31:72 | ControlFlowNode for Attribute() | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:31:50:31:72 | ControlFlowNode for Attribute() | This SQL query depends on a $@. | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | user-provided value |
|
||||
| sqlalchemy_textclause.py:41:26:41:33 | ControlFlowNode for username | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:41:26:41:33 | ControlFlowNode for username | This SQL query depends on a $@. | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | user-provided value |
|
||||
| sqlalchemy_textclause.py:42:31:42:38 | ControlFlowNode for username | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:42:31:42:38 | ControlFlowNode for username | This SQL query depends on a $@. | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | user-provided value |
|
||||
| sqlalchemy_textclause.py:43:30:43:37 | ControlFlowNode for username | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:43:30:43:37 | ControlFlowNode for username | This SQL query depends on a $@. | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | user-provided value |
|
||||
| sqlalchemy_textclause.py:44:35:44:42 | ControlFlowNode for username | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:44:35:44:42 | ControlFlowNode for username | This SQL query depends on a $@. | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | user-provided value |
|
||||
| sqlalchemy_textclause.py:45:41:45:48 | ControlFlowNode for username | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:45:41:45:48 | ControlFlowNode for username | This SQL query depends on a $@. | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | user-provided value |
|
||||
| sqlalchemy_textclause.py:46:46:46:53 | ControlFlowNode for username | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:46:46:46:53 | ControlFlowNode for username | This SQL query depends on a $@. | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | user-provided value |
|
||||
| sqlalchemy_textclause.py:47:47:47:54 | ControlFlowNode for username | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:47:47:47:54 | ControlFlowNode for username | This SQL query depends on a $@. | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | user-provided value |
|
||||
| sqlalchemy_textclause.py:48:52:48:59 | ControlFlowNode for username | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:48:52:48:59 | ControlFlowNode for username | This SQL query depends on a $@. | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | user-provided value |
|
||||
| sqlalchemy_textclause.py:50:18:50:25 | ControlFlowNode for username | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:50:18:50:25 | ControlFlowNode for username | This SQL query depends on a $@. | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | user-provided value |
|
||||
| sqlalchemy_textclause.py:51:24:51:31 | ControlFlowNode for username | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:51:24:51:31 | ControlFlowNode for username | This SQL query depends on a $@. | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | user-provided value |
|
||||
edges
|
||||
| app.py:20:18:20:21 | ControlFlowNode for name | app.py:21:5:21:9 | ControlFlowNode for query | provenance | |
|
||||
| app.py:21:5:21:9 | ControlFlowNode for query | app.py:23:20:23:24 | ControlFlowNode for query | provenance | |
|
||||
| app.py:27:19:27:22 | ControlFlowNode for name | app.py:28:5:28:9 | ControlFlowNode for query | provenance | |
|
||||
| app.py:28:5:28:9 | ControlFlowNode for query | app.py:30:20:30:24 | ControlFlowNode for query | provenance | |
|
||||
| app.py:34:19:34:22 | ControlFlowNode for name | app.py:35:5:35:9 | ControlFlowNode for query | provenance | |
|
||||
| app.py:35:5:35:9 | ControlFlowNode for query | app.py:37:20:37:24 | ControlFlowNode for query | provenance | |
|
||||
| app.py:41:19:41:22 | ControlFlowNode for name | app.py:42:5:42:9 | ControlFlowNode for query | provenance | |
|
||||
| app.py:42:5:42:9 | ControlFlowNode for query | app.py:44:20:44:24 | ControlFlowNode for query | provenance | |
|
||||
| app.py:48:19:48:22 | ControlFlowNode for name | app.py:49:5:49:9 | ControlFlowNode for query | provenance | |
|
||||
| app.py:49:5:49:9 | ControlFlowNode for query | app.py:51:20:51:24 | ControlFlowNode for query | provenance | |
|
||||
| sql_injection.py:14:15:14:22 | ControlFlowNode for username | sql_injection.py:21:24:21:77 | ControlFlowNode for BinaryExpr | provenance | |
|
||||
| sql_injection.py:14:15:14:22 | ControlFlowNode for username | sql_injection.py:24:38:24:95 | ControlFlowNode for BinaryExpr | provenance | |
|
||||
| sql_injection.py:14:15:14:22 | ControlFlowNode for username | sql_injection.py:25:26:25:83 | ControlFlowNode for BinaryExpr | provenance | |
|
||||
@@ -48,21 +16,6 @@ edges
|
||||
| sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:50:18:50:25 | ControlFlowNode for username | provenance | |
|
||||
| sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:51:24:51:31 | ControlFlowNode for username | provenance | |
|
||||
nodes
|
||||
| app.py:20:18:20:21 | ControlFlowNode for name | semmle.label | ControlFlowNode for name |
|
||||
| app.py:21:5:21:9 | ControlFlowNode for query | semmle.label | ControlFlowNode for query |
|
||||
| app.py:23:20:23:24 | ControlFlowNode for query | semmle.label | ControlFlowNode for query |
|
||||
| app.py:27:19:27:22 | ControlFlowNode for name | semmle.label | ControlFlowNode for name |
|
||||
| app.py:28:5:28:9 | ControlFlowNode for query | semmle.label | ControlFlowNode for query |
|
||||
| app.py:30:20:30:24 | ControlFlowNode for query | semmle.label | ControlFlowNode for query |
|
||||
| app.py:34:19:34:22 | ControlFlowNode for name | semmle.label | ControlFlowNode for name |
|
||||
| app.py:35:5:35:9 | ControlFlowNode for query | semmle.label | ControlFlowNode for query |
|
||||
| app.py:37:20:37:24 | ControlFlowNode for query | semmle.label | ControlFlowNode for query |
|
||||
| app.py:41:19:41:22 | ControlFlowNode for name | semmle.label | ControlFlowNode for name |
|
||||
| app.py:42:5:42:9 | ControlFlowNode for query | semmle.label | ControlFlowNode for query |
|
||||
| app.py:44:20:44:24 | ControlFlowNode for query | semmle.label | ControlFlowNode for query |
|
||||
| app.py:48:19:48:22 | ControlFlowNode for name | semmle.label | ControlFlowNode for name |
|
||||
| app.py:49:5:49:9 | ControlFlowNode for query | semmle.label | ControlFlowNode for query |
|
||||
| app.py:51:20:51:24 | ControlFlowNode for query | semmle.label | ControlFlowNode for query |
|
||||
| sql_injection.py:14:15:14:22 | ControlFlowNode for username | semmle.label | ControlFlowNode for username |
|
||||
| sql_injection.py:21:24:21:77 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr |
|
||||
| sql_injection.py:24:38:24:95 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr |
|
||||
@@ -82,3 +35,20 @@ nodes
|
||||
| sqlalchemy_textclause.py:50:18:50:25 | ControlFlowNode for username | semmle.label | ControlFlowNode for username |
|
||||
| sqlalchemy_textclause.py:51:24:51:31 | ControlFlowNode for username | semmle.label | ControlFlowNode for username |
|
||||
subpaths
|
||||
#select
|
||||
| sql_injection.py:21:24:21:77 | ControlFlowNode for BinaryExpr | sql_injection.py:14:15:14:22 | ControlFlowNode for username | sql_injection.py:21:24:21:77 | ControlFlowNode for BinaryExpr | This SQL query depends on a $@. | sql_injection.py:14:15:14:22 | ControlFlowNode for username | user-provided value |
|
||||
| sql_injection.py:24:38:24:95 | ControlFlowNode for BinaryExpr | sql_injection.py:14:15:14:22 | ControlFlowNode for username | sql_injection.py:24:38:24:95 | ControlFlowNode for BinaryExpr | This SQL query depends on a $@. | sql_injection.py:14:15:14:22 | ControlFlowNode for username | user-provided value |
|
||||
| sql_injection.py:25:26:25:83 | ControlFlowNode for BinaryExpr | sql_injection.py:14:15:14:22 | ControlFlowNode for username | sql_injection.py:25:26:25:83 | ControlFlowNode for BinaryExpr | This SQL query depends on a $@. | sql_injection.py:14:15:14:22 | ControlFlowNode for username | user-provided value |
|
||||
| sql_injection.py:26:28:26:85 | ControlFlowNode for BinaryExpr | sql_injection.py:14:15:14:22 | ControlFlowNode for username | sql_injection.py:26:28:26:85 | ControlFlowNode for BinaryExpr | This SQL query depends on a $@. | sql_injection.py:14:15:14:22 | ControlFlowNode for username | user-provided value |
|
||||
| sqlalchemy_textclause.py:27:28:27:87 | ControlFlowNode for Attribute() | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:27:28:27:87 | ControlFlowNode for Attribute() | This SQL query depends on a $@. | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | user-provided value |
|
||||
| sqlalchemy_textclause.py:31:50:31:72 | ControlFlowNode for Attribute() | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:31:50:31:72 | ControlFlowNode for Attribute() | This SQL query depends on a $@. | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | user-provided value |
|
||||
| sqlalchemy_textclause.py:41:26:41:33 | ControlFlowNode for username | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:41:26:41:33 | ControlFlowNode for username | This SQL query depends on a $@. | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | user-provided value |
|
||||
| sqlalchemy_textclause.py:42:31:42:38 | ControlFlowNode for username | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:42:31:42:38 | ControlFlowNode for username | This SQL query depends on a $@. | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | user-provided value |
|
||||
| sqlalchemy_textclause.py:43:30:43:37 | ControlFlowNode for username | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:43:30:43:37 | ControlFlowNode for username | This SQL query depends on a $@. | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | user-provided value |
|
||||
| sqlalchemy_textclause.py:44:35:44:42 | ControlFlowNode for username | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:44:35:44:42 | ControlFlowNode for username | This SQL query depends on a $@. | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | user-provided value |
|
||||
| sqlalchemy_textclause.py:45:41:45:48 | ControlFlowNode for username | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:45:41:45:48 | ControlFlowNode for username | This SQL query depends on a $@. | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | user-provided value |
|
||||
| sqlalchemy_textclause.py:46:46:46:53 | ControlFlowNode for username | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:46:46:46:53 | ControlFlowNode for username | This SQL query depends on a $@. | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | user-provided value |
|
||||
| sqlalchemy_textclause.py:47:47:47:54 | ControlFlowNode for username | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:47:47:47:54 | ControlFlowNode for username | This SQL query depends on a $@. | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | user-provided value |
|
||||
| sqlalchemy_textclause.py:48:52:48:59 | ControlFlowNode for username | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:48:52:48:59 | ControlFlowNode for username | This SQL query depends on a $@. | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | user-provided value |
|
||||
| sqlalchemy_textclause.py:50:18:50:25 | ControlFlowNode for username | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:50:18:50:25 | ControlFlowNode for username | This SQL query depends on a $@. | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | user-provided value |
|
||||
| sqlalchemy_textclause.py:51:24:51:31 | ControlFlowNode for username | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | sqlalchemy_textclause.py:51:24:51:31 | ControlFlowNode for username | This SQL query depends on a $@. | sqlalchemy_textclause.py:23:15:23:22 | ControlFlowNode for username | user-provided value |
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
query: Security/CWE-089/SqlInjection.ql
|
||||
postprocess: utils/test/InlineExpectationsTestQuery.ql
|
||||
Security/CWE-089/SqlInjection.ql
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
from fastapi import FastAPI
|
||||
from hdbcli import dbapi
|
||||
from db_connection import get_conn
|
||||
from db_connection import hdb_con
|
||||
from db_connection import hdb_con2
|
||||
from db_connection import hdb_con3
|
||||
app = FastAPI()
|
||||
|
||||
class DatabaseConnection:
|
||||
|
||||
def __init__(self):
|
||||
self._conn = dbapi.connect(address='localhost', port=30015, user='system', password='Password123')
|
||||
|
||||
def get_conn(self):
|
||||
return self._conn
|
||||
|
||||
db_connection = DatabaseConnection()
|
||||
|
||||
@app.get("/unsafe1/")
|
||||
async def unsafe(name: str): # $ Source
|
||||
query = "select * from users where name=" + name
|
||||
cursor = hdb_con.cursor()
|
||||
cursor.execute(query) # $ Alert
|
||||
cursor.close()
|
||||
|
||||
@app.get("/unsafe2/")
|
||||
async def unsafe2(name: str): # $ Source
|
||||
query = "select * from users where name=" + name
|
||||
cursor = hdb_con2.cursor()
|
||||
cursor.execute(query) # $ Alert
|
||||
cursor.close()
|
||||
|
||||
@app.get("/unsafe3/")
|
||||
async def unsafe3(name: str): # $ Source
|
||||
query = "select * from users where name=" + name
|
||||
cursor = hdb_con3.cursor()
|
||||
cursor.execute(query) # $ Alert
|
||||
cursor.close()
|
||||
|
||||
@app.get("/unsafe4/")
|
||||
async def unsafe4(name: str): # $ Source
|
||||
query = "select * from users where name=" + name
|
||||
cursor = get_conn().cursor()
|
||||
cursor.execute(query) # $ Alert
|
||||
cursor.close()
|
||||
|
||||
@app.get("/unsafe5/")
|
||||
async def unsafe5(name: str): # $ Source
|
||||
query = "select * from users where name=" + name
|
||||
cursor = db_connection.get_conn().cursor()
|
||||
cursor.execute(query) # $ Alert
|
||||
cursor.close()
|
||||
@@ -1,24 +0,0 @@
|
||||
from hdbcli import dbapi
|
||||
from typing import Optional
|
||||
|
||||
hdb_con = dbapi.connect(address='localhost', port=30015, user='system', password='Password123')
|
||||
|
||||
|
||||
class DatabaseConnection:
|
||||
|
||||
def __init__(self):
|
||||
self._conn = dbapi.connect(address='localhost', port=30015, user='system', password='Password123')
|
||||
|
||||
def get_conn(self):
|
||||
return self._conn
|
||||
|
||||
|
||||
hdb_con2 = DatabaseConnection().get_conn()
|
||||
hdb_con3 = DatabaseConnection()._conn
|
||||
|
||||
_hana_connection: Optional[DatabaseConnection] = None
|
||||
def get_conn():
|
||||
global _hana_connection
|
||||
if _hana_connection is None:
|
||||
_hana_connection = DatabaseConnection()
|
||||
return _hana_connection.get_conn()
|
||||
@@ -11,19 +11,19 @@ class User(models.Model):
|
||||
pass
|
||||
|
||||
@app.route("/users/<username>")
|
||||
def show_user(username): # $ Source
|
||||
def show_user(username):
|
||||
with connection.cursor() as cursor:
|
||||
# GOOD -- Using parameters
|
||||
cursor.execute("SELECT * FROM users WHERE username = %s", username)
|
||||
User.objects.raw("SELECT * FROM users WHERE username = %s", (username,))
|
||||
|
||||
# BAD -- Using string formatting
|
||||
cursor.execute("SELECT * FROM users WHERE username = '%s'" % username) # $ Alert
|
||||
cursor.execute("SELECT * FROM users WHERE username = '%s'" % username)
|
||||
|
||||
# BAD -- other ways of executing raw SQL code with string interpolation
|
||||
User.objects.annotate(RawSQL("insert into names_file ('name') values ('%s')" % username)) # $ Alert
|
||||
User.objects.raw("insert into names_file ('name') values ('%s')" % username) # $ Alert
|
||||
User.objects.extra("insert into names_file ('name') values ('%s')" % username) # $ Alert
|
||||
User.objects.annotate(RawSQL("insert into names_file ('name') values ('%s')" % username))
|
||||
User.objects.raw("insert into names_file ('name') values ('%s')" % username)
|
||||
User.objects.extra("insert into names_file ('name') values ('%s')" % username)
|
||||
|
||||
# BAD (but currently no custom query to find this)
|
||||
#
|
||||
|
||||
@@ -20,15 +20,15 @@ class User(Base):
|
||||
|
||||
|
||||
@app.route("/users/<username>")
|
||||
def show_user(username): # $ Source
|
||||
def show_user(username):
|
||||
session = sqlalchemy.orm.Session(engine)
|
||||
|
||||
# BAD, normal SQL injection
|
||||
stmt = sqlalchemy.text("SELECT * FROM users WHERE username = '{}'".format(username)) # $ Alert
|
||||
stmt = sqlalchemy.text("SELECT * FROM users WHERE username = '{}'".format(username))
|
||||
results = session.execute(stmt).fetchall()
|
||||
|
||||
# BAD, allows SQL injection
|
||||
username_formatted_for_sql = sqlalchemy.text("'{}'".format(username)) # $ Alert
|
||||
username_formatted_for_sql = sqlalchemy.text("'{}'".format(username))
|
||||
stmt = sqlalchemy.select(User).where(User.username == username_formatted_for_sql)
|
||||
results = session.execute(stmt).scalars().all()
|
||||
|
||||
@@ -38,14 +38,14 @@ def show_user(username): # $ Source
|
||||
|
||||
|
||||
# All of these should be flagged by query
|
||||
t1 = sqlalchemy.text(username) # $ Alert
|
||||
t2 = sqlalchemy.text(text=username) # $ Alert
|
||||
t3 = sqlalchemy.sql.text(username) # $ Alert
|
||||
t4 = sqlalchemy.sql.text(text=username) # $ Alert
|
||||
t5 = sqlalchemy.sql.expression.text(username) # $ Alert
|
||||
t6 = sqlalchemy.sql.expression.text(text=username) # $ Alert
|
||||
t7 = sqlalchemy.sql.expression.TextClause(username) # $ Alert
|
||||
t8 = sqlalchemy.sql.expression.TextClause(text=username) # $ Alert
|
||||
t1 = sqlalchemy.text(username)
|
||||
t2 = sqlalchemy.text(text=username)
|
||||
t3 = sqlalchemy.sql.text(username)
|
||||
t4 = sqlalchemy.sql.text(text=username)
|
||||
t5 = sqlalchemy.sql.expression.text(username)
|
||||
t6 = sqlalchemy.sql.expression.text(text=username)
|
||||
t7 = sqlalchemy.sql.expression.TextClause(username)
|
||||
t8 = sqlalchemy.sql.expression.TextClause(text=username)
|
||||
|
||||
t9 = db.text(username) # $ Alert
|
||||
t10 = db.text(text=username) # $ Alert
|
||||
t9 = db.text(username)
|
||||
t10 = db.text(text=username)
|
||||
|
||||
Reference in New Issue
Block a user