Merge branch 'main' into moresensitive2

This commit is contained in:
Geoffrey White
2025-07-14 11:58:08 +01:00
186 changed files with 6792 additions and 2004 deletions

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Type annotations such as `foo : Bar` are now treated by the call graph as an indication that `foo` may be an instance of `Bar`.

View File

@@ -762,6 +762,17 @@ class Annotation extends Expr {
or
this = any(FunctionExpr f).getReturns()
}
/** Gets the expression that this annotation annotates. */
Expr getAnnotatedExpression() {
result = any(AnnAssign a | a.getAnnotation() = this).getTarget()
or
result = any(Parameter p | p.getAnnotation() = this)
or
exists(FunctionExpr f, Return r |
this = f.getReturns() and r.getScope() = f.getInnerScope() and result = r.getValue()
)
}
}
/* Expression Contexts */

View File

@@ -580,6 +580,11 @@ private module TrackClassInstanceInput implements CallGraphConstruction::Simple:
class State = Class;
predicate start(Node start, Class cls) {
exists(Annotation ann |
ann = classTracker(cls).asExpr() and
start.asExpr() = ann.getAnnotatedExpression()
)
or
resolveClassCall(start.(CallCfgNode).asCfgNode(), cls)
or
// result of `super().__new__` as used in a `__new__` method implementation

View File

@@ -18,17 +18,7 @@ private module PolynomialReDoSConfig implements DataFlow::ConfigSig {
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
// Diff-informed incremental mode is currently disabled for this query due to
// API limitations. The query exposes sink.getABacktrackingTerm() as an alert
// location, but there is no way to express that information through
// getASelectedSinkLocation() because there is no @location in the CodeQL
// database that corresponds to a term inside a regular expression. As a
// result, this query could miss alerts in diff-informed incremental mode.
//
// To address this problem, we need to have a version of
// getASelectedSinkLocation() that uses hasLocationInfo() instead of
// returning Location objects.
predicate observeDiffInformedIncrementalMode() { none() }
predicate observeDiffInformedIncrementalMode() { any() }
Location getASelectedSinkLocation(DataFlow::Node sink) {
result = sink.(Sink).getHighlight().getLocation()

View File

@@ -0,0 +1,6 @@
testFailures
debug_callableNotUnique
pointsTo_found_typeTracker_notFound
typeTracker_found_pointsTo_notFound
| type_annotations.py:6:5:6:14 | ControlFlowNode for Attribute() | Foo.method |
| type_annotations.py:16:5:16:14 | ControlFlowNode for Attribute() | Foo.method |

View File

@@ -0,0 +1 @@
../CallGraph/InlineCallGraphTest.ql

View File

@@ -0,0 +1,33 @@
class Foo:
def method(self):
pass
def test_parameter_annotation(x: Foo):
x.method() #$ tt=Foo.method
def test_no_parameter_annotation(x):
x.method()
def function_with_return_annotation() -> Foo:
return eval("Foo()")
def test_return_annotation():
x = function_with_return_annotation() #$ pt,tt=function_with_return_annotation
x.method() #$ tt=Foo.method
def function_without_return_annotation():
return eval("Foo()")
def test_no_return_annotation():
x = function_without_return_annotation() #$ pt,tt=function_without_return_annotation
x.method()
def test_variable_annotation():
x = eval("Foo()")
x : Foo
# Currently fails because there is no flow from the class definition to the type annotation.
x.method() #$ MISSING: tt=Foo.method
def test_no_variable_annotation():
x = eval("Foo()")
x.method()