Skip to content

Commit 38875ca

Browse files
committed
Python: Improve handling of async methods
1 parent c3f942f commit 38875ca

File tree

3 files changed

+21
-11
lines changed

3 files changed

+21
-11
lines changed

python/ql/src/semmle/python/dataflow/new/internal/TaintTrackingPrivate.qll

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ private module Cached {
5151
DataFlowPrivate::iterableUnpackingReadStep(nodeFrom, _, nodeTo)
5252
or
5353
DataFlowPrivate::iterableUnpackingStoreStep(nodeFrom, _, nodeTo)
54+
or
55+
awaitStep(nodeFrom, nodeTo)
5456
}
5557
}
5658

@@ -201,3 +203,10 @@ predicate copyStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
201203
call.getArg(0) = nodeFrom
202204
)
203205
}
206+
207+
/**
208+
* Holds if taint can flow from `nodeFrom` to `nodeTo` with a step related `await`.
209+
*/
210+
predicate awaitStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
211+
nodeTo.asExpr().(Await).getValue() = nodeFrom.asExpr()
212+
}

python/ql/src/semmle/python/frameworks/internal/InstanceTaintStepsHelper.qll

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,15 @@ private class InstanceAdditionalTaintStep extends TaintTracking::AdditionalTaint
3333
nodeFrom = helper.getInstance() and
3434
nodeTo.(DataFlow::MethodCallNode).calls(nodeFrom, helper.getMethodName())
3535
or
36-
// async methods
37-
exists(DataFlow::MethodCallNode call, Await await |
38-
nodeTo.asExpr() = await and
39-
nodeFrom = helper.getInstance()
40-
|
41-
await.getValue() = any(DataFlow::Node awaitable | call.flowsTo(awaitable)).asExpr() and
42-
call.calls(nodeFrom, helper.getAsyncMethodName())
43-
)
36+
// async methods.
37+
//
38+
// since we have general taint-step from `foo` in `await foo` to the whole
39+
// expression, we simply taint the awaitable that is the result of "calling" the
40+
// async method. That also allows such an awaitable to be placed in a list (for
41+
// use with `asyncio.gather` for example), and thereby propagate taint to the
42+
// list.
43+
nodeFrom = helper.getInstance() and
44+
nodeTo.(DataFlow::MethodCallNode).calls(nodeFrom, helper.getAsyncMethodName())
4445
or
4546
// Attributes
4647
nodeFrom = helper.getInstance() and

python/ql/test/library-tests/frameworks/aiohttp/taint_test.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ async def test_taint(request: web.Request): # $ requestHandler
5656
await request.content.readchunk(), # $ tainted
5757
(await request.content.readchunk())[0], # $ tainted
5858
[line async for line in request.content], # $ tainted
59-
[data async for data in request.content.iter_chunked(1024)], # $ MISSING: tainted
60-
[data async for data in request.content.iter_any()], # $ MISSING: tainted
61-
[data async for data, _ in request.content.iter_chunks()], # $ MISSING: tainted
59+
[data async for data in request.content.iter_chunked(1024)], # $ tainted
60+
[data async for data in request.content.iter_any()], # $ tainted
61+
[data async for data, _ in request.content.iter_chunks()], # $ tainted
6262
request.content.read_nowait(), # $ tainted
6363

6464
# aiohttp.StreamReader

0 commit comments

Comments
 (0)