From 832d6fbca3584767cec914e550e19fd021d53df1 Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 9 Jan 2026 13:49:17 +0000 Subject: [PATCH 1/3] Prepare `LocalSourceNode` for locality Removes the dependence on the (global) `ModuleVariableNode.getARead()`, by adding a local version (that doesn't include `import *` reads) instead. --- .../python/dataflow/new/internal/DataFlowPublic.qll | 10 +++++++--- .../python/dataflow/new/internal/LocalSources.qll | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll index ffecbcba57ac..1597553c7563 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll @@ -440,13 +440,17 @@ class ModuleVariableNode extends Node, TModuleVariableNode { /** Gets a node that reads this variable. */ Node getARead() { - result.asCfgNode() = var.getALoad().getAFlowNode() and - // Ignore reads that happen when the module is imported. These are only executed once. - not result.getScope() = mod + result = this.getALocalRead() or this = import_star_read(result) } + /** Gets a node that reads this variable, excluding reads that happen through `from ... import *`. */ + Node getALocalRead() { + result.asCfgNode() = var.getALoad().getAFlowNode() and + not result.getScope() = mod + } + /** Gets an `EssaNode` that corresponds to an assignment of this global variable. */ Node getAWrite() { any(EssaNodeDefinition def).definedBy(var, result.asCfgNode().(DefinitionNode)) 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 c43a111c9c8b..7752846ae1ff 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/LocalSources.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/LocalSources.qll @@ -67,7 +67,7 @@ class LocalSourceNode extends Node { or // 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() + this = any(ModuleVariableNode v).getALocalRead() or // We include all scope entry definitions, as these act as the local source within the scope they // enter. @@ -248,7 +248,7 @@ private module Cached { pragma[nomagic] private predicate localSourceFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFrom, nodeTo, _) and - not nodeTo = any(ModuleVariableNode v).getARead() + not nodeTo = any(ModuleVariableNode v).getALocalRead() } /** From fc43d222a2cb0557c4006033e79a9eb735738c33 Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 9 Jan 2026 17:01:11 +0000 Subject: [PATCH 2/3] Remove global restriction on `ModuleVariableNode` This may result in more nodes, but it should still be bounded by the number of global variables in the source code. --- .../python/dataflow/new/internal/DataFlowPublic.qll | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll index 1597553c7563..76782e1f6340 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll @@ -76,15 +76,7 @@ newtype TNode = node.getNode() = any(Comp c).getIterable() } or /** A node representing a global (module-level) variable in a specific module. */ - TModuleVariableNode(Module m, GlobalVariable v) { - v.getScope() = m and - ( - v.escapes() - or - isAccessedThroughImportStar(m) and - ImportStar::globalNameDefinedInModule(v.getId(), m) - ) - } or + TModuleVariableNode(Module m, GlobalVariable v) { v.getScope() = m } or /** * A synthetic node representing that an iterable sequence flows to consumer. */ From cd842447e8aa649146006801ef9d5623ff710c41 Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 9 Jan 2026 17:12:39 +0000 Subject: [PATCH 3/3] Get rid of unused predicate --- .../lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll | 2 -- 1 file changed, 2 deletions(-) diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll index 76782e1f6340..b2832c8bdb44 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll @@ -462,8 +462,6 @@ class ModuleVariableNode extends Node, TModuleVariableNode { override Location getLocation() { result = mod.getLocation() } } -private predicate isAccessedThroughImportStar(Module m) { m = ImportStar::getStarImported(_) } - private ModuleVariableNode import_star_read(Node n) { resolved_import_star_module(result.getModule(), result.getVariable().getId(), n) }