Skip to content

Commit 64a7206

Browse files
committed
Python: Improve aiohttp FileResponse/StreamResponse modeling
However, notice that the concepts tests use the HttpResponse location for the `responseBody` tag, which seems a little odd in this situation, where they are actually separate. Will fix in next commit.
1 parent 15269c9 commit 64a7206

File tree

3 files changed

+27
-4
lines changed

3 files changed

+27
-4
lines changed

python/ql/lib/semmle/python/frameworks/Aiohttp.qll

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ module AiohttpWebModel {
518518
* - https://docs.aiohttp.org/en/stable/web_quickstart.html#aiohttp-web-exceptions
519519
*/
520520
class AiohttpWebResponseInstantiation extends Http::Server::HttpResponse::Range,
521-
Response::InstanceSource, DataFlow::CallCfgNode
521+
Response::InstanceSource, API::CallNode
522522
{
523523
API::Node apiNode;
524524

@@ -590,12 +590,32 @@ module AiohttpWebModel {
590590
/**
591591
* A call to the `aiohttp.web.FileResponse` constructor as a sink for Filesystem access.
592592
*/
593-
class FileResponseCall extends FileSystemAccess::Range, DataFlow::CallCfgNode {
593+
class FileResponseCall extends FileSystemAccess::Range, API::CallNode {
594594
FileResponseCall() {
595595
this = API::moduleImport("aiohttp").getMember("web").getMember("FileResponse").getACall()
596596
}
597597

598-
override DataFlow::Node getAPathArgument() { result = this.getArg(0) }
598+
override DataFlow::Node getAPathArgument() { result = this.getParameter(0, "path").asSink() }
599+
}
600+
601+
/**
602+
* An instantiation of `aiohttp.web.StreamResponse`.
603+
*
604+
* See https://docs.aiohttp.org/en/stable/web_reference.html#aiohttp.web.StreamResponse
605+
*/
606+
class StreamResponse extends AiohttpWebResponseInstantiation {
607+
StreamResponse() {
608+
this = API::moduleImport("aiohttp").getMember("web").getMember("StreamResponse").getACall()
609+
}
610+
611+
override DataFlow::Node getBody() {
612+
result =
613+
this.getReturn()
614+
.getMember(["write", "write_eof"])
615+
.getACall()
616+
.getParameter(0, "data")
617+
.asSink()
618+
}
599619
}
600620

601621
/** Gets an HTTP response instance. */
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
failures
22
testFailures
3+
| response_test.py:82:12:82:31 | ControlFlowNode for Attribute() | Unexpected result: responseBody=b"bar" |
4+
| response_test.py:82:12:82:31 | ControlFlowNode for Attribute() | Unexpected result: responseBody=b"baz" |
5+
| response_test.py:82:12:82:31 | ControlFlowNode for Attribute() | Unexpected result: responseBody=b"foo" |

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ async def redirect_302(request): # $ requestHandler
7373
async def file_response(request): # $ requestHandler
7474
filename = "foo.txt"
7575
resp = web.FileResponse(filename) # $ HttpResponse mimetype=application/octet-stream getAPathArgument=filename
76-
resp = web.FileResponse(path=filename) # $ HttpResponse mimetype=application/octet-stream MISSING: getAPathArgument=filename
76+
resp = web.FileResponse(path=filename) # $ HttpResponse mimetype=application/octet-stream getAPathArgument=filename
7777
return resp
7878

7979

0 commit comments

Comments
 (0)