diff --git a/python/ql/lib/semmle/python/ApiGraphs.qll b/python/ql/lib/semmle/python/ApiGraphs.qll index c294e062f6d..7da45eb7fc2 100644 --- a/python/ql/lib/semmle/python/ApiGraphs.qll +++ b/python/ql/lib/semmle/python/ApiGraphs.qll @@ -987,7 +987,7 @@ module API { DataFlow::LocalSourceNode trackUseNode(DataFlow::LocalSourceNode src) { Stages::TypeTracking::ref() and result = trackUseNode(src, DataFlow::TypeTracker::end()) and - result instanceof DataFlow::ExprNode + result instanceof DataFlow::LocalSourceNodeNotModule } /** diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/LocalSources.qll b/python/ql/lib/semmle/python/dataflow/new/internal/LocalSources.qll index 72ea5d95310..585d5c39d6b 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/LocalSources.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/LocalSources.qll @@ -51,6 +51,10 @@ class LocalSourceNode extends Node { // We explicitly include any read of a global variable, as some of these may have local flow going // into them. this = any(ModuleVariableNode mvn).getARead() + or + // We include all scope entry definitions, as these act as the local source within the scope they + // enter. + this.asVar() instanceof ScopeEntryDefinition } /** Holds if this `LocalSourceNode` can flow to `nodeTo` in one or more local flow steps. */ @@ -133,6 +137,19 @@ class LocalSourceNode extends Node { LocalSourceNode backtrack(TypeBackTracker t2, TypeBackTracker t) { t2 = t.step(result, this) } } +/** + * A LocalSourceNode that is not a ModuleVariableNode + * This class provides a positive formulation of that in its charpred. + */ +class LocalSourceNodeNotModule extends LocalSourceNode { + cached + LocalSourceNodeNotModule() { + this instanceof ExprNode + or + this.asVar() instanceof ScopeEntryDefinition + } +} + /** * A node that can be used for type tracking or type back-tracking. * diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackerSpecific.qll b/python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackerSpecific.qll index 67e3db984e8..9e05b7869c5 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackerSpecific.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackerSpecific.qll @@ -43,7 +43,19 @@ predicate compatibleContents(TypeTrackerContent storeContent, TypeTrackerContent predicate simpleLocalFlowStep = DataFlowPrivate::simpleLocalFlowStepForTypetracking/2; -predicate jumpStep = DataFlowPrivate::jumpStepSharedWithTypeTracker/2; +predicate jumpStep(Node nodeFrom, Node nodeTo) { + DataFlowPrivate::jumpStepSharedWithTypeTracker(nodeFrom, nodeTo) + or + capturedJumpStep(nodeFrom, nodeTo) +} + +predicate capturedJumpStep(Node nodeFrom, Node nodeTo) { + exists(SsaSourceVariable var, DefinitionNode def | var.hasDefiningNode(def) | + nodeTo.asVar().(ScopeEntryDefinition).getSourceVariable() = var and + nodeFrom.asCfgNode() = def.getValue() and + var.getScope().getScope*() = nodeFrom.getScope() + ) +} /** Holds if there is a level step from `nodeFrom` to `nodeTo`, which may depend on the call graph. */ predicate levelStepCall(Node nodeFrom, Node nodeTo) { none() } diff --git a/python/ql/test/library-tests/ApiGraphs/py3/dataflow-consistency.expected b/python/ql/test/library-tests/ApiGraphs/py3/dataflow-consistency.expected index 54acd44d74f..8de0286359a 100644 --- a/python/ql/test/library-tests/ApiGraphs/py3/dataflow-consistency.expected +++ b/python/ql/test/library-tests/ApiGraphs/py3/dataflow-consistency.expected @@ -3,6 +3,7 @@ uniqueCallEnclosingCallable | test_captured.py:7:22:7:25 | p() | Call should have one enclosing callable but has 0. | | test_captured.py:7:22:7:25 | p() | Call should have one enclosing callable but has 0. | | test_captured.py:14:26:14:30 | pp() | Call should have one enclosing callable but has 0. | +| test_captured.py:14:26:14:30 | pp() | Call should have one enclosing callable but has 0. | uniqueType uniqueNodeLocation missingLocation diff --git a/python/ql/test/library-tests/ApiGraphs/py3/test.py b/python/ql/test/library-tests/ApiGraphs/py3/test.py index 29a28bc6252..424c6248907 100644 --- a/python/ql/test/library-tests/ApiGraphs/py3/test.py +++ b/python/ql/test/library-tests/ApiGraphs/py3/test.py @@ -89,7 +89,7 @@ def use_of_builtins(): def imported_builtins(): import builtins #$ use=moduleImport("builtins") def open(f): - return builtins.open(f) #$ MISSING: use=moduleImport("builtins").getMember("open").getReturn() + return builtins.open(f) #$ use=moduleImport("builtins").getMember("open").getReturn() def redefine_print(): def my_print(x): diff --git a/python/ql/test/library-tests/ApiGraphs/py3/test_captured.py b/python/ql/test/library-tests/ApiGraphs/py3/test_captured.py index 965132c3181..9057b1d1fd7 100644 --- a/python/ql/test/library-tests/ApiGraphs/py3/test_captured.py +++ b/python/ql/test/library-tests/ApiGraphs/py3/test_captured.py @@ -11,4 +11,4 @@ def pp_list(l): return escape(x) #$ use=moduleImport("html").getMember("escape").getReturn() def pp_list_inner(l): - return ", ".join(pp(x) for x in l) #$ MISSING: use=moduleImport("html").getMember("escape").getReturn() + return ", ".join(pp(x) for x in l) #$ use=moduleImport("html").getMember("escape").getReturn() diff --git a/python/ql/test/library-tests/ApiGraphs/py3/test_captured_flask.py b/python/ql/test/library-tests/ApiGraphs/py3/test_captured_flask.py index fbab3164eb5..48e0202e5de 100644 --- a/python/ql/test/library-tests/ApiGraphs/py3/test_captured_flask.py +++ b/python/ql/test/library-tests/ApiGraphs/py3/test_captured_flask.py @@ -21,6 +21,6 @@ def create_app(): if not sid: return make_response(jsonify({'Error':'Token check failed: {0}'.format(sid)})) try: - user = Users.query.filter_by(id=id).first() #$ MISSING: use=moduleImport("flask_sqlalchemy").getMember("SQLAlchemy").getReturn().getMember("Model").getASubclass().getMember("query").getMember("filter_by") + user = Users.query.filter_by(id=id).first() #$ use=moduleImport("flask_sqlalchemy").getMember("SQLAlchemy").getReturn().getMember("Model").getASubclass().getMember("query").getMember("filter_by") except Exception as e: return make_response(jsonify({'error':str(e)}),500) diff --git a/python/ql/test/library-tests/ApiGraphs/py3/test_import_star.py b/python/ql/test/library-tests/ApiGraphs/py3/test_import_star.py index 7b2934357be..ff4d58509ec 100644 --- a/python/ql/test/library-tests/ApiGraphs/py3/test_import_star.py +++ b/python/ql/test/library-tests/ApiGraphs/py3/test_import_star.py @@ -32,5 +32,5 @@ def func1(): def func3(): var2 = print #$ use=moduleImport("builtins").getMember("print") def func4(): - var2() #$ MISSING: use=moduleImport("builtins").getMember("print").getReturn() + var2() #$ use=moduleImport("builtins").getMember("print").getReturn() func4()