Skip to content
This repository was archived by the owner on Jan 5, 2023. It is now read-only.

Commit 397282f

Browse files
committed
Add models for the Echo framework
1 parent bdb3e54 commit 397282f

File tree

12 files changed

+865
-0
lines changed

12 files changed

+865
-0
lines changed

change-notes/2020-09-17-echo.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* Added support for the Echo web framework

ql/src/go.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import semmle.go.dataflow.GlobalValueNumbering
2727
import semmle.go.dataflow.SSA
2828
import semmle.go.dataflow.TaintTracking
2929
import semmle.go.frameworks.Chi
30+
import semmle.go.frameworks.Echo
3031
import semmle.go.frameworks.Email
3132
import semmle.go.frameworks.Encoding
3233
import semmle.go.frameworks.Gin

ql/src/semmle/go/frameworks/Echo.qll

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/**
2+
* Provides classes for working with untrusted flow sources, taint propagators, and HTTP sinks
3+
* from the `github.com/labstack/echo` package.
4+
*/
5+
6+
import go
7+
8+
private module Echo {
9+
/** Gets an Echo package name. */
10+
bindingset[result]
11+
private string packagePath() { result = package("github.com/labstack/echo", "") }
12+
13+
/**
14+
* Data from a `Context` interface method, considered as a source of untrusted flow.
15+
*/
16+
private class EchoContextSource extends UntrustedFlowSource::Range {
17+
EchoContextSource() {
18+
exists(DataFlow::MethodCallNode call, string methodName |
19+
methodName =
20+
["Param", "ParamValues", "QueryParam", "QueryParams", "QueryString", "FormValue",
21+
"FormParams", "FormFile", "MultipartForm", "Cookie", "Cookies"] and
22+
call.getTarget().hasQualifiedName(packagePath(), "Context", methodName) and
23+
this = call.getResult(0)
24+
)
25+
}
26+
}
27+
28+
/**
29+
* Data from a `Context` interface method that is not generally exploitable for open-redirect attacks.
30+
*/
31+
private class EchoContextRedirectUnexploitableSource extends HTTP::Redirect::UnexploitableSource {
32+
EchoContextRedirectUnexploitableSource() {
33+
exists(DataFlow::MethodCallNode call, string methodName |
34+
methodName = ["FormValue", "FormParams", "FormFile", "MultipartForm", "Cookie", "Cookies"] and
35+
call.getTarget().hasQualifiedName(packagePath(), "Context", methodName) and
36+
this = call.getResult(0)
37+
)
38+
}
39+
}
40+
41+
/**
42+
* Models of `Context.Get/Set`. `Context` behaves like a map, with corresponding taint propagation.
43+
*/
44+
private class ContextMapModels extends TaintTracking::FunctionModel, Method {
45+
string methodName;
46+
FunctionInput input;
47+
FunctionOutput output;
48+
49+
ContextMapModels() {
50+
(
51+
methodName = "Get" and input.isReceiver() and output.isResult()
52+
or
53+
methodName = "Set" and input.isParameter(1) and output.isReceiver()
54+
) and
55+
this.hasQualifiedName(packagePath(), "Context", methodName)
56+
}
57+
58+
override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) {
59+
inp = input and outp = output
60+
}
61+
}
62+
63+
/**
64+
* A call to a method on `Context` struct that unmarshals data into a target.
65+
*/
66+
private class EchoContextBinder extends UntrustedFlowSource::Range {
67+
EchoContextBinder() {
68+
exists(DataFlow::MethodCallNode call |
69+
call.getTarget().hasQualifiedName(packagePath(), "Context", "Bind")
70+
|
71+
this = any(FunctionOutput output | output.isParameter(0)).getExitNode(call)
72+
)
73+
}
74+
}
75+
76+
/**
77+
* `echo.Context` methods which set the content-type to `text/html` and write a result in one operation.
78+
*/
79+
private class EchoHtmlOutputs extends HTTP::ResponseBody::Range {
80+
EchoHtmlOutputs() {
81+
exists(Method m | m.hasQualifiedName(packagePath(), "Context", ["HTML", "HTMLBlob"]) |
82+
this = m.getACall().getArgument(1)
83+
)
84+
}
85+
86+
override HTTP::ResponseWriter getResponseWriter() { none() }
87+
88+
override string getAContentType() { result = "text/html" }
89+
}
90+
91+
/**
92+
* `echo.Context` methods which take a content-type as a parameter.
93+
*/
94+
private class EchoParameterizedOutputs extends HTTP::ResponseBody::Range {
95+
DataFlow::CallNode callNode;
96+
97+
EchoParameterizedOutputs() {
98+
exists(Method m | m.hasQualifiedName(packagePath(), "Context", ["Blob", "Stream"]) |
99+
callNode = m.getACall() and this = callNode.getArgument(2)
100+
)
101+
}
102+
103+
override HTTP::ResponseWriter getResponseWriter() { none() }
104+
105+
override DataFlow::Node getAContentTypeNode() { result = callNode.getArgument(1) }
106+
}
107+
108+
/**
109+
* The `echo.Context.Redirect` method.
110+
*/
111+
private class EchoRedirectMethod extends HTTP::Redirect::Range, DataFlow::CallNode {
112+
EchoRedirectMethod() {
113+
exists(Method m | m.hasQualifiedName(packagePath(), "Context", "Redirect") |
114+
this = m.getACall()
115+
)
116+
}
117+
118+
override DataFlow::Node getUrl() { result = this.getArgument(1) }
119+
120+
override HTTP::ResponseWriter getResponseWriter() { none() }
121+
}
122+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
edges
2+
| test.go:170:11:170:32 | call to Param : string | test.go:171:20:171:24 | param |
3+
| test.go:176:11:176:32 | call to Param : string | test.go:180:20:180:28 | ...+... |
4+
| test.go:188:10:188:26 | selection of URL : pointer type | test.go:188:10:188:26 | selection of URL : pointer type |
5+
| test.go:188:10:188:26 | selection of URL : pointer type | test.go:188:10:188:26 | selection of URL : pointer type |
6+
| test.go:188:10:188:26 | selection of URL : pointer type | test.go:191:21:191:32 | call to String |
7+
| test.go:188:10:188:26 | selection of URL : pointer type | test.go:191:21:191:32 | call to String |
8+
nodes
9+
| test.go:170:11:170:32 | call to Param : string | semmle.label | call to Param : string |
10+
| test.go:171:20:171:24 | param | semmle.label | param |
11+
| test.go:176:11:176:32 | call to Param : string | semmle.label | call to Param : string |
12+
| test.go:180:20:180:28 | ...+... | semmle.label | ...+... |
13+
| test.go:188:10:188:26 | selection of URL : pointer type | semmle.label | selection of URL : pointer type |
14+
| test.go:188:10:188:26 | selection of URL : pointer type | semmle.label | selection of URL : pointer type |
15+
| test.go:191:21:191:32 | call to String | semmle.label | call to String |
16+
| test.go:191:21:191:32 | call to String | semmle.label | call to String |
17+
#select
18+
| test.go:171:20:171:24 | param | test.go:170:11:170:32 | call to Param : string | test.go:171:20:171:24 | param | Untrusted URL redirection due to $@. | test.go:170:11:170:32 | call to Param | user-provided value |
19+
| test.go:180:20:180:28 | ...+... | test.go:176:11:176:32 | call to Param : string | test.go:180:20:180:28 | ...+... | Untrusted URL redirection due to $@. | test.go:176:11:176:32 | call to Param | user-provided value |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Security/CWE-601/OpenUrlRedirect.ql
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
edges
2+
| test.go:13:11:13:32 | call to Param : string | test.go:14:16:14:20 | param |
3+
| test.go:19:11:19:27 | call to ParamValues : slice type | test.go:20:16:20:20 | param |
4+
| test.go:25:11:25:37 | call to QueryParam : string | test.go:26:16:26:20 | param |
5+
| test.go:31:11:31:27 | call to QueryParams : Values | test.go:32:16:32:20 | param |
6+
| test.go:37:10:37:26 | call to QueryString : string | test.go:38:16:38:19 | qstr |
7+
| test.go:43:9:43:34 | call to FormValue : string | test.go:44:16:44:18 | val |
8+
| test.go:49:2:49:30 | ... := ...[0] : Values | test.go:50:16:50:37 | index expression |
9+
| test.go:55:2:55:46 | ... := ...[0] : pointer type | test.go:59:20:59:25 | buffer |
10+
| test.go:64:2:64:31 | ... := ...[0] : pointer type | test.go:65:16:65:19 | implicit dereference : Form |
11+
| test.go:64:2:64:31 | ... := ...[0] : pointer type | test.go:65:16:65:41 | index expression |
12+
| test.go:65:16:65:19 | implicit dereference : Form | test.go:65:16:65:19 | implicit dereference : Form |
13+
| test.go:65:16:65:19 | implicit dereference : Form | test.go:65:16:65:41 | index expression |
14+
| test.go:70:2:70:31 | ... := ...[0] : pointer type | test.go:71:16:71:19 | implicit dereference : Form |
15+
| test.go:70:2:70:31 | ... := ...[0] : pointer type | test.go:75:20:75:25 | buffer |
16+
| test.go:71:16:71:19 | implicit dereference : Form | test.go:71:16:71:19 | implicit dereference : Form |
17+
| test.go:71:16:71:19 | implicit dereference : Form | test.go:75:20:75:25 | buffer |
18+
| test.go:80:2:80:32 | ... := ...[0] : pointer type | test.go:81:16:81:18 | implicit dereference : Cookie |
19+
| test.go:80:2:80:32 | ... := ...[0] : pointer type | test.go:81:16:81:24 | selection of Value |
20+
| test.go:81:16:81:18 | implicit dereference : Cookie | test.go:81:16:81:18 | implicit dereference : Cookie |
21+
| test.go:81:16:81:18 | implicit dereference : Cookie | test.go:81:16:81:24 | selection of Value |
22+
| test.go:86:13:86:25 | call to Cookies : slice type | test.go:87:16:87:25 | implicit dereference : Cookie |
23+
| test.go:86:13:86:25 | call to Cookies : slice type | test.go:87:16:87:31 | selection of Value |
24+
| test.go:87:16:87:25 | implicit dereference : Cookie | test.go:87:16:87:25 | implicit dereference : Cookie |
25+
| test.go:87:16:87:25 | implicit dereference : Cookie | test.go:87:16:87:31 | selection of Value |
26+
| test.go:97:11:97:15 | &... : pointer type | test.go:98:16:98:21 | selection of s |
27+
| test.go:111:21:111:42 | call to Param : string | test.go:112:16:112:42 | type assertion |
28+
| test.go:122:11:122:32 | call to Param : string | test.go:123:16:123:20 | param |
29+
| test.go:128:11:128:32 | call to Param : string | test.go:129:20:129:32 | type conversion |
30+
| test.go:134:11:134:32 | call to Param : string | test.go:135:29:135:41 | type conversion |
31+
| test.go:146:11:146:32 | call to Param : string | test.go:148:31:148:36 | reader |
32+
| test.go:162:11:162:32 | call to Param : string | test.go:163:23:163:35 | type conversion |
33+
nodes
34+
| test.go:13:11:13:32 | call to Param : string | semmle.label | call to Param : string |
35+
| test.go:14:16:14:20 | param | semmle.label | param |
36+
| test.go:19:11:19:27 | call to ParamValues : slice type | semmle.label | call to ParamValues : slice type |
37+
| test.go:20:16:20:20 | param | semmle.label | param |
38+
| test.go:25:11:25:37 | call to QueryParam : string | semmle.label | call to QueryParam : string |
39+
| test.go:26:16:26:20 | param | semmle.label | param |
40+
| test.go:31:11:31:27 | call to QueryParams : Values | semmle.label | call to QueryParams : Values |
41+
| test.go:32:16:32:20 | param | semmle.label | param |
42+
| test.go:37:10:37:26 | call to QueryString : string | semmle.label | call to QueryString : string |
43+
| test.go:38:16:38:19 | qstr | semmle.label | qstr |
44+
| test.go:43:9:43:34 | call to FormValue : string | semmle.label | call to FormValue : string |
45+
| test.go:44:16:44:18 | val | semmle.label | val |
46+
| test.go:49:2:49:30 | ... := ...[0] : Values | semmle.label | ... := ...[0] : Values |
47+
| test.go:50:16:50:37 | index expression | semmle.label | index expression |
48+
| test.go:55:2:55:46 | ... := ...[0] : pointer type | semmle.label | ... := ...[0] : pointer type |
49+
| test.go:59:20:59:25 | buffer | semmle.label | buffer |
50+
| test.go:64:2:64:31 | ... := ...[0] : pointer type | semmle.label | ... := ...[0] : pointer type |
51+
| test.go:65:16:65:19 | implicit dereference : Form | semmle.label | implicit dereference : Form |
52+
| test.go:65:16:65:41 | index expression | semmle.label | index expression |
53+
| test.go:70:2:70:31 | ... := ...[0] : pointer type | semmle.label | ... := ...[0] : pointer type |
54+
| test.go:71:16:71:19 | implicit dereference : Form | semmle.label | implicit dereference : Form |
55+
| test.go:75:20:75:25 | buffer | semmle.label | buffer |
56+
| test.go:80:2:80:32 | ... := ...[0] : pointer type | semmle.label | ... := ...[0] : pointer type |
57+
| test.go:81:16:81:18 | implicit dereference : Cookie | semmle.label | implicit dereference : Cookie |
58+
| test.go:81:16:81:24 | selection of Value | semmle.label | selection of Value |
59+
| test.go:86:13:86:25 | call to Cookies : slice type | semmle.label | call to Cookies : slice type |
60+
| test.go:87:16:87:25 | implicit dereference : Cookie | semmle.label | implicit dereference : Cookie |
61+
| test.go:87:16:87:31 | selection of Value | semmle.label | selection of Value |
62+
| test.go:97:11:97:15 | &... : pointer type | semmle.label | &... : pointer type |
63+
| test.go:98:16:98:21 | selection of s | semmle.label | selection of s |
64+
| test.go:111:21:111:42 | call to Param : string | semmle.label | call to Param : string |
65+
| test.go:112:16:112:42 | type assertion | semmle.label | type assertion |
66+
| test.go:122:11:122:32 | call to Param : string | semmle.label | call to Param : string |
67+
| test.go:123:16:123:20 | param | semmle.label | param |
68+
| test.go:128:11:128:32 | call to Param : string | semmle.label | call to Param : string |
69+
| test.go:129:20:129:32 | type conversion | semmle.label | type conversion |
70+
| test.go:134:11:134:32 | call to Param : string | semmle.label | call to Param : string |
71+
| test.go:135:29:135:41 | type conversion | semmle.label | type conversion |
72+
| test.go:146:11:146:32 | call to Param : string | semmle.label | call to Param : string |
73+
| test.go:148:31:148:36 | reader | semmle.label | reader |
74+
| test.go:162:11:162:32 | call to Param : string | semmle.label | call to Param : string |
75+
| test.go:163:23:163:35 | type conversion | semmle.label | type conversion |
76+
#select
77+
| test.go:14:16:14:20 | param | test.go:13:11:13:32 | call to Param : string | test.go:14:16:14:20 | param | Cross-site scripting vulnerability due to $@. | test.go:13:11:13:32 | call to Param | user-provided value |
78+
| test.go:20:16:20:20 | param | test.go:19:11:19:27 | call to ParamValues : slice type | test.go:20:16:20:20 | param | Cross-site scripting vulnerability due to $@. | test.go:19:11:19:27 | call to ParamValues | user-provided value |
79+
| test.go:26:16:26:20 | param | test.go:25:11:25:37 | call to QueryParam : string | test.go:26:16:26:20 | param | Cross-site scripting vulnerability due to $@. | test.go:25:11:25:37 | call to QueryParam | user-provided value |
80+
| test.go:32:16:32:20 | param | test.go:31:11:31:27 | call to QueryParams : Values | test.go:32:16:32:20 | param | Cross-site scripting vulnerability due to $@. | test.go:31:11:31:27 | call to QueryParams | user-provided value |
81+
| test.go:38:16:38:19 | qstr | test.go:37:10:37:26 | call to QueryString : string | test.go:38:16:38:19 | qstr | Cross-site scripting vulnerability due to $@. | test.go:37:10:37:26 | call to QueryString | user-provided value |
82+
| test.go:44:16:44:18 | val | test.go:43:9:43:34 | call to FormValue : string | test.go:44:16:44:18 | val | Cross-site scripting vulnerability due to $@. | test.go:43:9:43:34 | call to FormValue | user-provided value |
83+
| test.go:50:16:50:37 | index expression | test.go:49:2:49:30 | ... := ...[0] : Values | test.go:50:16:50:37 | index expression | Cross-site scripting vulnerability due to $@. | test.go:49:2:49:30 | ... := ...[0] | user-provided value |
84+
| test.go:59:20:59:25 | buffer | test.go:55:2:55:46 | ... := ...[0] : pointer type | test.go:59:20:59:25 | buffer | Cross-site scripting vulnerability due to $@. | test.go:55:2:55:46 | ... := ...[0] | user-provided value |
85+
| test.go:65:16:65:41 | index expression | test.go:64:2:64:31 | ... := ...[0] : pointer type | test.go:65:16:65:41 | index expression | Cross-site scripting vulnerability due to $@. | test.go:64:2:64:31 | ... := ...[0] | user-provided value |
86+
| test.go:75:20:75:25 | buffer | test.go:70:2:70:31 | ... := ...[0] : pointer type | test.go:75:20:75:25 | buffer | Cross-site scripting vulnerability due to $@. | test.go:70:2:70:31 | ... := ...[0] | user-provided value |
87+
| test.go:81:16:81:24 | selection of Value | test.go:80:2:80:32 | ... := ...[0] : pointer type | test.go:81:16:81:24 | selection of Value | Cross-site scripting vulnerability due to $@. | test.go:80:2:80:32 | ... := ...[0] | user-provided value |
88+
| test.go:87:16:87:31 | selection of Value | test.go:86:13:86:25 | call to Cookies : slice type | test.go:87:16:87:31 | selection of Value | Cross-site scripting vulnerability due to $@. | test.go:86:13:86:25 | call to Cookies | user-provided value |
89+
| test.go:98:16:98:21 | selection of s | test.go:97:11:97:15 | &... : pointer type | test.go:98:16:98:21 | selection of s | Cross-site scripting vulnerability due to $@. | test.go:97:11:97:15 | &... | user-provided value |
90+
| test.go:112:16:112:42 | type assertion | test.go:111:21:111:42 | call to Param : string | test.go:112:16:112:42 | type assertion | Cross-site scripting vulnerability due to $@. | test.go:111:21:111:42 | call to Param | user-provided value |
91+
| test.go:123:16:123:20 | param | test.go:122:11:122:32 | call to Param : string | test.go:123:16:123:20 | param | Cross-site scripting vulnerability due to $@. | test.go:122:11:122:32 | call to Param | user-provided value |
92+
| test.go:129:20:129:32 | type conversion | test.go:128:11:128:32 | call to Param : string | test.go:129:20:129:32 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:128:11:128:32 | call to Param | user-provided value |
93+
| test.go:135:29:135:41 | type conversion | test.go:134:11:134:32 | call to Param : string | test.go:135:29:135:41 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:134:11:134:32 | call to Param | user-provided value |
94+
| test.go:148:31:148:36 | reader | test.go:146:11:146:32 | call to Param : string | test.go:148:31:148:36 | reader | Cross-site scripting vulnerability due to $@. | test.go:146:11:146:32 | call to Param | user-provided value |
95+
| test.go:163:23:163:35 | type conversion | test.go:162:11:162:32 | call to Param : string | test.go:163:23:163:35 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:162:11:162:32 | call to Param | user-provided value |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Security/CWE-079/ReflectedXss.ql
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
go 1.14
2+
3+
module test
4+
5+
require (
6+
github.com/labstack/echo/v4 v4.1.17
7+
)

0 commit comments

Comments
 (0)