Skip to content

Commit 39753d5

Browse files
authored
Merge pull request #8693 from erik-krogh/pyApi
PY: more API-graphs refactorings
2 parents e1c7d36 + 7e4c76c commit 39753d5

File tree

13 files changed

+50
-166
lines changed

13 files changed

+50
-166
lines changed

python/ql/lib/semmle/python/frameworks/Asyncpg.qll

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ private module Asyncpg {
3333
string methodName;
3434

3535
SqlExecutionOnConnection() {
36-
methodName in ["copy_from_query", "execute", "fetch", "fetchrow", "fetchval", "executemany"] and
37-
this.calls([connectionPool().getAUse(), connection().getAUse()], methodName)
36+
this = [connectionPool(), connection()].getMember(methodName).getACall() and
37+
methodName in ["copy_from_query", "execute", "fetch", "fetchrow", "fetchval", "executemany"]
3838
}
3939

4040
override DataFlow::Node getSql() {
@@ -51,8 +51,8 @@ private module Asyncpg {
5151
string methodName;
5252

5353
FileAccessOnConnection() {
54-
methodName in ["copy_from_query", "copy_from_table", "copy_to_table"] and
55-
this.calls([connectionPool().getAUse(), connection().getAUse()], methodName)
54+
this = [connectionPool(), connection()].getMember(methodName).getACall() and
55+
methodName in ["copy_from_query", "copy_from_table", "copy_to_table"]
5656
}
5757

5858
// The path argument is keyword only.

python/ql/lib/semmle/python/frameworks/Cryptography.qll

Lines changed: 21 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ private module CryptographyModel {
2222
* Gets a predefined curve class from
2323
* `cryptography.hazmat.primitives.asymmetric.ec` with a specific key size (in bits).
2424
*/
25-
private API::Node predefinedCurveClass(int keySize) {
25+
API::Node predefinedCurveClass(int keySize) {
2626
exists(string curveName |
2727
result =
2828
API::moduleImport("cryptography")
@@ -73,41 +73,6 @@ private module CryptographyModel {
7373
curveName = "BrainpoolP512R1" and keySize = 512
7474
)
7575
}
76-
77-
/** Gets a reference to a predefined curve class with a specific key size (in bits), as well as the origin of the class. */
78-
private DataFlow::TypeTrackingNode curveClassWithKeySize(
79-
DataFlow::TypeTracker t, int keySize, DataFlow::Node origin
80-
) {
81-
t.start() and
82-
result = predefinedCurveClass(keySize).getAnImmediateUse() and
83-
origin = result
84-
or
85-
exists(DataFlow::TypeTracker t2 |
86-
result = curveClassWithKeySize(t2, keySize, origin).track(t2, t)
87-
)
88-
}
89-
90-
/** Gets a reference to a predefined curve class with a specific key size (in bits), as well as the origin of the class. */
91-
DataFlow::Node curveClassWithKeySize(int keySize, DataFlow::Node origin) {
92-
curveClassWithKeySize(DataFlow::TypeTracker::end(), keySize, origin).flowsTo(result)
93-
}
94-
95-
/** Gets a reference to a predefined curve class instance with a specific key size (in bits), as well as the origin of the class. */
96-
private DataFlow::TypeTrackingNode curveClassInstanceWithKeySize(
97-
DataFlow::TypeTracker t, int keySize, DataFlow::Node origin
98-
) {
99-
t.start() and
100-
result.(DataFlow::CallCfgNode).getFunction() = curveClassWithKeySize(keySize, origin)
101-
or
102-
exists(DataFlow::TypeTracker t2 |
103-
result = curveClassInstanceWithKeySize(t2, keySize, origin).track(t2, t)
104-
)
105-
}
106-
107-
/** Gets a reference to a predefined curve class instance with a specific key size (in bits), as well as the origin of the class. */
108-
DataFlow::Node curveClassInstanceWithKeySize(int keySize, DataFlow::Node origin) {
109-
curveClassInstanceWithKeySize(DataFlow::TypeTracker::end(), keySize, origin).flowsTo(result)
110-
}
11176
}
11277

11378
// ---------------------------------------------------------------------------
@@ -179,9 +144,13 @@ private module CryptographyModel {
179144
DataFlow::Node getCurveArg() { result in [this.getArg(0), this.getArgByName("curve")] }
180145

181146
override int getKeySizeWithOrigin(DataFlow::Node origin) {
182-
this.getCurveArg() = Ecc::curveClassInstanceWithKeySize(result, origin)
183-
or
184-
this.getCurveArg() = Ecc::curveClassWithKeySize(result, origin)
147+
exists(API::Node n |
148+
n = Ecc::predefinedCurveClass(result) and origin = n.getAnImmediateUse()
149+
|
150+
this.getCurveArg() = n.getAUse()
151+
or
152+
this.getCurveArg() = n.getReturn().getAUse()
153+
)
185154
}
186155

187156
// Note: There is not really a key-size argument, since it's always specified by the curve.
@@ -202,9 +171,8 @@ private module CryptographyModel {
202171
}
203172

204173
/** Gets a reference to a Cipher instance using algorithm with `algorithmName`. */
205-
DataFlow::TypeTrackingNode cipherInstance(DataFlow::TypeTracker t, string algorithmName) {
206-
t.start() and
207-
exists(DataFlow::CallCfgNode call | result = call |
174+
API::Node cipherInstance(string algorithmName) {
175+
exists(API::CallNode call | result = call.getReturn() |
208176
call =
209177
API::moduleImport("cryptography")
210178
.getMember("hazmat")
@@ -216,47 +184,6 @@ private module CryptographyModel {
216184
call.getArg(0), call.getArgByName("algorithm")
217185
]
218186
)
219-
or
220-
exists(DataFlow::TypeTracker t2 | result = cipherInstance(t2, algorithmName).track(t2, t))
221-
}
222-
223-
/** Gets a reference to a Cipher instance using algorithm with `algorithmName`. */
224-
DataFlow::Node cipherInstance(string algorithmName) {
225-
cipherInstance(DataFlow::TypeTracker::end(), algorithmName).flowsTo(result)
226-
}
227-
228-
/** Gets a reference to the encryptor of a Cipher instance using algorithm with `algorithmName`. */
229-
DataFlow::TypeTrackingNode cipherEncryptor(DataFlow::TypeTracker t, string algorithmName) {
230-
t.start() and
231-
result.(DataFlow::MethodCallNode).calls(cipherInstance(algorithmName), "encryptor")
232-
or
233-
exists(DataFlow::TypeTracker t2 | result = cipherEncryptor(t2, algorithmName).track(t2, t))
234-
}
235-
236-
/**
237-
* Gets a reference to the encryptor of a Cipher instance using algorithm with `algorithmName`.
238-
*
239-
* You obtain an encryptor by using the `encryptor()` method on a Cipher instance.
240-
*/
241-
DataFlow::Node cipherEncryptor(string algorithmName) {
242-
cipherEncryptor(DataFlow::TypeTracker::end(), algorithmName).flowsTo(result)
243-
}
244-
245-
/** Gets a reference to the dncryptor of a Cipher instance using algorithm with `algorithmName`. */
246-
DataFlow::TypeTrackingNode cipherDecryptor(DataFlow::TypeTracker t, string algorithmName) {
247-
t.start() and
248-
result.(DataFlow::MethodCallNode).calls(cipherInstance(algorithmName), "decryptor")
249-
or
250-
exists(DataFlow::TypeTracker t2 | result = cipherDecryptor(t2, algorithmName).track(t2, t))
251-
}
252-
253-
/**
254-
* Gets a reference to the decryptor of a Cipher instance using algorithm with `algorithmName`.
255-
*
256-
* You obtain an decryptor by using the `decryptor()` method on a Cipher instance.
257-
*/
258-
DataFlow::Node cipherDecryptor(string algorithmName) {
259-
cipherDecryptor(DataFlow::TypeTracker::end(), algorithmName).flowsTo(result)
260187
}
261188

262189
/**
@@ -267,11 +194,12 @@ private module CryptographyModel {
267194
string algorithmName;
268195

269196
CryptographyGenericCipherOperation() {
270-
exists(DataFlow::Node object, string method |
271-
object in [cipherEncryptor(algorithmName), cipherDecryptor(algorithmName)] and
272-
method in ["update", "update_into"] and
273-
this.calls(object, method)
274-
)
197+
this =
198+
cipherInstance(algorithmName)
199+
.getMember(["decryptor", "encryptor"])
200+
.getReturn()
201+
.getMember(["update", "update_into"])
202+
.getACall()
275203
}
276204

277205
override Cryptography::CryptographicAlgorithm getAlgorithm() {
@@ -298,9 +226,8 @@ private module CryptographyModel {
298226
}
299227

300228
/** Gets a reference to a Hash instance using algorithm with `algorithmName`. */
301-
private DataFlow::TypeTrackingNode hashInstance(DataFlow::TypeTracker t, string algorithmName) {
302-
t.start() and
303-
exists(DataFlow::CallCfgNode call | result = call |
229+
private API::Node hashInstance(string algorithmName) {
230+
exists(API::CallNode call | result = call.getReturn() |
304231
call =
305232
API::moduleImport("cryptography")
306233
.getMember("hazmat")
@@ -312,13 +239,6 @@ private module CryptographyModel {
312239
call.getArg(0), call.getArgByName("algorithm")
313240
]
314241
)
315-
or
316-
exists(DataFlow::TypeTracker t2 | result = hashInstance(t2, algorithmName).track(t2, t))
317-
}
318-
319-
/** Gets a reference to a Hash instance using algorithm with `algorithmName`. */
320-
DataFlow::Node hashInstance(string algorithmName) {
321-
hashInstance(DataFlow::TypeTracker::end(), algorithmName).flowsTo(result)
322242
}
323243

324244
/**
@@ -328,7 +248,9 @@ private module CryptographyModel {
328248
DataFlow::MethodCallNode {
329249
string algorithmName;
330250

331-
CryptographyGenericHashOperation() { this.calls(hashInstance(algorithmName), "update") }
251+
CryptographyGenericHashOperation() {
252+
this = hashInstance(algorithmName).getMember("update").getACall()
253+
}
332254

333255
override Cryptography::CryptographicAlgorithm getAlgorithm() {
334256
result.matchesName(algorithmName)

python/ql/lib/semmle/python/frameworks/Django.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,7 @@ module PrivateDjango {
554554

555555
/** A `django.db.connection` is a PEP249 compliant DB connection. */
556556
class DjangoDbConnection extends PEP249::Connection::InstanceSource {
557-
DjangoDbConnection() { this = connection().getAUse() }
557+
DjangoDbConnection() { this = connection().getAnImmediateUse() }
558558
}
559559

560560
// -------------------------------------------------------------------------

python/ql/lib/semmle/python/frameworks/Flask.qll

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -403,11 +403,8 @@ module Flask {
403403
}
404404

405405
private class RequestAttrMultiDict extends Werkzeug::MultiDict::InstanceSource {
406-
string attr_name;
407-
408406
RequestAttrMultiDict() {
409-
attr_name in ["args", "values", "form", "files"] and
410-
this.(DataFlow::AttrRead).accesses(request().getAUse(), attr_name)
407+
this = request().getMember(["args", "values", "form", "files"]).getAnImmediateUse()
411408
}
412409
}
413410

@@ -421,7 +418,7 @@ module Flask {
421418
// TODO: This approach for identifying member-access is very adhoc, and we should
422419
// be able to do something more structured for providing modeling of the members
423420
// of a container-object.
424-
exists(DataFlow::AttrRead files | files.accesses(request().getAUse(), "files") |
421+
exists(DataFlow::AttrRead files | files = request().getMember("files").getAnImmediateUse() |
425422
this.asCfgNode().(SubscriptNode).getObject() = files.asCfgNode()
426423
or
427424
this.(DataFlow::MethodCallNode).calls(files, "get")
@@ -435,15 +432,13 @@ module Flask {
435432

436433
/** An `Headers` instance that originates from a flask request. */
437434
private class FlaskRequestHeadersInstances extends Werkzeug::Headers::InstanceSource {
438-
FlaskRequestHeadersInstances() {
439-
this.(DataFlow::AttrRead).accesses(request().getAUse(), "headers")
440-
}
435+
FlaskRequestHeadersInstances() { this = request().getMember("headers").getAnImmediateUse() }
441436
}
442437

443438
/** An `Authorization` instance that originates from a flask request. */
444439
private class FlaskRequestAuthorizationInstances extends Werkzeug::Authorization::InstanceSource {
445440
FlaskRequestAuthorizationInstances() {
446-
this.(DataFlow::AttrRead).accesses(request().getAUse(), "authorization")
441+
this = request().getMember("authorization").getAnImmediateUse()
447442
}
448443
}
449444

python/ql/lib/semmle/python/frameworks/FlaskSqlAlchemy.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ private module FlaskSqlAlchemy {
3535
/** Access on a DB resulting in an Engine */
3636
private class DbEngine extends SqlAlchemy::Engine::InstanceSource {
3737
DbEngine() {
38-
this = dbInstance().getMember("engine").getAUse()
38+
this = dbInstance().getMember("engine").getAnImmediateUse()
3939
or
4040
this = dbInstance().getMember("get_engine").getACall()
4141
}
@@ -44,7 +44,7 @@ private module FlaskSqlAlchemy {
4444
/** Access on a DB resulting in a Session */
4545
private class DbSession extends SqlAlchemy::Session::InstanceSource {
4646
DbSession() {
47-
this = dbInstance().getMember("session").getAUse()
47+
this = dbInstance().getMember("session").getAnImmediateUse()
4848
or
4949
this = dbInstance().getMember("create_session").getReturn().getACall()
5050
or

python/ql/lib/semmle/python/frameworks/internal/SubclassFinder.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ private module NotExposed {
204204
FindSubclassesSpec spec, string newSubclassQualified, ClassExpr classExpr, Module mod,
205205
Location loc
206206
) {
207-
classExpr = newOrExistingModeling(spec).getASubclass*().getAUse().asExpr() and
207+
classExpr = newOrExistingModeling(spec).getASubclass*().getAnImmediateUse().asExpr() and
208208
classExpr.getScope() = mod and
209209
newSubclassQualified = mod.getName() + "." + classExpr.getName() and
210210
loc = classExpr.getLocation() and

python/ql/lib/semmle/python/regex.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ private string canonical_name(API::Node flag) {
7575
*/
7676
private DataFlow::TypeTrackingNode re_flag_tracker(string flag_name, DataFlow::TypeTracker t) {
7777
t.start() and
78-
exists(API::Node flag | flag_name = canonical_name(flag) and result = flag.getAUse())
78+
exists(API::Node flag | flag_name = canonical_name(flag) and result = flag.getAnImmediateUse())
7979
or
8080
exists(BinaryExprNode binop, DataFlow::Node operand |
8181
operand.getALocalSource() = re_flag_tracker(flag_name, t.continue()) and

python/ql/src/Security/CWE-215/FlaskDebug.ql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ private DataFlow::TypeTrackingNode truthyLiteral(DataFlow::TypeTracker t) {
2727
/** Gets a reference to a truthy literal. */
2828
DataFlow::Node truthyLiteral() { truthyLiteral(DataFlow::TypeTracker::end()).flowsTo(result) }
2929

30-
from DataFlow::CallCfgNode call, DataFlow::Node debugArg
30+
from API::CallNode call, DataFlow::Node debugArg
3131
where
32-
call.getFunction() = Flask::FlaskApp::instance().getMember("run").getAUse() and
32+
call = Flask::FlaskApp::instance().getMember("run").getACall() and
3333
debugArg in [call.getArg(2), call.getArgByName("debug")] and
3434
debugArg = truthyLiteral()
3535
select call,

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

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,8 @@ module ExperimentalFlask {
2727
}
2828

2929
/** Gets a reference to a header instance. */
30-
private DataFlow::LocalSourceNode headerInstance(DataFlow::TypeTracker t) {
31-
t.start() and
32-
result.(DataFlow::AttrRead).getObject().getALocalSource() =
33-
[Flask::Response::classRef(), flaskMakeResponse()].getReturn().getAUse()
34-
or
35-
exists(DataFlow::TypeTracker t2 | result = headerInstance(t2).track(t2, t))
36-
}
37-
38-
/** Gets a reference to a header instance use. */
39-
private DataFlow::Node headerInstance() {
40-
headerInstance(DataFlow::TypeTracker::end()).flowsTo(result)
30+
private DataFlow::LocalSourceNode headerInstance() {
31+
result = [Flask::Response::classRef(), flaskMakeResponse()].getReturn().getAMember().getAUse()
4132
}
4233

4334
/** Gets a reference to a header instance call/subscript */

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ private module NoSql {
6464
or
6565
result.(DataFlow::AttrRead).getObject() = mongoInstance().getAUse()
6666
or
67-
result = mongoDBInstance().getAUse()
67+
result = mongoDBInstance().getAnImmediateUse()
6868
)
6969
or
7070
exists(DataFlow::TypeTracker t2 | result = mongoDB(t2).track(t2, t))

0 commit comments

Comments
 (0)