mirror of
https://github.com/github/codeql.git
synced 2026-06-06 05:57:07 +02:00
Python: visit function parameter and return annotations in new CFG
The new (shared-CFG-based) Python control flow graph in `semmle.python.controlflow.internal.Cfg` previously did not emit CFG nodes for parameter type annotations (`def f(x: T): ...`) or for the return type annotation (`-> T`). The legacy CFG emitted both, and a small number of framework models rely on this: `LocalSources.qll`'s `annotatedInstance` walks the parameter annotation expression by way of its CFG node to track that a parameter receives an instance of the annotated class. After the dataflow flip to the new CFG/SSA this regression manifested as lost flows in any test exercising annotation-based parameter tracking: FastAPI `Depends()` receivers, Pydantic request bodies, Starlette `WebSocket`, the call-graph type-annotation test, and so on. Extend `FunctionDefExpr` to visit each annotation as a child of the function-def expression, in CPython evaluation order: positional parameter annotations, `*args` annotation, keyword-only parameter annotations, `**kwargs` annotation, then the return annotation. (Lambda expressions have no annotations in Python syntax, so `LambdaExpr` is unchanged.) PEP 695 type parameters remain out of scope; they belong to the inner annotation scope, not the enclosing CFG. Restored test results across `framework/aiohttp`, `framework/fastapi`, `framework/lxml`, the `CallGraph-type-annotations` test, and `CWE-022-PathInjection`. Two FastAPI list-comprehension MISSING markers become positive (`taint_test.py:41,55`). CPython CFG consistency remains clean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The new (shared-CFG-based) Python control flow graph now visits parameter and return type annotations as CFG nodes for function definitions, matching the legacy CFG. This restores annotation-based type tracking through framework models such as FastAPI's `Depends()`, Pydantic request models, Starlette `WebSocket` handlers, and any other models that flow a class reference through `Parameter.getAnnotation()` to identify instances of the annotated class.
|
||||
@@ -1446,10 +1446,19 @@ module Ast implements AstSig<Py::Location> {
|
||||
|
||||
/**
|
||||
* A function definition expression (visits positional and keyword
|
||||
* defaults, but NOT PEP 695 type parameters — those bind in an
|
||||
* annotation scope that nests the function body, so they belong to
|
||||
* the inner scope's CFG, not the enclosing scope's; the legacy CFG
|
||||
* also omitted them).
|
||||
* defaults followed by parameter and return type annotations, but NOT
|
||||
* PEP 695 type parameters — those bind in an annotation scope that
|
||||
* nests the function body, so they belong to the inner scope's CFG,
|
||||
* not the enclosing scope's; the legacy CFG also omitted them).
|
||||
*
|
||||
* Evaluation order follows CPython: defaults are pushed first, then
|
||||
* keyword-only defaults, then annotations (the `__annotations__` dict
|
||||
* is built last, before `MAKE_FUNCTION`). Annotations are emitted as
|
||||
* CFG nodes so that flows from a class reference into a parameter's
|
||||
* type annotation are visible to dataflow (e.g. so that framework
|
||||
* models like FastAPI's `Depends()` can use a parameter's type hint
|
||||
* to track that the parameter receives an instance of the annotated
|
||||
* class — see `LocalSources::annotatedInstance`).
|
||||
*/
|
||||
additional class FunctionDefExpr extends Expr {
|
||||
private Py::FunctionExpr funcExpr;
|
||||
@@ -1473,15 +1482,61 @@ module Ast implements AstSig<Py::Location> {
|
||||
rank[n + 1](Py::Expr d, int i | d = funcExpr.getArgs().getKwDefault(i) | d order by i)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `n`th annotation expression, in CPython evaluation
|
||||
* order: positional parameter annotations (by argument position),
|
||||
* `*args` annotation, keyword-only parameter annotations (by
|
||||
* argument position), `**kwargs` annotation, then the return
|
||||
* annotation. Each annotation appears at most once.
|
||||
*/
|
||||
Expr getAnnotation(int n) {
|
||||
result.asExpr() =
|
||||
rank[n + 1](Py::Expr a, int subOrder, int subIndex |
|
||||
functionAnnotation(funcExpr, a, subOrder, subIndex)
|
||||
|
|
||||
a order by subOrder, subIndex
|
||||
)
|
||||
}
|
||||
|
||||
int getNumberOfDefaults() { result = count(funcExpr.getArgs().getADefault()) }
|
||||
|
||||
int getNumberOfKwDefaults() { result = count(funcExpr.getArgs().getAKwDefault()) }
|
||||
|
||||
int getNumberOfAnnotations() {
|
||||
result = count(Py::Expr a | functionAnnotation(funcExpr, a, _, _))
|
||||
}
|
||||
|
||||
override AstNode getChild(int index) {
|
||||
result = this.getDefault(index)
|
||||
or
|
||||
result = this.getKwDefault(index - this.getNumberOfDefaults())
|
||||
or
|
||||
result = this.getAnnotation(index - this.getNumberOfDefaults() - this.getNumberOfKwDefaults())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `a` is an annotation of `funcExpr` in slot
|
||||
* `(subOrder, subIndex)`. Slots are CPython evaluation order:
|
||||
* positional param annotations (subOrder 0, subIndex = argument
|
||||
* position), `*args` annotation (1, 0), keyword-only annotations
|
||||
* (2, position), `**kwargs` annotation (3, 0), return annotation
|
||||
* (4, 0).
|
||||
*/
|
||||
private predicate functionAnnotation(
|
||||
Py::FunctionExpr funcExpr, Py::Expr a, int subOrder, int subIndex
|
||||
) {
|
||||
a = funcExpr.getArgs().getAnnotation(subIndex) and subOrder = 0
|
||||
or
|
||||
a = funcExpr.getArgs().getVarargannotation() and subOrder = 1 and subIndex = 0
|
||||
or
|
||||
a = funcExpr.getArgs().getKwAnnotation(subIndex) and subOrder = 2
|
||||
or
|
||||
a = funcExpr.getArgs().getKwargannotation() and subOrder = 3 and subIndex = 0
|
||||
or
|
||||
a = funcExpr.getReturns() and subOrder = 4 and subIndex = 0
|
||||
}
|
||||
|
||||
/** A lambda expression (has default args evaluated at definition time). */
|
||||
additional class LambdaExpr extends Expr {
|
||||
private Py::Lambda lambda;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
testFailures
|
||||
| type_annotations.py:6:16:6:32 | Comment # $ tt=Foo.method | Missing result: tt=Foo.method |
|
||||
| type_annotations.py:16:16:16:32 | Comment # $ tt=Foo.method | Missing result: tt=Foo.method |
|
||||
debug_callableNotUnique
|
||||
pointsTo_found_typeTracker_notFound
|
||||
typeTracker_found_pointsTo_notFound
|
||||
| type_annotations.py:6:5:6:14 | Attribute() | Foo.method |
|
||||
| type_annotations.py:16:5:16:14 | Attribute() | Foo.method |
|
||||
| type_annotations.py:29:5:29:14 | Attribute() | Foo.method |
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
argumentToEnsureNotTaintedNotMarkedAsSpurious
|
||||
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
|
||||
| taint_test.py:151:9:151:15 | taint_test.py:151 | ERROR, you should add `# $ MISSING: tainted` annotation | request |
|
||||
| taint_test.py:152:9:152:19 | taint_test.py:152 | ERROR, you should add `# $ MISSING: tainted` annotation | request.url |
|
||||
| taint_test.py:153:9:153:36 | taint_test.py:153 | ERROR, you should add `# $ MISSING: tainted` annotation | Await |
|
||||
testFailures
|
||||
| taint_test.py:151:18:151:28 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:152:22:152:32 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:153:39:153:49 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:168:76:168:96 | Comment # $ SPURIOUS: tainted | Fixed spurious result: tainted |
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
| response_test.py:11:30:11:37 | Parameter | Unexpected result: routedParameter=response |
|
||||
| response_test.py:12:41:12:151 | Comment # $ CookieWrite CookieName="key" CookieValue="value" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieHttpOnly=false |
|
||||
| response_test.py:12:41:12:151 | Comment # $ CookieWrite CookieName="key" CookieValue="value" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieName="key" |
|
||||
| response_test.py:12:41:12:151 | Comment # $ CookieWrite CookieName="key" CookieValue="value" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieSameSite=Lax |
|
||||
| response_test.py:12:41:12:151 | Comment # $ CookieWrite CookieName="key" CookieValue="value" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieSecure=false |
|
||||
| response_test.py:12:41:12:151 | Comment # $ CookieWrite CookieName="key" CookieValue="value" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieValue="value" |
|
||||
| response_test.py:12:41:12:151 | Comment # $ CookieWrite CookieName="key" CookieValue="value" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieWrite |
|
||||
| response_test.py:13:51:13:161 | Comment # $ CookieWrite CookieName="key" CookieValue="value" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieHttpOnly=false |
|
||||
| response_test.py:13:51:13:161 | Comment # $ CookieWrite CookieName="key" CookieValue="value" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieName="key" |
|
||||
| response_test.py:13:51:13:161 | Comment # $ CookieWrite CookieName="key" CookieValue="value" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieSameSite=Lax |
|
||||
| response_test.py:13:51:13:161 | Comment # $ CookieWrite CookieName="key" CookieValue="value" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieSecure=false |
|
||||
| response_test.py:13:51:13:161 | Comment # $ CookieWrite CookieName="key" CookieValue="value" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieValue="value" |
|
||||
| response_test.py:13:51:13:161 | Comment # $ CookieWrite CookieName="key" CookieValue="value" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieWrite |
|
||||
| response_test.py:14:96:14:205 | Comment # $ CookieWrite CookieName="key" CookieValue="value" CookieSecure=false CookieHttpOnly=true CookieSameSite=Lax | Missing result: CookieHttpOnly=true |
|
||||
| response_test.py:14:96:14:205 | Comment # $ CookieWrite CookieName="key" CookieValue="value" CookieSecure=false CookieHttpOnly=true CookieSameSite=Lax | Missing result: CookieName="key" |
|
||||
| response_test.py:14:96:14:205 | Comment # $ CookieWrite CookieName="key" CookieValue="value" CookieSecure=false CookieHttpOnly=true CookieSameSite=Lax | Missing result: CookieSameSite=Lax |
|
||||
| response_test.py:14:96:14:205 | Comment # $ CookieWrite CookieName="key" CookieValue="value" CookieSecure=false CookieHttpOnly=true CookieSameSite=Lax | Missing result: CookieSecure=false |
|
||||
| response_test.py:14:96:14:205 | Comment # $ CookieWrite CookieName="key" CookieValue="value" CookieSecure=false CookieHttpOnly=true CookieSameSite=Lax | Missing result: CookieValue="value" |
|
||||
| response_test.py:14:96:14:205 | Comment # $ CookieWrite CookieName="key" CookieValue="value" CookieSecure=false CookieHttpOnly=true CookieSameSite=Lax | Missing result: CookieWrite |
|
||||
| response_test.py:15:58:15:221 | Comment # $ headerWriteName="Set-Cookie" headerWriteValue="key2=value2" CookieWrite CookieRawHeader="key2=value2" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieHttpOnly=false |
|
||||
| response_test.py:15:58:15:221 | Comment # $ headerWriteName="Set-Cookie" headerWriteValue="key2=value2" CookieWrite CookieRawHeader="key2=value2" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieRawHeader="key2=value2" |
|
||||
| response_test.py:15:58:15:221 | Comment # $ headerWriteName="Set-Cookie" headerWriteValue="key2=value2" CookieWrite CookieRawHeader="key2=value2" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieSameSite=Lax |
|
||||
| response_test.py:15:58:15:221 | Comment # $ headerWriteName="Set-Cookie" headerWriteValue="key2=value2" CookieWrite CookieRawHeader="key2=value2" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieSecure=false |
|
||||
| response_test.py:15:58:15:221 | Comment # $ headerWriteName="Set-Cookie" headerWriteValue="key2=value2" CookieWrite CookieRawHeader="key2=value2" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieWrite |
|
||||
| response_test.py:15:58:15:221 | Comment # $ headerWriteName="Set-Cookie" headerWriteValue="key2=value2" CookieWrite CookieRawHeader="key2=value2" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: headerWriteName="Set-Cookie" |
|
||||
| response_test.py:15:58:15:221 | Comment # $ headerWriteName="Set-Cookie" headerWriteValue="key2=value2" CookieWrite CookieRawHeader="key2=value2" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: headerWriteValue="key2=value2" |
|
||||
| response_test.py:16:68:16:231 | Comment # $ headerWriteName="Set-Cookie" headerWriteValue="key2=value2" CookieWrite CookieRawHeader="key2=value2" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieHttpOnly=false |
|
||||
| response_test.py:16:68:16:231 | Comment # $ headerWriteName="Set-Cookie" headerWriteValue="key2=value2" CookieWrite CookieRawHeader="key2=value2" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieRawHeader="key2=value2" |
|
||||
| response_test.py:16:68:16:231 | Comment # $ headerWriteName="Set-Cookie" headerWriteValue="key2=value2" CookieWrite CookieRawHeader="key2=value2" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieSameSite=Lax |
|
||||
| response_test.py:16:68:16:231 | Comment # $ headerWriteName="Set-Cookie" headerWriteValue="key2=value2" CookieWrite CookieRawHeader="key2=value2" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieSecure=false |
|
||||
| response_test.py:16:68:16:231 | Comment # $ headerWriteName="Set-Cookie" headerWriteValue="key2=value2" CookieWrite CookieRawHeader="key2=value2" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieWrite |
|
||||
| response_test.py:16:68:16:231 | Comment # $ headerWriteName="Set-Cookie" headerWriteValue="key2=value2" CookieWrite CookieRawHeader="key2=value2" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: headerWriteName="Set-Cookie" |
|
||||
| response_test.py:16:68:16:231 | Comment # $ headerWriteName="Set-Cookie" headerWriteValue="key2=value2" CookieWrite CookieRawHeader="key2=value2" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: headerWriteValue="key2=value2" |
|
||||
| response_test.py:17:53:17:116 | Comment # $ headerWriteName="X-MyHeader" headerWriteValue="header-value" | Missing result: headerWriteName="X-MyHeader" |
|
||||
| response_test.py:17:53:17:116 | Comment # $ headerWriteName="X-MyHeader" headerWriteValue="header-value" | Missing result: headerWriteValue="header-value" |
|
||||
| response_test.py:23:26:23:29 | Parameter | Unexpected result: routedParameter=resp |
|
||||
| response_test.py:41:42:41:49 | Parameter | Unexpected result: routedParameter=response |
|
||||
| response_test.py:48:41:48:151 | Comment # $ CookieWrite CookieName="key" CookieValue="value" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieHttpOnly=false |
|
||||
| response_test.py:48:41:48:151 | Comment # $ CookieWrite CookieName="key" CookieValue="value" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieName="key" |
|
||||
| response_test.py:48:41:48:151 | Comment # $ CookieWrite CookieName="key" CookieValue="value" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieSameSite=Lax |
|
||||
| response_test.py:48:41:48:151 | Comment # $ CookieWrite CookieName="key" CookieValue="value" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieSecure=false |
|
||||
| response_test.py:48:41:48:151 | Comment # $ CookieWrite CookieName="key" CookieValue="value" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieValue="value" |
|
||||
| response_test.py:48:41:48:151 | Comment # $ CookieWrite CookieName="key" CookieValue="value" CookieSecure=false CookieHttpOnly=false CookieSameSite=Lax | Missing result: CookieWrite |
|
||||
| response_test.py:49:87:49:184 | Comment # $ headerWriteName="Custom-Response-Type" headerWriteValue="yes, but only after function has run" | Missing result: headerWriteName="Custom-Response-Type" |
|
||||
| response_test.py:49:87:49:184 | Comment # $ headerWriteName="Custom-Response-Type" headerWriteValue="yes, but only after function has run" | Missing result: headerWriteValue="yes, but only after function has run" |
|
||||
|
||||
@@ -1,107 +1,3 @@
|
||||
argumentToEnsureNotTaintedNotMarkedAsSpurious
|
||||
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
|
||||
| taint_test.py:33:9:33:24 | taint_test.py:33 | ERROR, you should add `# $ MISSING: tainted` annotation | also_input.field |
|
||||
| taint_test.py:35:9:35:27 | taint_test.py:35 | ERROR, you should add `# $ MISSING: tainted` annotation | also_input.main_foo |
|
||||
| taint_test.py:36:9:36:31 | taint_test.py:36 | ERROR, you should add `# $ MISSING: tainted` annotation | also_input.main_foo.foo |
|
||||
| taint_test.py:38:9:38:29 | taint_test.py:38 | ERROR, you should add `# $ MISSING: tainted` annotation | also_input.other_foos |
|
||||
| taint_test.py:39:9:39:32 | taint_test.py:39 | ERROR, you should add `# $ MISSING: tainted` annotation | also_input.other_foos[0] |
|
||||
| taint_test.py:40:9:40:36 | taint_test.py:40 | ERROR, you should add `# $ MISSING: tainted` annotation | also_input.other_foos[0].foo |
|
||||
| taint_test.py:43:9:43:30 | taint_test.py:43 | ERROR, you should add `# $ MISSING: tainted` annotation | also_input.nested_foos |
|
||||
| taint_test.py:44:9:44:33 | taint_test.py:44 | ERROR, you should add `# $ MISSING: tainted` annotation | also_input.nested_foos[0] |
|
||||
| taint_test.py:45:9:45:36 | taint_test.py:45 | ERROR, you should add `# $ MISSING: tainted` annotation | also_input.nested_foos[0][0] |
|
||||
| taint_test.py:46:9:46:40 | taint_test.py:46 | ERROR, you should add `# $ MISSING: tainted` annotation | also_input.nested_foos[0][0].foo |
|
||||
| taint_test.py:52:9:52:18 | taint_test.py:52 | ERROR, you should add `# $ MISSING: tainted` annotation | other_foos |
|
||||
| taint_test.py:53:9:53:21 | taint_test.py:53 | ERROR, you should add `# $ MISSING: tainted` annotation | other_foos[0] |
|
||||
| taint_test.py:54:9:54:25 | taint_test.py:54 | ERROR, you should add `# $ MISSING: tainted` annotation | other_foos[0].foo |
|
||||
| taint_test.py:140:9:140:21 | taint_test.py:140 | ERROR, you should add `# $ MISSING: tainted` annotation | websocket.url |
|
||||
| taint_test.py:142:9:142:28 | taint_test.py:142 | ERROR, you should add `# $ MISSING: tainted` annotation | websocket.url.netloc |
|
||||
| taint_test.py:143:9:143:26 | taint_test.py:143 | ERROR, you should add `# $ MISSING: tainted` annotation | websocket.url.path |
|
||||
| taint_test.py:144:9:144:27 | taint_test.py:144 | ERROR, you should add `# $ MISSING: tainted` annotation | websocket.url.query |
|
||||
| taint_test.py:145:9:145:30 | taint_test.py:145 | ERROR, you should add `# $ MISSING: tainted` annotation | websocket.url.fragment |
|
||||
| taint_test.py:146:9:146:30 | taint_test.py:146 | ERROR, you should add `# $ MISSING: tainted` annotation | websocket.url.username |
|
||||
| taint_test.py:147:9:147:30 | taint_test.py:147 | ERROR, you should add `# $ MISSING: tainted` annotation | websocket.url.password |
|
||||
| taint_test.py:148:9:148:30 | taint_test.py:148 | ERROR, you should add `# $ MISSING: tainted` annotation | websocket.url.hostname |
|
||||
| taint_test.py:149:9:149:26 | taint_test.py:149 | ERROR, you should add `# $ MISSING: tainted` annotation | websocket.url.port |
|
||||
| taint_test.py:151:9:151:32 | taint_test.py:151 | ERROR, you should add `# $ MISSING: tainted` annotation | websocket.url.components |
|
||||
| taint_test.py:152:9:152:39 | taint_test.py:152 | ERROR, you should add `# $ MISSING: tainted` annotation | websocket.url.components.netloc |
|
||||
| taint_test.py:153:9:153:37 | taint_test.py:153 | ERROR, you should add `# $ MISSING: tainted` annotation | websocket.url.components.path |
|
||||
| taint_test.py:154:9:154:38 | taint_test.py:154 | ERROR, you should add `# $ MISSING: tainted` annotation | websocket.url.components.query |
|
||||
| taint_test.py:155:9:155:41 | taint_test.py:155 | ERROR, you should add `# $ MISSING: tainted` annotation | websocket.url.components.fragment |
|
||||
| taint_test.py:156:9:156:41 | taint_test.py:156 | ERROR, you should add `# $ MISSING: tainted` annotation | websocket.url.components.username |
|
||||
| taint_test.py:157:9:157:41 | taint_test.py:157 | ERROR, you should add `# $ MISSING: tainted` annotation | websocket.url.components.password |
|
||||
| taint_test.py:158:9:158:41 | taint_test.py:158 | ERROR, you should add `# $ MISSING: tainted` annotation | websocket.url.components.hostname |
|
||||
| taint_test.py:159:9:159:37 | taint_test.py:159 | ERROR, you should add `# $ MISSING: tainted` annotation | websocket.url.components.port |
|
||||
| taint_test.py:161:9:161:25 | taint_test.py:161 | ERROR, you should add `# $ MISSING: tainted` annotation | websocket.headers |
|
||||
| taint_test.py:162:9:162:32 | taint_test.py:162 | ERROR, you should add `# $ MISSING: tainted` annotation | websocket.headers["key"] |
|
||||
| taint_test.py:164:9:164:30 | taint_test.py:164 | ERROR, you should add `# $ MISSING: tainted` annotation | websocket.query_params |
|
||||
| taint_test.py:165:9:165:37 | taint_test.py:165 | ERROR, you should add `# $ MISSING: tainted` annotation | websocket.query_params["key"] |
|
||||
| taint_test.py:167:9:167:25 | taint_test.py:167 | ERROR, you should add `# $ MISSING: tainted` annotation | websocket.cookies |
|
||||
| taint_test.py:168:9:168:32 | taint_test.py:168 | ERROR, you should add `# $ MISSING: tainted` annotation | websocket.cookies["key"] |
|
||||
| taint_test.py:170:9:170:33 | taint_test.py:170 | ERROR, you should add `# $ MISSING: tainted` annotation | Await |
|
||||
| taint_test.py:171:9:171:39 | taint_test.py:171 | ERROR, you should add `# $ MISSING: tainted` annotation | Await |
|
||||
| taint_test.py:172:9:172:38 | taint_test.py:172 | ERROR, you should add `# $ MISSING: tainted` annotation | Await |
|
||||
| taint_test.py:173:9:173:38 | taint_test.py:173 | ERROR, you should add `# $ MISSING: tainted` annotation | Await |
|
||||
| taint_test.py:183:24:183:27 | taint_test.py:183 | ERROR, you should add `# $ MISSING: tainted` annotation | data |
|
||||
| taint_test.py:186:24:186:27 | taint_test.py:186 | ERROR, you should add `# $ MISSING: tainted` annotation | data |
|
||||
| taint_test.py:189:24:189:27 | taint_test.py:189 | ERROR, you should add `# $ MISSING: tainted` annotation | data |
|
||||
| taint_test.py:205:9:205:28 | taint_test.py:205 | ERROR, you should add `# $ MISSING: tainted` annotation | Await |
|
||||
| taint_test.py:207:9:207:28 | taint_test.py:207 | ERROR, you should add `# $ MISSING: tainted` annotation | Await |
|
||||
| taint_test.py:208:9:208:35 | taint_test.py:208 | ERROR, you should add `# $ MISSING: tainted` annotation | Await |
|
||||
| taint_test.py:211:9:211:28 | taint_test.py:211 | ERROR, you should add `# $ MISSING: tainted` annotation | Await |
|
||||
| taint_test.py:212:9:212:35 | taint_test.py:212 | ERROR, you should add `# $ MISSING: tainted` annotation | Await |
|
||||
| taint_test.py:219:9:219:23 | taint_test.py:219 | ERROR, you should add `# $ MISSING: tainted` annotation | request.cookies |
|
||||
| taint_test.py:220:9:220:30 | taint_test.py:220 | ERROR, you should add `# $ MISSING: tainted` annotation | request.cookies["key"] |
|
||||
| taint_test.py:224:24:224:28 | taint_test.py:224 | ERROR, you should add `# $ MISSING: tainted` annotation | chunk |
|
||||
testFailures
|
||||
| taint_test.py:33:27:33:37 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:35:30:35:40 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:36:34:36:44 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:38:32:38:42 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:39:35:39:45 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:40:39:40:49 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:43:33:43:43 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:44:36:44:46 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:45:39:45:49 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:46:43:46:53 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:52:21:52:31 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:53:24:53:34 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:54:28:54:38 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:140:24:140:34 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:142:31:142:41 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:143:29:143:39 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:144:30:144:40 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:145:33:145:43 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:146:33:146:43 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:147:33:147:43 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:148:33:148:43 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:149:29:149:39 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:151:35:151:45 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:152:42:152:52 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:153:40:153:50 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:154:41:154:51 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:155:44:155:54 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:156:44:156:54 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:157:44:157:54 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:158:44:158:54 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:159:40:159:50 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:161:28:161:38 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:162:35:162:45 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:164:33:164:43 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:165:40:165:50 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:167:28:167:38 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:168:35:168:45 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:170:36:170:46 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:171:42:171:52 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:172:41:172:51 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:173:41:173:51 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:183:30:183:40 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:186:30:186:40 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:189:30:189:40 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:205:31:205:41 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:207:31:207:41 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:208:38:208:48 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:211:31:211:41 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:212:38:212:48 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:219:26:219:36 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:220:33:220:43 | Comment # $ tainted | Missing result: tainted |
|
||||
| taint_test.py:224:31:224:41 | Comment # $ tainted | Missing result: tainted |
|
||||
|
||||
@@ -38,7 +38,7 @@ async def test_taint(name : str, number : int, also_input: MyComplexModel): # $
|
||||
also_input.other_foos, # $ tainted
|
||||
also_input.other_foos[0], # $ tainted
|
||||
also_input.other_foos[0].foo, # $ tainted
|
||||
[f.foo for f in also_input.other_foos], # $ MISSING: tainted
|
||||
[f.foo for f in also_input.other_foos], # $ tainted
|
||||
|
||||
also_input.nested_foos, # $ tainted
|
||||
also_input.nested_foos[0], # $ tainted
|
||||
@@ -52,7 +52,7 @@ async def test_taint(name : str, number : int, also_input: MyComplexModel): # $
|
||||
other_foos, # $ tainted
|
||||
other_foos[0], # $ tainted
|
||||
other_foos[0].foo, # $ tainted
|
||||
[f.foo for f in other_foos], # $ MISSING: tainted
|
||||
[f.foo for f in other_foos], # $ tainted
|
||||
)
|
||||
|
||||
return "ok" # $ HttpResponse
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
argumentToEnsureNotTaintedNotMarkedAsSpurious
|
||||
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
|
||||
| taint_test.py:133:13:133:35 | taint_test.py:133 | ERROR, you should add `# $ MISSING: tainted` annotation | tree_arg.getroot().text |
|
||||
testFailures
|
||||
| taint_test.py:133:37:133:82 | Comment # $ tainted # Type tracking from the type hint | Missing result: tainted |
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#select
|
||||
| fastapi_path_injection.py:7:19:7:26 | filepath | fastapi_path_injection.py:17:21:17:24 | path | fastapi_path_injection.py:7:19:7:26 | filepath | This path depends on a $@. | fastapi_path_injection.py:17:21:17:24 | path | user-provided value |
|
||||
| fastapi_path_injection.py:7:19:7:26 | filepath | fastapi_path_injection.py:26:21:26:24 | path | fastapi_path_injection.py:7:19:7:26 | filepath | This path depends on a $@. | fastapi_path_injection.py:26:21:26:24 | path | user-provided value |
|
||||
| fastapi_path_injection.py:7:19:7:26 | filepath | fastapi_path_injection.py:31:21:31:24 | path | fastapi_path_injection.py:7:19:7:26 | filepath | This path depends on a $@. | fastapi_path_injection.py:31:21:31:24 | path | user-provided value |
|
||||
| fastapi_path_injection.py:7:19:7:26 | filepath | fastapi_path_injection.py:48:21:48:24 | path | fastapi_path_injection.py:7:19:7:26 | filepath | This path depends on a $@. | fastapi_path_injection.py:48:21:48:24 | path | user-provided value |
|
||||
| flask_path_injection.py:21:32:21:38 | dirname | flask_path_injection.py:1:26:1:32 | After ImportMember | flask_path_injection.py:21:32:21:38 | dirname | This path depends on a $@. | flask_path_injection.py:1:26:1:32 | After ImportMember | user-provided value |
|
||||
@@ -26,6 +27,8 @@ edges
|
||||
| fastapi_path_injection.py:6:24:6:31 | filepath | fastapi_path_injection.py:7:19:7:26 | filepath | provenance | |
|
||||
| fastapi_path_injection.py:17:21:17:24 | path | fastapi_path_injection.py:20:34:20:37 | path | provenance | |
|
||||
| fastapi_path_injection.py:20:34:20:37 | path | fastapi_path_injection.py:6:24:6:31 | filepath | provenance | |
|
||||
| fastapi_path_injection.py:26:21:26:24 | path | fastapi_path_injection.py:27:34:27:37 | path | provenance | |
|
||||
| fastapi_path_injection.py:27:34:27:37 | path | fastapi_path_injection.py:6:24:6:31 | filepath | provenance | |
|
||||
| fastapi_path_injection.py:31:21:31:24 | path | fastapi_path_injection.py:32:34:32:37 | path | provenance | |
|
||||
| fastapi_path_injection.py:32:34:32:37 | path | fastapi_path_injection.py:6:24:6:31 | filepath | provenance | |
|
||||
| fastapi_path_injection.py:48:21:48:24 | path | fastapi_path_injection.py:49:45:49:48 | path | provenance | |
|
||||
@@ -157,6 +160,8 @@ nodes
|
||||
| fastapi_path_injection.py:7:19:7:26 | filepath | semmle.label | filepath |
|
||||
| fastapi_path_injection.py:17:21:17:24 | path | semmle.label | path |
|
||||
| fastapi_path_injection.py:20:34:20:37 | path | semmle.label | path |
|
||||
| fastapi_path_injection.py:26:21:26:24 | path | semmle.label | path |
|
||||
| fastapi_path_injection.py:27:34:27:37 | path | semmle.label | path |
|
||||
| fastapi_path_injection.py:31:21:31:24 | path | semmle.label | path |
|
||||
| fastapi_path_injection.py:32:34:32:37 | path | semmle.label | path |
|
||||
| fastapi_path_injection.py:48:21:48:24 | path | semmle.label | path |
|
||||
@@ -291,5 +296,3 @@ nodes
|
||||
subpaths
|
||||
| test.py:25:19:25:19 | x | test.py:12:15:12:15 | x | test.py:13:12:13:30 | After Attribute() | test.py:25:9:25:20 | After normalize() |
|
||||
| test.py:48:23:48:23 | x | test.py:12:15:12:15 | x | test.py:13:12:13:30 | After Attribute() | test.py:48:13:48:24 | After normalize() |
|
||||
testFailures
|
||||
| fastapi_path_injection.py:26:72:26:81 | Comment # $ Source | Missing result: Source |
|
||||
|
||||
Reference in New Issue
Block a user