Skip to content

Commit f2cc2af

Browse files
author
Alvaro Muñoz
committed
aiohttp improvements
1 parent fe24cc1 commit f2cc2af

File tree

2 files changed

+50
-2
lines changed

2 files changed

+50
-2
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* Improvements of the `aiohttp` models including heuristic sources and new path manipulation and SSRF sinks.

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

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,30 @@ module AiohttpWebModel {
468468
override string getSourceType() { result = "aiohttp.web.Request" }
469469
}
470470

471+
/**
472+
* Heuristic source that considers a method parameter with a type hint of `aiohttp.web.Request`
473+
* as a parameter that will receive an `aiohttp.web.Request` instance when a request
474+
* handler is invoked.
475+
*/
476+
class AiohttpHeuristicRequestHandlerRequestParam extends Request::InstanceSource,
477+
DataFlow::ParameterNode, RemoteFlowSource::Range
478+
{
479+
AiohttpHeuristicRequestHandlerRequestParam() {
480+
exists(FunctionExpr fe, int i |
481+
// the API::Node is the annotation (type hint), we need to get the annotated parameter
482+
fe.getArgs().getAnnotation(i) =
483+
API::moduleImport("aiohttp")
484+
.getMember("web")
485+
.getMember("Request")
486+
.getAValueReachableFromSource()
487+
.asExpr() and
488+
fe.getInnerScope().getArg(i) = this.getParameter()
489+
)
490+
}
491+
492+
override string getSourceType() { result = "aiohttp web request parameter" }
493+
}
494+
471495
/**
472496
* A read of the `request` attribute on an instance of an aiohttp.web View class,
473497
* which is the request being processed currently.
@@ -505,7 +529,10 @@ module AiohttpWebModel {
505529
AiohttpWebResponseInstantiation() {
506530
this = apiNode.getACall() and
507531
(
508-
apiNode = API::moduleImport("aiohttp").getMember("web").getMember("Response")
532+
apiNode =
533+
API::moduleImport("aiohttp")
534+
.getMember("web")
535+
.getMember(["FileResponse", "Response", "StreamResponse"])
509536
or
510537
exists(string httpExceptionClassName |
511538
httpExceptionClassName in [
@@ -545,6 +572,14 @@ module AiohttpWebModel {
545572

546573
override DataFlow::Node getMimetypeOrContentTypeArg() {
547574
result = this.getArgByName("content_type")
575+
or
576+
exists(DataFlow::Node headers, Dict d |
577+
headers = this.getArgByName("headers").getALocalSource()
578+
|
579+
headers.asExpr() = d and
580+
d.getAKey().(StrConst).getText().toLowerCase() = "content-type" and
581+
d.getAValue() = result.asExpr()
582+
)
548583
}
549584

550585
override string getMimetypeDefault() {
@@ -556,6 +591,17 @@ module AiohttpWebModel {
556591
}
557592
}
558593

594+
/**
595+
* A call to the `aiohttp.web.FileResponse` constructor as a sink for Filesystem access.
596+
*/
597+
class FileResponseCall extends FileSystemAccess::Range, DataFlow::CallCfgNode {
598+
FileResponseCall() {
599+
this = API::moduleImport("aiohttp").getMember("web").getMember("FileResponse").getACall()
600+
}
601+
602+
override DataFlow::Node getAPathArgument() { result = this.getArg(0) }
603+
}
604+
559605
/** Gets an HTTP response instance. */
560606
private API::Node aiohttpResponseInstance() {
561607
result = any(AiohttpWebResponseInstantiation call).getApiNode().getReturn()
@@ -670,7 +716,7 @@ private module AiohttpClientModel {
670716
string methodName;
671717

672718
OutgoingRequestCall() {
673-
methodName in [Http::httpVerbLower(), "request"] and
719+
methodName in [Http::httpVerbLower(), "request", "ws_connect"] and
674720
this = instance().getMember(methodName).getACall()
675721
}
676722

0 commit comments

Comments
 (0)