From 4e5afab08232e8d724f054e8360d19d817893f30 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 7 Apr 2022 12:08:22 +0200 Subject: [PATCH 1/4] refactor more python type-trackers to API-graphs --- .../semmle/python/frameworks/Cryptography.qll | 120 +++--------------- .../semmle/python/frameworks/Xml.qll | 38 +----- 2 files changed, 28 insertions(+), 130 deletions(-) diff --git a/python/ql/lib/semmle/python/frameworks/Cryptography.qll b/python/ql/lib/semmle/python/frameworks/Cryptography.qll index 211f429cb98b..b23c42362e6c 100644 --- a/python/ql/lib/semmle/python/frameworks/Cryptography.qll +++ b/python/ql/lib/semmle/python/frameworks/Cryptography.qll @@ -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") @@ -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) - } } // --------------------------------------------------------------------------- @@ -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. @@ -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") @@ -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) } /** @@ -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() { @@ -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") @@ -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) } /** @@ -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) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Xml.qll b/python/ql/src/experimental/semmle/python/frameworks/Xml.qll index a2f36f66f2e3..eb1899fdd18f 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Xml.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Xml.qll @@ -119,7 +119,7 @@ private module SaxBasedParsing { * * See https://docs.python.org/3.10/library/xml.sax.reader.html#xml.sax.xmlreader.XMLReader.setFeature */ - class SaxParserSetFeatureCall extends DataFlow::MethodCallNode { + class SaxParserSetFeatureCall extends API::CallNode, DataFlow::MethodCallNode { SaxParserSetFeatureCall() { this = API::moduleImport("xml") @@ -132,27 +132,9 @@ private module SaxBasedParsing { // The keyword argument names does not match documentation. I checked (with Python // 3.9.5) that the names used here actually works. - DataFlow::Node getFeatureArg() { result in [this.getArg(0), this.getArgByName("name")] } + API::Node getFeatureArg() { result = this.getParameter(0, "name") } - DataFlow::Node getStateArg() { result in [this.getArg(1), this.getArgByName("state")] } - } - - /** Gets a back-reference to the `setFeature` state argument `arg`. */ - private DataFlow::TypeTrackingNode saxParserSetFeatureStateArgBacktracker( - DataFlow::TypeBackTracker t, DataFlow::Node arg - ) { - t.start() and - arg = any(SaxParserSetFeatureCall c).getStateArg() and - result = arg.getALocalSource() - or - exists(DataFlow::TypeBackTracker t2 | - result = saxParserSetFeatureStateArgBacktracker(t2, arg).backtrack(t2, t) - ) - } - - /** Gets a back-reference to the `setFeature` state argument `arg`. */ - DataFlow::LocalSourceNode saxParserSetFeatureStateArgBacktracker(DataFlow::Node arg) { - result = saxParserSetFeatureStateArgBacktracker(DataFlow::TypeBackTracker::end(), arg) + API::Node getStateArg() { result = this.getParameter(1, "state") } } /** @@ -163,16 +145,13 @@ private module SaxBasedParsing { private DataFlow::Node saxParserWithFeatureExternalGesTurnedOn(DataFlow::TypeTracker t) { t.start() and exists(SaxParserSetFeatureCall call | - call.getFeatureArg() = + call.getFeatureArg().getARhs() = API::moduleImport("xml") .getMember("sax") .getMember("handler") .getMember("feature_external_ges") .getAUse() and - saxParserSetFeatureStateArgBacktracker(call.getStateArg()) - .asExpr() - .(BooleanLiteral) - .booleanValue() = true and + call.getStateArg().getAValueReachingRhs().asExpr().(BooleanLiteral).booleanValue() = true and result = call.getObject() ) or @@ -182,16 +161,13 @@ private module SaxBasedParsing { // take account of that we can set the feature to False, which makes the parser safe again not exists(SaxParserSetFeatureCall call | call.getObject() = result and - call.getFeatureArg() = + call.getFeatureArg().getARhs() = API::moduleImport("xml") .getMember("sax") .getMember("handler") .getMember("feature_external_ges") .getAUse() and - saxParserSetFeatureStateArgBacktracker(call.getStateArg()) - .asExpr() - .(BooleanLiteral) - .booleanValue() = false + call.getStateArg().getAValueReachingRhs().asExpr().(BooleanLiteral).booleanValue() = false ) } From 50bfc8eaa09592b22826d50ee6f7a0e896c4f9de Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 7 Apr 2022 12:33:37 +0200 Subject: [PATCH 2/4] refactor uses of API::Node::getAUse() that should have been something else --- python/ql/lib/semmle/python/frameworks/Asyncpg.qll | 8 ++++---- python/ql/lib/semmle/python/frameworks/Django.qll | 2 +- python/ql/lib/semmle/python/frameworks/Flask.qll | 14 ++++++-------- .../semmle/python/frameworks/FlaskSqlAlchemy.qll | 4 ++-- .../python/frameworks/internal/SubclassFinder.qll | 2 +- python/ql/lib/semmle/python/regex.qll | 2 +- python/ql/src/Security/CWE-215/FlaskDebug.ql | 4 ++-- .../semmle/python/frameworks/Flask.qll | 13 ++----------- .../semmle/python/frameworks/NoSQL.qll | 2 +- .../dataflow/typetracking/moduleattr.ql | 2 +- .../experimental/dataflow/typetracking/tracked.ql | 6 +++--- 11 files changed, 24 insertions(+), 35 deletions(-) diff --git a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll index 8c1892164b62..45c274c64bad 100644 --- a/python/ql/lib/semmle/python/frameworks/Asyncpg.qll +++ b/python/ql/lib/semmle/python/frameworks/Asyncpg.qll @@ -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() { @@ -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. diff --git a/python/ql/lib/semmle/python/frameworks/Django.qll b/python/ql/lib/semmle/python/frameworks/Django.qll index a60c577cd406..d1478526b837 100644 --- a/python/ql/lib/semmle/python/frameworks/Django.qll +++ b/python/ql/lib/semmle/python/frameworks/Django.qll @@ -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() } } // ------------------------------------------------------------------------- diff --git a/python/ql/lib/semmle/python/frameworks/Flask.qll b/python/ql/lib/semmle/python/frameworks/Flask.qll index 0059a8a11542..a5357b497d4a 100644 --- a/python/ql/lib/semmle/python/frameworks/Flask.qll +++ b/python/ql/lib/semmle/python/frameworks/Flask.qll @@ -339,7 +339,7 @@ module Flask { */ private class FlaskRequestSource extends RemoteFlowSource::Range { FlaskRequestSource() { - this = request().getAUse() and + this = request().getAnImmediateUse() and not any(Import imp).contains(this.asExpr()) and not exists(ControlFlowNode def | this.asVar().getSourceVariable().hasDefiningNode(def) | any(Import imp).contains(def.getNode()) @@ -406,8 +406,8 @@ module Flask { string attr_name; RequestAttrMultiDict() { - attr_name in ["args", "values", "form", "files"] and - this.(DataFlow::AttrRead).accesses(request().getAUse(), attr_name) + this = request().getMember(attr_name).getAnImmediateUse() and + attr_name in ["args", "values", "form", "files"] } } @@ -421,7 +421,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") @@ -435,15 +435,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() } } diff --git a/python/ql/lib/semmle/python/frameworks/FlaskSqlAlchemy.qll b/python/ql/lib/semmle/python/frameworks/FlaskSqlAlchemy.qll index 08ba276e6ce4..6269baee9d52 100644 --- a/python/ql/lib/semmle/python/frameworks/FlaskSqlAlchemy.qll +++ b/python/ql/lib/semmle/python/frameworks/FlaskSqlAlchemy.qll @@ -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() } @@ -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 diff --git a/python/ql/lib/semmle/python/frameworks/internal/SubclassFinder.qll b/python/ql/lib/semmle/python/frameworks/internal/SubclassFinder.qll index c2a24358ad28..8bef349f4174 100644 --- a/python/ql/lib/semmle/python/frameworks/internal/SubclassFinder.qll +++ b/python/ql/lib/semmle/python/frameworks/internal/SubclassFinder.qll @@ -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 diff --git a/python/ql/lib/semmle/python/regex.qll b/python/ql/lib/semmle/python/regex.qll index 5f3dea9258ea..b72adba1716b 100644 --- a/python/ql/lib/semmle/python/regex.qll +++ b/python/ql/lib/semmle/python/regex.qll @@ -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 diff --git a/python/ql/src/Security/CWE-215/FlaskDebug.ql b/python/ql/src/Security/CWE-215/FlaskDebug.ql index e63fe1f3af66..e7a1d91f08c8 100644 --- a/python/ql/src/Security/CWE-215/FlaskDebug.ql +++ b/python/ql/src/Security/CWE-215/FlaskDebug.ql @@ -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, diff --git a/python/ql/src/experimental/semmle/python/frameworks/Flask.qll b/python/ql/src/experimental/semmle/python/frameworks/Flask.qll index 9c66d9a46010..aec6dbc6c8f3 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Flask.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Flask.qll @@ -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 */ diff --git a/python/ql/src/experimental/semmle/python/frameworks/NoSQL.qll b/python/ql/src/experimental/semmle/python/frameworks/NoSQL.qll index bdd067218b3d..a472e5034355 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/NoSQL.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/NoSQL.qll @@ -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)) diff --git a/python/ql/test/experimental/dataflow/typetracking/moduleattr.ql b/python/ql/test/experimental/dataflow/typetracking/moduleattr.ql index e578670885b5..913759040433 100644 --- a/python/ql/test/experimental/dataflow/typetracking/moduleattr.ql +++ b/python/ql/test/experimental/dataflow/typetracking/moduleattr.ql @@ -5,7 +5,7 @@ import semmle.python.ApiGraphs private DataFlow::TypeTrackingNode module_tracker(TypeTracker t) { t.start() and - result = API::moduleImport("module").getAUse() + result = API::moduleImport("module").getAnImmediateUse() or exists(TypeTracker t2 | result = module_tracker(t2).track(t2, t)) } diff --git a/python/ql/test/experimental/dataflow/typetracking/tracked.ql b/python/ql/test/experimental/dataflow/typetracking/tracked.ql index 5748d660e436..ffc1d43ca72e 100644 --- a/python/ql/test/experimental/dataflow/typetracking/tracked.ql +++ b/python/ql/test/experimental/dataflow/typetracking/tracked.ql @@ -120,7 +120,7 @@ class TrackedSelfTest extends InlineExpectationsTest { /** Gets a reference to `foo` (fictive module). */ private DataFlow::TypeTrackingNode foo(DataFlow::TypeTracker t) { t.start() and - result = API::moduleImport("foo").getAUse() + result = API::moduleImport("foo").getAnImmediateUse() or exists(DataFlow::TypeTracker t2 | result = foo(t2).track(t2, t)) } @@ -131,7 +131,7 @@ DataFlow::Node foo() { foo(DataFlow::TypeTracker::end()).flowsTo(result) } /** Gets a reference to `foo.bar` (fictive module). */ private DataFlow::TypeTrackingNode foo_bar(DataFlow::TypeTracker t) { t.start() and - result = API::moduleImport("foo.bar").getAUse() + result = API::moduleImport("foo.bar").getAnImmediateUse() or t.startInAttr("bar") and result = foo() @@ -145,7 +145,7 @@ DataFlow::Node foo_bar() { foo_bar(DataFlow::TypeTracker::end()).flowsTo(result) /** Gets a reference to `foo.bar.baz` (fictive attribute on `foo.bar` module). */ private DataFlow::TypeTrackingNode foo_bar_baz(DataFlow::TypeTracker t) { t.start() and - result = API::moduleImport("foo.bar.baz").getAUse() + result = API::moduleImport("foo.bar.baz").getAnImmediateUse() or t.startInAttr("baz") and result = foo_bar() From bdfd6bdc7907f915a08c61ee9ba298c5745aee1a Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 7 Apr 2022 12:40:14 +0200 Subject: [PATCH 3/4] fix a ql/field-only-used-in-charpred warning --- python/ql/lib/semmle/python/frameworks/Flask.qll | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/python/ql/lib/semmle/python/frameworks/Flask.qll b/python/ql/lib/semmle/python/frameworks/Flask.qll index a5357b497d4a..342c793f4ea7 100644 --- a/python/ql/lib/semmle/python/frameworks/Flask.qll +++ b/python/ql/lib/semmle/python/frameworks/Flask.qll @@ -403,11 +403,8 @@ module Flask { } private class RequestAttrMultiDict extends Werkzeug::MultiDict::InstanceSource { - string attr_name; - RequestAttrMultiDict() { - this = request().getMember(attr_name).getAnImmediateUse() and - attr_name in ["args", "values", "form", "files"] + this = request().getMember(["args", "values", "form", "files"]).getAnImmediateUse() } } From 7e4c76c63b7c30e515eca66c27d3a007bef4c609 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 7 Apr 2022 13:16:31 +0200 Subject: [PATCH 4/4] revert API-graph change in Flask.qll --- python/ql/lib/semmle/python/frameworks/Flask.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/lib/semmle/python/frameworks/Flask.qll b/python/ql/lib/semmle/python/frameworks/Flask.qll index 342c793f4ea7..8fe9e9bdd4b8 100644 --- a/python/ql/lib/semmle/python/frameworks/Flask.qll +++ b/python/ql/lib/semmle/python/frameworks/Flask.qll @@ -339,7 +339,7 @@ module Flask { */ private class FlaskRequestSource extends RemoteFlowSource::Range { FlaskRequestSource() { - this = request().getAnImmediateUse() and + this = request().getAUse() and not any(Import imp).contains(this.asExpr()) and not exists(ControlFlowNode def | this.asVar().getSourceVariable().hasDefiningNode(def) | any(Import imp).contains(def.getNode())