Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e1dfbc0

Browse files
committed
Python: address review
1 parent 01845d1 commit e1dfbc0

2 files changed

Lines changed: 83 additions & 74 deletions

File tree

python/ql/src/experimental/semmle/python/frameworks/Django.qll

Lines changed: 73 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -55,35 +55,38 @@ private module Django {
5555
/** Gets a reference to the `django.db.connection` object. */
5656
DataFlow::Node connection() { result = connection(DataFlow::TypeTracker::end()) }
5757

58-
/** Gets a reference to the `django.db.connection.cursor` class. */
59-
private DataFlow::Node classCursor(DataFlow::TypeTracker t) {
60-
t.start() and
61-
result = DataFlow::importNode("django.db.connection.cursor")
62-
or
63-
t.startInAttr("cursor") and
64-
result = connection()
65-
or
66-
exists(DataFlow::TypeTracker t2 | result = classCursor(t2).track(t2, t))
67-
}
58+
/** Provides models for the `django.db.connection.cursor` method. */
59+
module cursor {
60+
/** Gets a reference to the `django.db.connection.cursor` metod. */
61+
private DataFlow::Node methodRef(DataFlow::TypeTracker t) {
62+
t.start() and
63+
result = DataFlow::importNode("django.db.connection.cursor")
64+
or
65+
t.startInAttr("cursor") and
66+
result = connection()
67+
or
68+
exists(DataFlow::TypeTracker t2 | result = methodRef(t2).track(t2, t))
69+
}
6870

69-
/** Gets a reference to the `django.db.connection.cursor` class. */
70-
DataFlow::Node classCursor() { result = classCursor(DataFlow::TypeTracker::end()) }
71+
/** Gets a reference to the `django.db.connection.cursor` metod. */
72+
DataFlow::Node methodRef() { result = methodRef(DataFlow::TypeTracker::end()) }
7173

72-
/** Gets a reference to an instance of `django.db.connection.cursor`. */
73-
private DataFlow::Node cursor(DataFlow::TypeTracker t) {
74-
t.start() and
75-
result.asCfgNode().(CallNode).getFunction() = classCursor().asCfgNode()
76-
or
77-
exists(DataFlow::TypeTracker t2 | result = cursor(t2).track(t2, t))
78-
}
74+
/** Gets a reference to a result of calling `django.db.connection.cursor`. */
75+
private DataFlow::Node methodResult(DataFlow::TypeTracker t) {
76+
t.start() and
77+
result.asCfgNode().(CallNode).getFunction() = methodRef().asCfgNode()
78+
or
79+
exists(DataFlow::TypeTracker t2 | result = methodResult(t2).track(t2, t))
80+
}
7981

80-
/** Gets a reference to an instance of `django.db.connection.cursor`. */
81-
DataFlow::Node cursor() { result = cursor(DataFlow::TypeTracker::end()) }
82+
/** Gets a reference to a result of calling `django.db.connection.cursor`. */
83+
DataFlow::Node methodResult() { result = methodResult(DataFlow::TypeTracker::end()) }
84+
}
8285

8386
/** Gets a reference to the `django.db.connection.cursor.execute` function. */
8487
private DataFlow::Node execute(DataFlow::TypeTracker t) {
8588
t.startInAttr("execute") and
86-
result = cursor()
89+
result = cursor::methodResult()
8790
or
8891
exists(DataFlow::TypeTracker t2 | result = execute(t2).track(t2, t))
8992
}
@@ -107,49 +110,59 @@ private module Django {
107110

108111
/** Provides models for the `django.db.models` module. */
109112
module models {
110-
/** Gets a reference to the `django.db.models.Model` class. */
111-
private DataFlow::Node classModel(DataFlow::TypeTracker t) {
112-
t.start() and
113-
result = DataFlow::importNode("django.db.models.Model")
114-
or
115-
t.startInAttr("Model") and
116-
result = models()
117-
or
118-
exists(DataFlow::TypeTracker t2 | result = classModel(t2).track(t2, t))
119-
}
113+
/** Provides models for the `django.db.models.Model` class. */
114+
module Model {
115+
/** Gets a reference to the `django.db.models.Model` class. */
116+
private DataFlow::Node classRef(DataFlow::TypeTracker t) {
117+
t.start() and
118+
result = DataFlow::importNode("django.db.models.Model")
119+
or
120+
t.startInAttr("Model") and
121+
result = models()
122+
or
123+
exists(DataFlow::TypeTracker t2 | result = classRef(t2).track(t2, t))
124+
}
120125

121-
/** Gets a reference to the `django.db.models.Model` class. */
122-
DataFlow::Node classModel() { result = classModel(DataFlow::TypeTracker::end()) }
126+
/** Gets a reference to the `django.db.models.Model` class. */
127+
DataFlow::Node classRef() { result = classRef(DataFlow::TypeTracker::end()) }
123128

124-
/** Gets a definition of a subclass the `django.db.models.Model` class. */
125-
class ClassModelSubclassDef extends ControlFlowNode {
126-
string name;
129+
/** Gets a definition of a subclass the `django.db.models.Model` class. */
130+
class SubclassDef extends ControlFlowNode {
131+
string name;
127132

128-
ClassModelSubclassDef() {
129-
exists(ClassExpr ce |
130-
this.getNode() = ce and
131-
ce.getABase() = classModel().asExpr() and
132-
ce.getName() = name
133-
)
134-
}
133+
SubclassDef() {
134+
exists(ClassExpr ce |
135+
this.getNode() = ce and
136+
ce.getABase() = classRef().asExpr() and
137+
ce.getName() = name
138+
)
139+
}
135140

136-
string getName() { result = name }
137-
}
141+
string getName() { result = name }
142+
}
138143

139-
/**
140-
* A reference to a class that is a subclass of the `django.db.models.Model` class.
141-
* This is quite an approximation, since it simply matches identifiers.
142-
*/
143-
class ClassModelSubclass extends DataFlow::CfgNode {
144-
override NameNode node;
144+
/**
145+
* A reference to a class that is a subclass of the `django.db.models.Model` class.
146+
* This is an approximation, since it simply matches identifiers.
147+
*/
148+
private DataFlow::Node subclassRef(DataFlow::TypeTracker t) {
149+
t.start() and
150+
result.asCfgNode().(NameNode).getId() = any(SubclassDef cd).getName()
151+
or
152+
exists(DataFlow::TypeTracker t2 | result = subclassRef(t2).track(t2, t))
153+
}
145154

146-
ClassModelSubclass() { node.getId() = any(ClassModelSubclassDef cd).getName() }
155+
/**
156+
* A reference to a class that is a subclass of the `django.db.models.Model` class.
157+
* This is an approximation, since it simply matches identifiers.
158+
*/
159+
DataFlow::Node subclassRef() { result = subclassRef(DataFlow::TypeTracker::end()) }
147160
}
148161

149162
/** Gets a reference to the `objects` object of a django model. */
150163
private DataFlow::Node objects(DataFlow::TypeTracker t) {
151164
t.startInAttr("objects") and
152-
result instanceof ClassModelSubclass
165+
result = Model::subclassRef()
153166
or
154167
exists(DataFlow::TypeTracker t2 | result = objects(t2).track(t2, t))
155168
}
@@ -239,7 +252,7 @@ private module Django {
239252

240253
ObjectsAnnotate() {
241254
node.getFunction() = django::db::models::objects_attr("annotate").asCfgNode() and
242-
raw = node.getArg(0) and
255+
raw in [node.getArg(0), node.getArgByName(_)] and
243256
raw.getFunction() = django::db::models::classRawSQL().asCfgNode()
244257
}
245258

@@ -261,6 +274,10 @@ private module Django {
261274

262275
ObjectsExtra() { node.getFunction() = django::db::models::objects_attr("extra").asCfgNode() }
263276

264-
override DataFlow::Node getSql() { result.asCfgNode() = node.getArg(0) }
277+
override DataFlow::Node getSql() {
278+
result.asCfgNode() =
279+
[node.getArg([0 .. 5]),
280+
node.getArgByName(["select", "where", "params", "tables", "order_by", "select_params"])]
281+
}
265282
}
266283
}

python/ql/test/experimental/library-tests/frameworks/django/SqlExecution.py

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,22 @@
22
from django.db.models.expressions import RawSQL
33

44

5-
def test_plain(username):
6-
# GOOD -- Using parameters
7-
connection.cursor().execute("SELECT * FROM users WHERE username = %s", username) # $getSql="SELECT * FROM users WHERE username = %s"
5+
def test_plain():
6+
cursor = connection.cursor()
7+
cursor.execute("some sql") # $getSql="some sql"
88

9-
# BAD -- Using string formatting
10-
connection.cursor().execute("SELECT * FROM users WHERE username = '%s'" % username) # $getSql=BinaryExpr
119

12-
13-
def test_context(username):
10+
def test_context():
1411
with connection.cursor() as cursor:
15-
# GOOD -- Using parameters
16-
cursor.execute("SELECT * FROM users WHERE username = %s", username) # $getSql="SELECT * FROM users WHERE username = %s"
12+
cursor.execute("some sql") # $getSql="some sql"
1713

18-
# BAD -- Using string formatting
19-
cursor.execute("SELECT * FROM users WHERE username = '%s'" % username) # $getSql=BinaryExpr
2014

2115
class User(models.Model):
2216
pass
2317

24-
def test_model(username):
25-
# GOOD -- Using parameters
26-
User.objects.raw("SELECT * FROM users WHERE username = %s", (username,)) # $getSql="SELECT * FROM users WHERE username = %s"
2718

28-
# BAD -- other ways of executing raw SQL code with string interpolation
29-
User.objects.annotate(RawSQL("insert into names_file ('name') values ('%s')" % username)) # $getSql=BinaryExpr
30-
User.objects.raw("insert into names_file ('name') values ('%s')" % username) # $getSql=BinaryExpr
31-
User.objects.extra("insert into names_file ('name') values ('%s')" % username) # $getSql=BinaryExpr
19+
def test_model():
20+
User.objects.raw("some sql") # $getSql="some sql"
21+
User.objects.annotate(RawSQL("some sql")) # $getSql="some sql"
22+
User.objects.annotate(val=RawSQL("some sql")) # $getSql="some sql"
23+
User.objects.extra("some sql") # $getSql="some sql"

0 commit comments

Comments
 (0)