From 8d79248ea767a3e931d0e1695d48c8b4d0b719ae Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 20 Mar 2026 13:55:49 +0000 Subject: [PATCH 1/2] Python: Port ModificationOfLocals.ql --- python/ql/src/Statements/ModificationOfLocals.ql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/ql/src/Statements/ModificationOfLocals.ql b/python/ql/src/Statements/ModificationOfLocals.ql index e4791a410f7a..82529cbd6d0b 100644 --- a/python/ql/src/Statements/ModificationOfLocals.ql +++ b/python/ql/src/Statements/ModificationOfLocals.ql @@ -12,10 +12,10 @@ */ import python -private import LegacyPointsTo +private import semmle.python.ApiGraphs -predicate originIsLocals(ControlFlowNodeWithPointsTo n) { - n.pointsTo(_, _, Value::named("locals").getACall()) +predicate originIsLocals(ControlFlowNode n) { + API::builtin("locals").getReturn().getAValueReachableFromSource().asCfgNode() = n } predicate modification_of_locals(ControlFlowNode f) { @@ -37,5 +37,5 @@ where // in module level scope `locals() == globals()` // see https://docs.python.org/3/library/functions.html#locals // FP report in https://github.com/github/codeql/issues/6674 - not a.getScope() instanceof ModuleScope + not a.getScope() instanceof Module select a, "Modification of the locals() dictionary will have no effect on the local variables." From e3688444d74582c29c0356f96ac80c19201f3166 Mon Sep 17 00:00:00 2001 From: Taus Date: Tue, 7 Apr 2026 21:39:30 +0000 Subject: [PATCH 2/2] Python: Also exclude class scope Changing the `locals()` dictionary actually _does_ change the attributes of the class being defined, so we shouldn't alert in this case. --- python/ql/src/Statements/ModificationOfLocals.ql | 5 ++++- python/ql/test/query-tests/Statements/general/test.py | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/python/ql/src/Statements/ModificationOfLocals.ql b/python/ql/src/Statements/ModificationOfLocals.ql index 82529cbd6d0b..f32ddcf78849 100644 --- a/python/ql/src/Statements/ModificationOfLocals.ql +++ b/python/ql/src/Statements/ModificationOfLocals.ql @@ -37,5 +37,8 @@ where // in module level scope `locals() == globals()` // see https://docs.python.org/3/library/functions.html#locals // FP report in https://github.com/github/codeql/issues/6674 - not a.getScope() instanceof Module + not a.getScope() instanceof Module and + // in class level scope `locals()` reflects the class namespace, + // so modifications do take effect. + not a.getScope() instanceof Class select a, "Modification of the locals() dictionary will have no effect on the local variables." diff --git a/python/ql/test/query-tests/Statements/general/test.py b/python/ql/test/query-tests/Statements/general/test.py index eee63fa89e88..a5848f7c718d 100644 --- a/python/ql/test/query-tests/Statements/general/test.py +++ b/python/ql/test/query-tests/Statements/general/test.py @@ -174,3 +174,9 @@ def assert_ok(seq): # False positive. ODASA-8042. Fixed in PR #2401. class false_positive: e = (x for x in []) + +# In class-level scope `locals()` reflects the class namespace, +# so modifications do take effect. +class MyClass: + locals()['x'] = 43 # OK + y = x