How to write a cross-function isAdditionalFlowStep while preserving context sensitive dataflow.
- 主要語言
- CodeQL
- 星號
- 10.1k
- 分支
- 2.1k
- 平均合併
- 2 天 15 小時
- 30 天內合併 PR
- 141
描述
For example:
```python
import json
def my_dumps(obj):
return json.dumps(obj)
def my_loads(s):
return json.loads(s)
with open('test.json', 'r') as json_file:
json_obj = json.load(json_file)
json_obj = my_loads(my_dumps(json_obj))
json_obj2 = my_loads(my_dumps([1,2,3]))
with open('out.json', 'w') as out:
json.dump(json_obj, out)
with open('out2.json', 'w') as out:
json.dump(json_obj2, out)
```
```CodeQL
import python
import semmle.python.ApiGraphs
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.TaintTracking
module TConfig implements DataFlow::ConfigSig{
predicate isSource(DataFlow::Node source) {
source = API::builtin("open").getReturn().asSource()
}
predicate isSink(DataFlow::Node sink) {
sink = API::moduleImport("json").getMember("dump").getACall().getArg(0)
}
}
module TFlow = TaintTracking::Global;
import TFlow::PathGraph
from TFlow::PathNode source, TFlow::PathNode sink
where
TFlow::flowPath(source, sink)
select
source.getNode(), source, sink, "root"
```
This works as expected. `json_file` only have a flow to `json.dump(json_obj, out)`.
But when it comes to
```python
import json
def my_dumps(obj):
return json.dumps(obj)
def my_loads(s):
return json.loads(s)
def mock_rpc_call(func_name, arg):
return globals()[func_name](arg)
with open('test.json', 'r') as json_file:
json_obj = json.load(json_file)
json_obj = mock_rpc_call('my_loads', mock_rpc_call('my_dumps', json_obj))
json_obj2 = mock_rpc_call('my_loads', mock_rpc_call('my_dumps', [1,2,3]))
with open('out.json', 'w') as out:
json.dump(json_obj, out)
with open('out2.json', 'w') as out:
json.dump(json_obj2, out)
```
```CodeQL
import python
import semmle.python.ApiGraphs
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.TaintTracking
module TConfig implements DataFlow::ConfigSig{
predicate isSource(DataFlow::Node source) {
source = API::builtin("open").getReturn().asSource()
}
predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
nodeFrom.getLocation().getFile() = nodeTo.getLocation().getFile() and (
// Handle call
exists(DataFlow::CallCfgNode rpc_call, FunctionObject rpc_func, FunctionObject called_func|
rpc_func.getName() = "mock_rpc_call" and
rpc_call.getArg(1) = nodeFrom and
rpc_call.getFunction().asCfgNode() = rpc_func.theCallable().getAReference() and
called_func.getName() = rpc_call.getArg(0).asExpr().(StringLiteral).getS() and
called_func.getFunction().getArg(0) = nodeTo.asExpr()
) or
// Handle return
exists(Return ret , DataFlow::CallCfgNode rpc_call, FunctionObject rpc_func|
ret.getValue() = nodeFrom.asExpr() and
ret.getScope().(Function).getFunctionObject().getName() = rpc_call.getArg(0).asExpr().(StringLiteral).getS() and
rpc_call.getFunction().asCfgNode() = rpc_func.theCallable().getAReference() and
rpc_call = nodeTo
)
// How can I match the call and return to preserve context sensitive?
)
}
predicate isSink(DataFlow::Node sink) {
sink = API::moduleImport("json").getMember("dump").getACall().getArg(0)
}
}
module TFlow = TaintTracking::Global;
import TFlow::PathGraph
from TFlow::PathNode source, TFlow::PathNode sink
where
TFlow::flowPath(source, sink)
select
source.getNode(), source, sink, "root"
```
It will return 4 flow, and 3 of them is context insensitive. `json_file` will have flows to `json.dump(json_obj, out)` and `json.dump(json_obj2, out)`.
貢獻指南
研究方向
Start with the shown CodeQL query and its DataFlow::ConfigSig implementation, especially isAdditionalFlowStep and the CallCfgNode and Return relationships. Trace how the mock_rpc_call examples produce context-insensitive paths and compare those paths with the expected json_obj and json_obj2 flows. Done means the query preserves call and return context and reports only the intended flow to each json.dump call.
由索引模型根據 Issue 內容生成。
評估
- 技術堆疊
- python
- 領域
- security
- Issue 類型
- 缺陷
- 難度
- 5/5
- 預估耗時
- 一週以上
- 活躍度
- 停滯
- 描述清晰度
- 基本清楚
- 新手友好度
- 25/100