From 00c3fb41036a4029883181dfbe8615b8d6729d97 Mon Sep 17 00:00:00 2001 From: Shivam Nayak Date: Sat, 26 Sep 2026 01:06:18 +0530 Subject: [PATCH] Python: Fix isType false positive on modules named *typing (fixes #22621) --- python/ql/lib/semmle/python/objects/TObject.qll | 4 +++- .../regressions/typing-filename/Test.expected | 1 + .../PointsTo/regressions/typing-filename/Test.ql | 12 ++++++++++++ .../PointsTo/regressions/typing-filename/main.py | 10 ++++++++++ .../regressions/typing-filename/pkg/__init__.py | 15 +++++++++++++++ .../regressions/typing-filename/pkg/other.py | 15 +++++++++++++++ .../regressions/typing-filename/pkg/typing.py | 15 +++++++++++++++ 7 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 python/ql/test/library-tests/PointsTo/regressions/typing-filename/Test.expected create mode 100644 python/ql/test/library-tests/PointsTo/regressions/typing-filename/Test.ql create mode 100644 python/ql/test/library-tests/PointsTo/regressions/typing-filename/main.py create mode 100644 python/ql/test/library-tests/PointsTo/regressions/typing-filename/pkg/__init__.py create mode 100644 python/ql/test/library-tests/PointsTo/regressions/typing-filename/pkg/other.py create mode 100644 python/ql/test/library-tests/PointsTo/regressions/typing-filename/pkg/typing.py diff --git a/python/ql/lib/semmle/python/objects/TObject.qll b/python/ql/lib/semmle/python/objects/TObject.qll index dd191467c19f..a6214e1a7aa9 100644 --- a/python/ql/lib/semmle/python/objects/TObject.qll +++ b/python/ql/lib/semmle/python/objects/TObject.qll @@ -216,7 +216,9 @@ private predicate scope_loads_tuplenode(Scope s, TupleNode origin) { predicate isType(ObjectInternal t) { t.isClass() = true or - t.getOrigin().getEnclosingModule().getName().matches("%typing") + exists(string name | name = t.getOrigin().getEnclosingModule().getName() | + name = "typing" or name = "typing_extensions" or name.matches("typing.%") + ) } private predicate is_power_2(int n) { diff --git a/python/ql/test/library-tests/PointsTo/regressions/typing-filename/Test.expected b/python/ql/test/library-tests/PointsTo/regressions/typing-filename/Test.expected new file mode 100644 index 000000000000..277ed0f0add6 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/regressions/typing-filename/Test.expected @@ -0,0 +1 @@ +| main.py:6:1:6:13 | ControlFlowNode for InTyping() | resolved: InTyping() | diff --git a/python/ql/test/library-tests/PointsTo/regressions/typing-filename/Test.ql b/python/ql/test/library-tests/PointsTo/regressions/typing-filename/Test.ql new file mode 100644 index 000000000000..d3245a7d5180 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/regressions/typing-filename/Test.ql @@ -0,0 +1,12 @@ +import python +private import LegacyPointsTo + +from ControlFlowNodeWithPointsTo use, string outcome +where + exists(CallNode call | call.getFunction().(NameNode).getId() = "InTyping" and use = call) and + ( + if exists(use.pointsTo()) + then outcome = "resolved: " + use.pointsTo().toString() + else outcome = "no points-to info" + ) +select use, outcome \ No newline at end of file diff --git a/python/ql/test/library-tests/PointsTo/regressions/typing-filename/main.py b/python/ql/test/library-tests/PointsTo/regressions/typing-filename/main.py new file mode 100644 index 000000000000..9a750dcda609 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/regressions/typing-filename/main.py @@ -0,0 +1,10 @@ +from typing import Optional + +from pkg.other import InOther +from pkg.typing import InTyping + +InTyping("a") +Optional[InTyping] + +InOther("b") +Optional[InOther] \ No newline at end of file diff --git a/python/ql/test/library-tests/PointsTo/regressions/typing-filename/pkg/__init__.py b/python/ql/test/library-tests/PointsTo/regressions/typing-filename/pkg/__init__.py new file mode 100644 index 000000000000..a22bbb470645 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/regressions/typing-filename/pkg/__init__.py @@ -0,0 +1,15 @@ +registry = {} + + +def make_type(name): + if name in registry: + return registry[name] + + class Created(str): + pass + + registry[name] = Created + return Created + + +InTyping = make_type("InTyping") \ No newline at end of file diff --git a/python/ql/test/library-tests/PointsTo/regressions/typing-filename/pkg/other.py b/python/ql/test/library-tests/PointsTo/regressions/typing-filename/pkg/other.py new file mode 100644 index 000000000000..42cf36016a09 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/regressions/typing-filename/pkg/other.py @@ -0,0 +1,15 @@ +registry = {} + + +def make_type(name): + if name in registry: + return registry[name] + + class Created(str): + pass + + registry[name] = Created + return Created + + +InOther = make_type("InOther") \ No newline at end of file diff --git a/python/ql/test/library-tests/PointsTo/regressions/typing-filename/pkg/typing.py b/python/ql/test/library-tests/PointsTo/regressions/typing-filename/pkg/typing.py new file mode 100644 index 000000000000..a22bbb470645 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/regressions/typing-filename/pkg/typing.py @@ -0,0 +1,15 @@ +registry = {} + + +def make_type(name): + if name in registry: + return registry[name] + + class Created(str): + pass + + registry[name] = Created + return Created + + +InTyping = make_type("InTyping") \ No newline at end of file