mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Python: Add global variable nested field jump steps
This commit is contained in:
@@ -556,6 +556,61 @@ predicate runtimeJumpStep(Node nodeFrom, Node nodeTo) {
|
||||
nodeFrom.asCfgNode() = param.getDefault() and
|
||||
nodeTo.asCfgNode() = param.getDefiningNode()
|
||||
)
|
||||
or
|
||||
// Enhanced global variable field access tracking
|
||||
globalVariableNestedFieldJumpStep(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a jump step from `nodeFrom` to `nodeTo` through global variable field access.
|
||||
* This supports tracking nested object field access through global variables like `app.obj.foo`.
|
||||
*/
|
||||
predicate globalVariableNestedFieldJumpStep(Node nodeFrom, Node nodeTo) {
|
||||
exists(GlobalVariable globalVar, AttrWrite write, AttrRead read |
|
||||
// Match writes and reads on the same global variable attribute path
|
||||
globalVariableAttrPath(globalVar, write.getObject()) and
|
||||
globalVariableAttrPath(globalVar, read.getObject()) and
|
||||
write.getAttributeName() = read.getAttributeName() and
|
||||
nodeFrom = write.getValue() and
|
||||
nodeTo = read and
|
||||
write.getEnclosingCallable() != read.getEnclosingCallable()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Maximum depth for global variable nested attribute access.
|
||||
* Depth 0 = globalVar.foo, depth 1 = globalVar.foo.bar, depth 2 = globalVar.foo.bar.baz, etc.
|
||||
*/
|
||||
private int getMaxGlobalVariableDepth() { result = 1 }
|
||||
|
||||
/**
|
||||
* Holds if `node` is an attribute access path starting from global variable `globalVar`.
|
||||
* Supports configurable nesting depth via getMaxGlobalVariableDepth().
|
||||
*/
|
||||
predicate globalVariableAttrPath(GlobalVariable globalVar, Node node) {
|
||||
globalVariableAttrPathAtDepth(globalVar, node, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is an attribute access path starting from global variable `globalVar` at specific `depth`.
|
||||
*/
|
||||
predicate globalVariableAttrPathAtDepth(GlobalVariable globalVar, Node node, int depth) {
|
||||
// Base case: Direct global variable access (depth 0)
|
||||
depth = 0 and
|
||||
exists(NameNode name |
|
||||
name.getId() = globalVar.getId() and
|
||||
node.asCfgNode() = name and
|
||||
name.getNode().(Name).getVariable() instanceof GlobalVariable and
|
||||
not exists(ClassExpr cls | cls.getName() = globalVar.getId())
|
||||
)
|
||||
or
|
||||
// Recursive case: Nested attribute access (depth > 0)
|
||||
exists(AttrRead attr, int parentDepth |
|
||||
globalVariableAttrPathAtDepth(globalVar, attr.getObject(), parentDepth) and
|
||||
node = attr and
|
||||
depth = parentDepth + 1 and
|
||||
depth <= getMaxGlobalVariableDepth()
|
||||
)
|
||||
}
|
||||
|
||||
//--------
|
||||
|
||||
@@ -177,18 +177,18 @@ def read_global():
|
||||
def test_global_nested_attributes():
|
||||
init_global()
|
||||
result = read_global()
|
||||
SINK(result) # $ MISSING: flow="SOURCE, l:-8 -> result"
|
||||
SINK(result) # $ flow="SOURCE, l:-8 -> result"
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Global scope interaction
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
def func_defined_before():
|
||||
SINK(global_obj.foo) # $ MISSING: flow="SOURCE, l:+3 -> global_obj.foo"
|
||||
SINK(global_obj.foo) # $ flow="SOURCE, l:+3 -> global_obj.foo"
|
||||
|
||||
global_obj = MyObj(NONSOURCE)
|
||||
global_obj.foo = SOURCE
|
||||
SINK(global_obj.foo) # $ flow="SOURCE, l:-1 -> global_obj.foo"
|
||||
|
||||
def func_defined_after():
|
||||
SINK(global_obj.foo) # $ MISSING: flow="SOURCE, l:-4 -> global_obj.foo"
|
||||
SINK(global_obj.foo) # $ flow="SOURCE, l:-4 -> global_obj.foo"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
| fastapi_path_injection.py:7:19:7:26 | ControlFlowNode for filepath | fastapi_path_injection.py:17:21:17:24 | ControlFlowNode for path | fastapi_path_injection.py:7:19:7:26 | ControlFlowNode for filepath | This path depends on a $@. | fastapi_path_injection.py:17:21:17:24 | ControlFlowNode for path | user-provided value |
|
||||
| fastapi_path_injection.py:7:19:7:26 | ControlFlowNode for filepath | fastapi_path_injection.py:26:21:26:24 | ControlFlowNode for path | fastapi_path_injection.py:7:19:7:26 | ControlFlowNode for filepath | This path depends on a $@. | fastapi_path_injection.py:26:21:26:24 | ControlFlowNode for path | user-provided value |
|
||||
| fastapi_path_injection.py:7:19:7:26 | ControlFlowNode for filepath | fastapi_path_injection.py:31:21:31:24 | ControlFlowNode for path | fastapi_path_injection.py:7:19:7:26 | ControlFlowNode for filepath | This path depends on a $@. | fastapi_path_injection.py:31:21:31:24 | ControlFlowNode for path | user-provided value |
|
||||
| fastapi_path_injection.py:7:19:7:26 | ControlFlowNode for filepath | fastapi_path_injection.py:48:21:48:24 | ControlFlowNode for path | fastapi_path_injection.py:7:19:7:26 | ControlFlowNode for filepath | This path depends on a $@. | fastapi_path_injection.py:48:21:48:24 | ControlFlowNode for path | user-provided value |
|
||||
| flask_path_injection.py:21:32:21:38 | ControlFlowNode for dirname | flask_path_injection.py:1:26:1:32 | ControlFlowNode for ImportMember | flask_path_injection.py:21:32:21:38 | ControlFlowNode for dirname | This path depends on a $@. | flask_path_injection.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
|
||||
| path_injection.py:13:14:13:47 | ControlFlowNode for Attribute() | path_injection.py:3:26:3:32 | ControlFlowNode for ImportMember | path_injection.py:13:14:13:47 | ControlFlowNode for Attribute() | This path depends on a $@. | path_injection.py:3:26:3:32 | ControlFlowNode for ImportMember | user-provided value |
|
||||
| path_injection.py:21:14:21:18 | ControlFlowNode for npath | path_injection.py:3:26:3:32 | ControlFlowNode for ImportMember | path_injection.py:21:14:21:18 | ControlFlowNode for npath | This path depends on a $@. | path_injection.py:3:26:3:32 | ControlFlowNode for ImportMember | user-provided value |
|
||||
@@ -30,6 +31,8 @@ edges
|
||||
| fastapi_path_injection.py:27:34:27:37 | ControlFlowNode for path | fastapi_path_injection.py:6:24:6:31 | ControlFlowNode for filepath | provenance | |
|
||||
| fastapi_path_injection.py:31:21:31:24 | ControlFlowNode for path | fastapi_path_injection.py:32:34:32:37 | ControlFlowNode for path | provenance | |
|
||||
| fastapi_path_injection.py:32:34:32:37 | ControlFlowNode for path | fastapi_path_injection.py:6:24:6:31 | ControlFlowNode for filepath | provenance | |
|
||||
| fastapi_path_injection.py:48:21:48:24 | ControlFlowNode for path | fastapi_path_injection.py:49:45:49:48 | ControlFlowNode for path | provenance | |
|
||||
| fastapi_path_injection.py:49:45:49:48 | ControlFlowNode for path | fastapi_path_injection.py:6:24:6:31 | ControlFlowNode for filepath | provenance | |
|
||||
| flask_path_injection.py:1:26:1:32 | ControlFlowNode for ImportMember | flask_path_injection.py:1:26:1:32 | ControlFlowNode for request | provenance | |
|
||||
| flask_path_injection.py:1:26:1:32 | ControlFlowNode for request | flask_path_injection.py:19:15:19:21 | ControlFlowNode for request | provenance | |
|
||||
| flask_path_injection.py:19:5:19:11 | ControlFlowNode for dirname | flask_path_injection.py:21:32:21:38 | ControlFlowNode for dirname | provenance | |
|
||||
@@ -161,6 +164,8 @@ nodes
|
||||
| fastapi_path_injection.py:27:34:27:37 | ControlFlowNode for path | semmle.label | ControlFlowNode for path |
|
||||
| fastapi_path_injection.py:31:21:31:24 | ControlFlowNode for path | semmle.label | ControlFlowNode for path |
|
||||
| fastapi_path_injection.py:32:34:32:37 | ControlFlowNode for path | semmle.label | ControlFlowNode for path |
|
||||
| fastapi_path_injection.py:48:21:48:24 | ControlFlowNode for path | semmle.label | ControlFlowNode for path |
|
||||
| fastapi_path_injection.py:49:45:49:48 | ControlFlowNode for path | semmle.label | ControlFlowNode for path |
|
||||
| flask_path_injection.py:1:26:1:32 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember |
|
||||
| flask_path_injection.py:1:26:1:32 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| flask_path_injection.py:19:5:19:11 | ControlFlowNode for dirname | semmle.label | ControlFlowNode for dirname |
|
||||
|
||||
@@ -45,5 +45,5 @@ async def read_item(path: str, data_source=Depends(get_data_source)): # $ MISSIN
|
||||
return data_source.get_data(path)
|
||||
|
||||
@app.get("/file5/", dependencies=[Depends(init_file_handler)])
|
||||
async def read_item(path: str): # $ MISSING: Source
|
||||
async def read_item(path: str): # $ Source
|
||||
return app.state.file_handler2.get_data(path)
|
||||
|
||||
Reference in New Issue
Block a user