|
1 | 1 | private import python
|
2 | 2 | private import semmle.python.Concepts
|
3 | 3 | private import semmle.python.ApiGraphs
|
4 |
| -private import semmle.python.dataflow.new.RemoteFlowSources |
| 4 | +private import semmle.python.frameworks.Flask |
| 5 | +private import semmle.python.frameworks.Django |
| 6 | +private import semmle.python.frameworks.Tornado |
5 | 7 |
|
6 | 8 | /**
|
7 | 9 | * A data flow source of the client ip obtained according to the remote endpoint identifier specified
|
8 | 10 | * (`X-Forwarded-For`, `X-Real-IP`, `Proxy-Client-IP`, etc.) in the header.
|
9 | 11 | *
|
10 | 12 | * For example: `request.headers.get("X-Forwarded-For")`.
|
| 13 | + * |
| 14 | + * A call to `request.headers.get` or `request.headers.get_all` or `request.headers.getlist`. |
11 | 15 | */
|
12 |
| -abstract class ClientSuppliedIpUsedInSecurityCheck extends DataFlow::CallCfgNode { } |
| 16 | +abstract class ClientSuppliedIpUsedInSecurityCheck extends DataFlow::Node { } |
13 | 17 |
|
14 |
| -private class FlaskClientSuppliedIpUsedInSecurityCheck extends ClientSuppliedIpUsedInSecurityCheck { |
| 18 | +private class FlaskClientSuppliedIpUsedInSecurityCheck extends ClientSuppliedIpUsedInSecurityCheck, |
| 19 | + DataFlow::MethodCallNode { |
15 | 20 | FlaskClientSuppliedIpUsedInSecurityCheck() {
|
16 |
| - exists(RemoteFlowSource rfs, DataFlow::AttrRead get | |
17 |
| - rfs.getSourceType() = "flask.request" and this.getFunction() = get |
18 |
| - | |
19 |
| - // `get` is a call to request.headers.get or request.headers.get_all or request.headers.getlist |
20 |
| - // request.headers |
21 |
| - get.getObject() |
22 |
| - .(DataFlow::AttrRead) |
23 |
| - // request |
24 |
| - .getObject() |
25 |
| - .getALocalSource() = rfs and |
26 |
| - get.getAttributeName() in ["get", "get_all", "getlist"] and |
27 |
| - get.getObject().(DataFlow::AttrRead).getAttributeName() = "headers" and |
28 |
| - this.getArg(0).asExpr().(StrConst).getText().toLowerCase() = clientIpParameterName() |
29 |
| - ) |
| 21 | + this = Flask::request().getMember("headers").getMember(["get", "get_all", "getlist"]).getACall() and |
| 22 | + this.getArg(0).asExpr().(StrConst).getText().toLowerCase() = clientIpParameterName() |
30 | 23 | }
|
31 | 24 | }
|
32 | 25 |
|
33 |
| -private class DjangoClientSuppliedIpUsedInSecurityCheck extends ClientSuppliedIpUsedInSecurityCheck { |
| 26 | +private class DjangoClientSuppliedIpUsedInSecurityCheck extends ClientSuppliedIpUsedInSecurityCheck, |
| 27 | + DataFlow::MethodCallNode { |
34 | 28 | DjangoClientSuppliedIpUsedInSecurityCheck() {
|
35 |
| - exists(RemoteFlowSource rfs, DataFlow::AttrRead get | |
36 |
| - rfs.getSourceType() = "django.http.request.HttpRequest" and this.getFunction() = get |
37 |
| - | |
38 |
| - // `get` is a call to request.headers.get or request.META.get |
39 |
| - // request.headers |
40 |
| - get.getObject() |
41 |
| - .(DataFlow::AttrRead) |
42 |
| - // request |
43 |
| - .getObject() |
44 |
| - .getALocalSource() = rfs and |
45 |
| - get.getAttributeName() = "get" and |
46 |
| - get.getObject().(DataFlow::AttrRead).getAttributeName() in ["headers", "META"] and |
47 |
| - this.getArg(0).asExpr().(StrConst).getText().toLowerCase() = clientIpParameterName() |
48 |
| - ) |
| 29 | + exists(DataFlow::Node req, DataFlow::AttrRead headers | |
| 30 | + // a call to request.headers.get or request.META.get |
| 31 | + req = PrivateDjango::DjangoImpl::DjangoHttp::Request::HttpRequest::instance() and |
| 32 | + headers.getObject().getALocalSource() = req and |
| 33 | + headers.getAttributeName() in ["headers", "META"] and |
| 34 | + this.calls(headers, "get") |
| 35 | + ) and |
| 36 | + this.getArg(0).asExpr().(StrConst).getText().toLowerCase() = clientIpParameterName() |
49 | 37 | }
|
50 | 38 | }
|
51 | 39 |
|
52 |
| -private class TornadoClientSuppliedIpUsedInSecurityCheck extends ClientSuppliedIpUsedInSecurityCheck { |
| 40 | +private class TornadoClientSuppliedIpUsedInSecurityCheck extends ClientSuppliedIpUsedInSecurityCheck, |
| 41 | + DataFlow::MethodCallNode { |
53 | 42 | TornadoClientSuppliedIpUsedInSecurityCheck() {
|
54 |
| - exists(RemoteFlowSource rfs, DataFlow::AttrRead get | |
55 |
| - rfs.getSourceType() = "tornado.web.RequestHandler" and this.getFunction() = get |
| 43 | + // a call to self.request.headers.get or self.request.headers.get_list inside a tornado requesthandler |
| 44 | + exists( |
| 45 | + Tornado::TornadoModule::Web::RequestHandler::SelfParam selfParam, DataFlow::AttrRead headers, |
| 46 | + DataFlow::AttrRead req |
56 | 47 | |
|
57 |
| - // `get` is a call to `rfs`.request.headers.get |
58 |
| - // `rfs`.request.headers |
59 |
| - get.getObject() |
60 |
| - .(DataFlow::AttrRead) |
61 |
| - // `rfs`.request |
62 |
| - .getObject() |
63 |
| - .(DataFlow::AttrRead) |
64 |
| - // `rfs` |
65 |
| - .getObject() |
66 |
| - .getALocalSource() = rfs and |
67 |
| - get.getAttributeName() in ["get", "get_list"] and |
68 |
| - get.getObject().(DataFlow::AttrRead).getAttributeName() = "headers" and |
69 |
| - this.getArg(0).asExpr().(StrConst).getText().toLowerCase() = clientIpParameterName() |
70 |
| - ) |
| 48 | + req.getObject().getALocalSource() = selfParam and |
| 49 | + req.getAttributeName() = "request" and |
| 50 | + headers.getObject().getALocalSource() = req and |
| 51 | + headers.getAttributeName() = "headers" and |
| 52 | + this.calls(headers, ["get", "get_list"]) |
| 53 | + ) and |
| 54 | + this.getArg(0).asExpr().(StrConst).getText().toLowerCase() = clientIpParameterName() |
71 | 55 | }
|
72 | 56 | }
|
73 | 57 |
|
|
0 commit comments