Skip to content

Commit

Permalink
Merge pull request #8693 from erik-krogh/pyApi
Browse files Browse the repository at this point in the history
PY: more API-graphs refactorings
  • Loading branch information
yoff authored Apr 27, 2022
2 parents e1c7d36 + 7e4c76c commit 39753d5
Show file tree
Hide file tree
Showing 13 changed files with 50 additions and 166 deletions.
8 changes: 4 additions & 4 deletions python/ql/lib/semmle/python/frameworks/Asyncpg.qll
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ private module Asyncpg {
string methodName;

SqlExecutionOnConnection() {
methodName in ["copy_from_query", "execute", "fetch", "fetchrow", "fetchval", "executemany"] and
this.calls([connectionPool().getAUse(), connection().getAUse()], methodName)
this = [connectionPool(), connection()].getMember(methodName).getACall() and
methodName in ["copy_from_query", "execute", "fetch", "fetchrow", "fetchval", "executemany"]
}

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

FileAccessOnConnection() {
methodName in ["copy_from_query", "copy_from_table", "copy_to_table"] and
this.calls([connectionPool().getAUse(), connection().getAUse()], methodName)
this = [connectionPool(), connection()].getMember(methodName).getACall() and
methodName in ["copy_from_query", "copy_from_table", "copy_to_table"]
}

// The path argument is keyword only.
Expand Down
120 changes: 21 additions & 99 deletions python/ql/lib/semmle/python/frameworks/Cryptography.qll
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ private module CryptographyModel {
* Gets a predefined curve class from
* `cryptography.hazmat.primitives.asymmetric.ec` with a specific key size (in bits).
*/
private API::Node predefinedCurveClass(int keySize) {
API::Node predefinedCurveClass(int keySize) {
exists(string curveName |
result =
API::moduleImport("cryptography")
Expand Down Expand Up @@ -73,41 +73,6 @@ private module CryptographyModel {
curveName = "BrainpoolP512R1" and keySize = 512
)
}

/** Gets a reference to a predefined curve class with a specific key size (in bits), as well as the origin of the class. */
private DataFlow::TypeTrackingNode curveClassWithKeySize(
DataFlow::TypeTracker t, int keySize, DataFlow::Node origin
) {
t.start() and
result = predefinedCurveClass(keySize).getAnImmediateUse() and
origin = result
or
exists(DataFlow::TypeTracker t2 |
result = curveClassWithKeySize(t2, keySize, origin).track(t2, t)
)
}

/** Gets a reference to a predefined curve class with a specific key size (in bits), as well as the origin of the class. */
DataFlow::Node curveClassWithKeySize(int keySize, DataFlow::Node origin) {
curveClassWithKeySize(DataFlow::TypeTracker::end(), keySize, origin).flowsTo(result)
}

/** Gets a reference to a predefined curve class instance with a specific key size (in bits), as well as the origin of the class. */
private DataFlow::TypeTrackingNode curveClassInstanceWithKeySize(
DataFlow::TypeTracker t, int keySize, DataFlow::Node origin
) {
t.start() and
result.(DataFlow::CallCfgNode).getFunction() = curveClassWithKeySize(keySize, origin)
or
exists(DataFlow::TypeTracker t2 |
result = curveClassInstanceWithKeySize(t2, keySize, origin).track(t2, t)
)
}

/** Gets a reference to a predefined curve class instance with a specific key size (in bits), as well as the origin of the class. */
DataFlow::Node curveClassInstanceWithKeySize(int keySize, DataFlow::Node origin) {
curveClassInstanceWithKeySize(DataFlow::TypeTracker::end(), keySize, origin).flowsTo(result)
}
}

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

override int getKeySizeWithOrigin(DataFlow::Node origin) {
this.getCurveArg() = Ecc::curveClassInstanceWithKeySize(result, origin)
or
this.getCurveArg() = Ecc::curveClassWithKeySize(result, origin)
exists(API::Node n |
n = Ecc::predefinedCurveClass(result) and origin = n.getAnImmediateUse()
|
this.getCurveArg() = n.getAUse()
or
this.getCurveArg() = n.getReturn().getAUse()
)
}

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

/** Gets a reference to a Cipher instance using algorithm with `algorithmName`. */
DataFlow::TypeTrackingNode cipherInstance(DataFlow::TypeTracker t, string algorithmName) {
t.start() and
exists(DataFlow::CallCfgNode call | result = call |
API::Node cipherInstance(string algorithmName) {
exists(API::CallNode call | result = call.getReturn() |
call =
API::moduleImport("cryptography")
.getMember("hazmat")
Expand All @@ -216,47 +184,6 @@ private module CryptographyModel {
call.getArg(0), call.getArgByName("algorithm")
]
)
or
exists(DataFlow::TypeTracker t2 | result = cipherInstance(t2, algorithmName).track(t2, t))
}

/** Gets a reference to a Cipher instance using algorithm with `algorithmName`. */
DataFlow::Node cipherInstance(string algorithmName) {
cipherInstance(DataFlow::TypeTracker::end(), algorithmName).flowsTo(result)
}

/** Gets a reference to the encryptor of a Cipher instance using algorithm with `algorithmName`. */
DataFlow::TypeTrackingNode cipherEncryptor(DataFlow::TypeTracker t, string algorithmName) {
t.start() and
result.(DataFlow::MethodCallNode).calls(cipherInstance(algorithmName), "encryptor")
or
exists(DataFlow::TypeTracker t2 | result = cipherEncryptor(t2, algorithmName).track(t2, t))
}

/**
* Gets a reference to the encryptor of a Cipher instance using algorithm with `algorithmName`.
*
* You obtain an encryptor by using the `encryptor()` method on a Cipher instance.
*/
DataFlow::Node cipherEncryptor(string algorithmName) {
cipherEncryptor(DataFlow::TypeTracker::end(), algorithmName).flowsTo(result)
}

/** Gets a reference to the dncryptor of a Cipher instance using algorithm with `algorithmName`. */
DataFlow::TypeTrackingNode cipherDecryptor(DataFlow::TypeTracker t, string algorithmName) {
t.start() and
result.(DataFlow::MethodCallNode).calls(cipherInstance(algorithmName), "decryptor")
or
exists(DataFlow::TypeTracker t2 | result = cipherDecryptor(t2, algorithmName).track(t2, t))
}

/**
* Gets a reference to the decryptor of a Cipher instance using algorithm with `algorithmName`.
*
* You obtain an decryptor by using the `decryptor()` method on a Cipher instance.
*/
DataFlow::Node cipherDecryptor(string algorithmName) {
cipherDecryptor(DataFlow::TypeTracker::end(), algorithmName).flowsTo(result)
}

/**
Expand All @@ -267,11 +194,12 @@ private module CryptographyModel {
string algorithmName;

CryptographyGenericCipherOperation() {
exists(DataFlow::Node object, string method |
object in [cipherEncryptor(algorithmName), cipherDecryptor(algorithmName)] and
method in ["update", "update_into"] and
this.calls(object, method)
)
this =
cipherInstance(algorithmName)
.getMember(["decryptor", "encryptor"])
.getReturn()
.getMember(["update", "update_into"])
.getACall()
}

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

/** Gets a reference to a Hash instance using algorithm with `algorithmName`. */
private DataFlow::TypeTrackingNode hashInstance(DataFlow::TypeTracker t, string algorithmName) {
t.start() and
exists(DataFlow::CallCfgNode call | result = call |
private API::Node hashInstance(string algorithmName) {
exists(API::CallNode call | result = call.getReturn() |
call =
API::moduleImport("cryptography")
.getMember("hazmat")
Expand All @@ -312,13 +239,6 @@ private module CryptographyModel {
call.getArg(0), call.getArgByName("algorithm")
]
)
or
exists(DataFlow::TypeTracker t2 | result = hashInstance(t2, algorithmName).track(t2, t))
}

/** Gets a reference to a Hash instance using algorithm with `algorithmName`. */
DataFlow::Node hashInstance(string algorithmName) {
hashInstance(DataFlow::TypeTracker::end(), algorithmName).flowsTo(result)
}

/**
Expand All @@ -328,7 +248,9 @@ private module CryptographyModel {
DataFlow::MethodCallNode {
string algorithmName;

CryptographyGenericHashOperation() { this.calls(hashInstance(algorithmName), "update") }
CryptographyGenericHashOperation() {
this = hashInstance(algorithmName).getMember("update").getACall()
}

override Cryptography::CryptographicAlgorithm getAlgorithm() {
result.matchesName(algorithmName)
Expand Down
2 changes: 1 addition & 1 deletion python/ql/lib/semmle/python/frameworks/Django.qll
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ module PrivateDjango {

/** A `django.db.connection` is a PEP249 compliant DB connection. */
class DjangoDbConnection extends PEP249::Connection::InstanceSource {
DjangoDbConnection() { this = connection().getAUse() }
DjangoDbConnection() { this = connection().getAnImmediateUse() }
}

// -------------------------------------------------------------------------
Expand Down
13 changes: 4 additions & 9 deletions python/ql/lib/semmle/python/frameworks/Flask.qll
Original file line number Diff line number Diff line change
Expand Up @@ -403,11 +403,8 @@ module Flask {
}

private class RequestAttrMultiDict extends Werkzeug::MultiDict::InstanceSource {
string attr_name;

RequestAttrMultiDict() {
attr_name in ["args", "values", "form", "files"] and
this.(DataFlow::AttrRead).accesses(request().getAUse(), attr_name)
this = request().getMember(["args", "values", "form", "files"]).getAnImmediateUse()
}
}

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

/** An `Headers` instance that originates from a flask request. */
private class FlaskRequestHeadersInstances extends Werkzeug::Headers::InstanceSource {
FlaskRequestHeadersInstances() {
this.(DataFlow::AttrRead).accesses(request().getAUse(), "headers")
}
FlaskRequestHeadersInstances() { this = request().getMember("headers").getAnImmediateUse() }
}

/** An `Authorization` instance that originates from a flask request. */
private class FlaskRequestAuthorizationInstances extends Werkzeug::Authorization::InstanceSource {
FlaskRequestAuthorizationInstances() {
this.(DataFlow::AttrRead).accesses(request().getAUse(), "authorization")
this = request().getMember("authorization").getAnImmediateUse()
}
}

Expand Down
4 changes: 2 additions & 2 deletions python/ql/lib/semmle/python/frameworks/FlaskSqlAlchemy.qll
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ private module FlaskSqlAlchemy {
/** Access on a DB resulting in an Engine */
private class DbEngine extends SqlAlchemy::Engine::InstanceSource {
DbEngine() {
this = dbInstance().getMember("engine").getAUse()
this = dbInstance().getMember("engine").getAnImmediateUse()
or
this = dbInstance().getMember("get_engine").getACall()
}
Expand All @@ -44,7 +44,7 @@ private module FlaskSqlAlchemy {
/** Access on a DB resulting in a Session */
private class DbSession extends SqlAlchemy::Session::InstanceSource {
DbSession() {
this = dbInstance().getMember("session").getAUse()
this = dbInstance().getMember("session").getAnImmediateUse()
or
this = dbInstance().getMember("create_session").getReturn().getACall()
or
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ private module NotExposed {
FindSubclassesSpec spec, string newSubclassQualified, ClassExpr classExpr, Module mod,
Location loc
) {
classExpr = newOrExistingModeling(spec).getASubclass*().getAUse().asExpr() and
classExpr = newOrExistingModeling(spec).getASubclass*().getAnImmediateUse().asExpr() and
classExpr.getScope() = mod and
newSubclassQualified = mod.getName() + "." + classExpr.getName() and
loc = classExpr.getLocation() and
Expand Down
2 changes: 1 addition & 1 deletion python/ql/lib/semmle/python/regex.qll
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ private string canonical_name(API::Node flag) {
*/
private DataFlow::TypeTrackingNode re_flag_tracker(string flag_name, DataFlow::TypeTracker t) {
t.start() and
exists(API::Node flag | flag_name = canonical_name(flag) and result = flag.getAUse())
exists(API::Node flag | flag_name = canonical_name(flag) and result = flag.getAnImmediateUse())
or
exists(BinaryExprNode binop, DataFlow::Node operand |
operand.getALocalSource() = re_flag_tracker(flag_name, t.continue()) and
Expand Down
4 changes: 2 additions & 2 deletions python/ql/src/Security/CWE-215/FlaskDebug.ql
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ private DataFlow::TypeTrackingNode truthyLiteral(DataFlow::TypeTracker t) {
/** Gets a reference to a truthy literal. */
DataFlow::Node truthyLiteral() { truthyLiteral(DataFlow::TypeTracker::end()).flowsTo(result) }

from DataFlow::CallCfgNode call, DataFlow::Node debugArg
from API::CallNode call, DataFlow::Node debugArg
where
call.getFunction() = Flask::FlaskApp::instance().getMember("run").getAUse() and
call = Flask::FlaskApp::instance().getMember("run").getACall() and
debugArg in [call.getArg(2), call.getArgByName("debug")] and
debugArg = truthyLiteral()
select call,
Expand Down
13 changes: 2 additions & 11 deletions python/ql/src/experimental/semmle/python/frameworks/Flask.qll
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,8 @@ module ExperimentalFlask {
}

/** Gets a reference to a header instance. */
private DataFlow::LocalSourceNode headerInstance(DataFlow::TypeTracker t) {
t.start() and
result.(DataFlow::AttrRead).getObject().getALocalSource() =
[Flask::Response::classRef(), flaskMakeResponse()].getReturn().getAUse()
or
exists(DataFlow::TypeTracker t2 | result = headerInstance(t2).track(t2, t))
}

/** Gets a reference to a header instance use. */
private DataFlow::Node headerInstance() {
headerInstance(DataFlow::TypeTracker::end()).flowsTo(result)
private DataFlow::LocalSourceNode headerInstance() {
result = [Flask::Response::classRef(), flaskMakeResponse()].getReturn().getAMember().getAUse()
}

/** Gets a reference to a header instance call/subscript */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ private module NoSql {
or
result.(DataFlow::AttrRead).getObject() = mongoInstance().getAUse()
or
result = mongoDBInstance().getAUse()
result = mongoDBInstance().getAnImmediateUse()
)
or
exists(DataFlow::TypeTracker t2 | result = mongoDB(t2).track(t2, t))
Expand Down
Loading

0 comments on commit 39753d5

Please sign in to comment.