mirror of
https://github.com/github/codeql.git
synced 2026-01-30 06:42:57 +01:00
Add models for the Echo framework
This commit is contained in:
2
change-notes/2020-09-17-echo.md
Normal file
2
change-notes/2020-09-17-echo.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* Added support for the Echo web framework
|
||||
@@ -27,6 +27,7 @@ import semmle.go.dataflow.GlobalValueNumbering
|
||||
import semmle.go.dataflow.SSA
|
||||
import semmle.go.dataflow.TaintTracking
|
||||
import semmle.go.frameworks.Chi
|
||||
import semmle.go.frameworks.Echo
|
||||
import semmle.go.frameworks.Email
|
||||
import semmle.go.frameworks.Encoding
|
||||
import semmle.go.frameworks.Gin
|
||||
|
||||
122
ql/src/semmle/go/frameworks/Echo.qll
Normal file
122
ql/src/semmle/go/frameworks/Echo.qll
Normal file
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* Provides classes for working with untrusted flow sources, taint propagators, and HTTP sinks
|
||||
* from the `github.com/labstack/echo` package.
|
||||
*/
|
||||
|
||||
import go
|
||||
|
||||
private module Echo {
|
||||
/** Gets an Echo package name. */
|
||||
bindingset[result]
|
||||
private string packagePath() { result = package("github.com/labstack/echo", "") }
|
||||
|
||||
/**
|
||||
* Data from a `Context` interface method, considered as a source of untrusted flow.
|
||||
*/
|
||||
private class EchoContextSource extends UntrustedFlowSource::Range {
|
||||
EchoContextSource() {
|
||||
exists(DataFlow::MethodCallNode call, string methodName |
|
||||
methodName =
|
||||
["Param", "ParamValues", "QueryParam", "QueryParams", "QueryString", "FormValue",
|
||||
"FormParams", "FormFile", "MultipartForm", "Cookie", "Cookies"] and
|
||||
call.getTarget().hasQualifiedName(packagePath(), "Context", methodName) and
|
||||
this = call.getResult(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data from a `Context` interface method that is not generally exploitable for open-redirect attacks.
|
||||
*/
|
||||
private class EchoContextRedirectUnexploitableSource extends HTTP::Redirect::UnexploitableSource {
|
||||
EchoContextRedirectUnexploitableSource() {
|
||||
exists(DataFlow::MethodCallNode call, string methodName |
|
||||
methodName = ["FormValue", "FormParams", "FormFile", "MultipartForm", "Cookie", "Cookies"] and
|
||||
call.getTarget().hasQualifiedName(packagePath(), "Context", methodName) and
|
||||
this = call.getResult(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Models of `Context.Get/Set`. `Context` behaves like a map, with corresponding taint propagation.
|
||||
*/
|
||||
private class ContextMapModels extends TaintTracking::FunctionModel, Method {
|
||||
string methodName;
|
||||
FunctionInput input;
|
||||
FunctionOutput output;
|
||||
|
||||
ContextMapModels() {
|
||||
(
|
||||
methodName = "Get" and input.isReceiver() and output.isResult()
|
||||
or
|
||||
methodName = "Set" and input.isParameter(1) and output.isReceiver()
|
||||
) and
|
||||
this.hasQualifiedName(packagePath(), "Context", methodName)
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) {
|
||||
inp = input and outp = output
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a method on `Context` struct that unmarshals data into a target.
|
||||
*/
|
||||
private class EchoContextBinder extends UntrustedFlowSource::Range {
|
||||
EchoContextBinder() {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call.getTarget().hasQualifiedName(packagePath(), "Context", "Bind")
|
||||
|
|
||||
this = any(FunctionOutput output | output.isParameter(0)).getExitNode(call)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `echo.Context` methods which set the content-type to `text/html` and write a result in one operation.
|
||||
*/
|
||||
private class EchoHtmlOutputs extends HTTP::ResponseBody::Range {
|
||||
EchoHtmlOutputs() {
|
||||
exists(Method m | m.hasQualifiedName(packagePath(), "Context", ["HTML", "HTMLBlob"]) |
|
||||
this = m.getACall().getArgument(1)
|
||||
)
|
||||
}
|
||||
|
||||
override HTTP::ResponseWriter getResponseWriter() { none() }
|
||||
|
||||
override string getAContentType() { result = "text/html" }
|
||||
}
|
||||
|
||||
/**
|
||||
* `echo.Context` methods which take a content-type as a parameter.
|
||||
*/
|
||||
private class EchoParameterizedOutputs extends HTTP::ResponseBody::Range {
|
||||
DataFlow::CallNode callNode;
|
||||
|
||||
EchoParameterizedOutputs() {
|
||||
exists(Method m | m.hasQualifiedName(packagePath(), "Context", ["Blob", "Stream"]) |
|
||||
callNode = m.getACall() and this = callNode.getArgument(2)
|
||||
)
|
||||
}
|
||||
|
||||
override HTTP::ResponseWriter getResponseWriter() { none() }
|
||||
|
||||
override DataFlow::Node getAContentTypeNode() { result = callNode.getArgument(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `echo.Context.Redirect` method.
|
||||
*/
|
||||
private class EchoRedirectMethod extends HTTP::Redirect::Range, DataFlow::CallNode {
|
||||
EchoRedirectMethod() {
|
||||
exists(Method m | m.hasQualifiedName(packagePath(), "Context", "Redirect") |
|
||||
this = m.getACall()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getUrl() { result = this.getArgument(1) }
|
||||
|
||||
override HTTP::ResponseWriter getResponseWriter() { none() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
edges
|
||||
| test.go:170:11:170:32 | call to Param : string | test.go:171:20:171:24 | param |
|
||||
| test.go:176:11:176:32 | call to Param : string | test.go:180:20:180:28 | ...+... |
|
||||
| test.go:188:10:188:26 | selection of URL : pointer type | test.go:188:10:188:26 | selection of URL : pointer type |
|
||||
| test.go:188:10:188:26 | selection of URL : pointer type | test.go:188:10:188:26 | selection of URL : pointer type |
|
||||
| test.go:188:10:188:26 | selection of URL : pointer type | test.go:191:21:191:32 | call to String |
|
||||
| test.go:188:10:188:26 | selection of URL : pointer type | test.go:191:21:191:32 | call to String |
|
||||
nodes
|
||||
| test.go:170:11:170:32 | call to Param : string | semmle.label | call to Param : string |
|
||||
| test.go:171:20:171:24 | param | semmle.label | param |
|
||||
| test.go:176:11:176:32 | call to Param : string | semmle.label | call to Param : string |
|
||||
| test.go:180:20:180:28 | ...+... | semmle.label | ...+... |
|
||||
| test.go:188:10:188:26 | selection of URL : pointer type | semmle.label | selection of URL : pointer type |
|
||||
| test.go:188:10:188:26 | selection of URL : pointer type | semmle.label | selection of URL : pointer type |
|
||||
| test.go:191:21:191:32 | call to String | semmle.label | call to String |
|
||||
| test.go:191:21:191:32 | call to String | semmle.label | call to String |
|
||||
#select
|
||||
| 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 |
|
||||
| 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 |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE-601/OpenUrlRedirect.ql
|
||||
@@ -0,0 +1,95 @@
|
||||
edges
|
||||
| test.go:13:11:13:32 | call to Param : string | test.go:14:16:14:20 | param |
|
||||
| test.go:19:11:19:27 | call to ParamValues : slice type | test.go:20:16:20:20 | param |
|
||||
| test.go:25:11:25:37 | call to QueryParam : string | test.go:26:16:26:20 | param |
|
||||
| test.go:31:11:31:27 | call to QueryParams : Values | test.go:32:16:32:20 | param |
|
||||
| test.go:37:10:37:26 | call to QueryString : string | test.go:38:16:38:19 | qstr |
|
||||
| test.go:43:9:43:34 | call to FormValue : string | test.go:44:16:44:18 | val |
|
||||
| test.go:49:2:49:30 | ... := ...[0] : Values | test.go:50:16:50:37 | index expression |
|
||||
| test.go:55:2:55:46 | ... := ...[0] : pointer type | test.go:59:20:59:25 | buffer |
|
||||
| test.go:64:2:64:31 | ... := ...[0] : pointer type | test.go:65:16:65:19 | implicit dereference : Form |
|
||||
| test.go:64:2:64:31 | ... := ...[0] : pointer type | test.go:65:16:65:41 | index expression |
|
||||
| test.go:65:16:65:19 | implicit dereference : Form | test.go:65:16:65:19 | implicit dereference : Form |
|
||||
| test.go:65:16:65:19 | implicit dereference : Form | test.go:65:16:65:41 | index expression |
|
||||
| test.go:70:2:70:31 | ... := ...[0] : pointer type | test.go:71:16:71:19 | implicit dereference : Form |
|
||||
| test.go:70:2:70:31 | ... := ...[0] : pointer type | test.go:75:20:75:25 | buffer |
|
||||
| test.go:71:16:71:19 | implicit dereference : Form | test.go:71:16:71:19 | implicit dereference : Form |
|
||||
| test.go:71:16:71:19 | implicit dereference : Form | test.go:75:20:75:25 | buffer |
|
||||
| test.go:80:2:80:32 | ... := ...[0] : pointer type | test.go:81:16:81:18 | implicit dereference : Cookie |
|
||||
| test.go:80:2:80:32 | ... := ...[0] : pointer type | test.go:81:16:81:24 | selection of Value |
|
||||
| test.go:81:16:81:18 | implicit dereference : Cookie | test.go:81:16:81:18 | implicit dereference : Cookie |
|
||||
| test.go:81:16:81:18 | implicit dereference : Cookie | test.go:81:16:81:24 | selection of Value |
|
||||
| test.go:86:13:86:25 | call to Cookies : slice type | test.go:87:16:87:25 | implicit dereference : Cookie |
|
||||
| test.go:86:13:86:25 | call to Cookies : slice type | test.go:87:16:87:31 | selection of Value |
|
||||
| test.go:87:16:87:25 | implicit dereference : Cookie | test.go:87:16:87:25 | implicit dereference : Cookie |
|
||||
| test.go:87:16:87:25 | implicit dereference : Cookie | test.go:87:16:87:31 | selection of Value |
|
||||
| test.go:97:11:97:15 | &... : pointer type | test.go:98:16:98:21 | selection of s |
|
||||
| test.go:111:21:111:42 | call to Param : string | test.go:112:16:112:42 | type assertion |
|
||||
| test.go:122:11:122:32 | call to Param : string | test.go:123:16:123:20 | param |
|
||||
| test.go:128:11:128:32 | call to Param : string | test.go:129:20:129:32 | type conversion |
|
||||
| test.go:134:11:134:32 | call to Param : string | test.go:135:29:135:41 | type conversion |
|
||||
| test.go:146:11:146:32 | call to Param : string | test.go:148:31:148:36 | reader |
|
||||
| test.go:162:11:162:32 | call to Param : string | test.go:163:23:163:35 | type conversion |
|
||||
nodes
|
||||
| test.go:13:11:13:32 | call to Param : string | semmle.label | call to Param : string |
|
||||
| test.go:14:16:14:20 | param | semmle.label | param |
|
||||
| test.go:19:11:19:27 | call to ParamValues : slice type | semmle.label | call to ParamValues : slice type |
|
||||
| test.go:20:16:20:20 | param | semmle.label | param |
|
||||
| test.go:25:11:25:37 | call to QueryParam : string | semmle.label | call to QueryParam : string |
|
||||
| test.go:26:16:26:20 | param | semmle.label | param |
|
||||
| test.go:31:11:31:27 | call to QueryParams : Values | semmle.label | call to QueryParams : Values |
|
||||
| test.go:32:16:32:20 | param | semmle.label | param |
|
||||
| test.go:37:10:37:26 | call to QueryString : string | semmle.label | call to QueryString : string |
|
||||
| test.go:38:16:38:19 | qstr | semmle.label | qstr |
|
||||
| test.go:43:9:43:34 | call to FormValue : string | semmle.label | call to FormValue : string |
|
||||
| test.go:44:16:44:18 | val | semmle.label | val |
|
||||
| test.go:49:2:49:30 | ... := ...[0] : Values | semmle.label | ... := ...[0] : Values |
|
||||
| test.go:50:16:50:37 | index expression | semmle.label | index expression |
|
||||
| test.go:55:2:55:46 | ... := ...[0] : pointer type | semmle.label | ... := ...[0] : pointer type |
|
||||
| test.go:59:20:59:25 | buffer | semmle.label | buffer |
|
||||
| test.go:64:2:64:31 | ... := ...[0] : pointer type | semmle.label | ... := ...[0] : pointer type |
|
||||
| test.go:65:16:65:19 | implicit dereference : Form | semmle.label | implicit dereference : Form |
|
||||
| test.go:65:16:65:41 | index expression | semmle.label | index expression |
|
||||
| test.go:70:2:70:31 | ... := ...[0] : pointer type | semmle.label | ... := ...[0] : pointer type |
|
||||
| test.go:71:16:71:19 | implicit dereference : Form | semmle.label | implicit dereference : Form |
|
||||
| test.go:75:20:75:25 | buffer | semmle.label | buffer |
|
||||
| test.go:80:2:80:32 | ... := ...[0] : pointer type | semmle.label | ... := ...[0] : pointer type |
|
||||
| test.go:81:16:81:18 | implicit dereference : Cookie | semmle.label | implicit dereference : Cookie |
|
||||
| test.go:81:16:81:24 | selection of Value | semmle.label | selection of Value |
|
||||
| test.go:86:13:86:25 | call to Cookies : slice type | semmle.label | call to Cookies : slice type |
|
||||
| test.go:87:16:87:25 | implicit dereference : Cookie | semmle.label | implicit dereference : Cookie |
|
||||
| test.go:87:16:87:31 | selection of Value | semmle.label | selection of Value |
|
||||
| test.go:97:11:97:15 | &... : pointer type | semmle.label | &... : pointer type |
|
||||
| test.go:98:16:98:21 | selection of s | semmle.label | selection of s |
|
||||
| test.go:111:21:111:42 | call to Param : string | semmle.label | call to Param : string |
|
||||
| test.go:112:16:112:42 | type assertion | semmle.label | type assertion |
|
||||
| test.go:122:11:122:32 | call to Param : string | semmle.label | call to Param : string |
|
||||
| test.go:123:16:123:20 | param | semmle.label | param |
|
||||
| test.go:128:11:128:32 | call to Param : string | semmle.label | call to Param : string |
|
||||
| test.go:129:20:129:32 | type conversion | semmle.label | type conversion |
|
||||
| test.go:134:11:134:32 | call to Param : string | semmle.label | call to Param : string |
|
||||
| test.go:135:29:135:41 | type conversion | semmle.label | type conversion |
|
||||
| test.go:146:11:146:32 | call to Param : string | semmle.label | call to Param : string |
|
||||
| test.go:148:31:148:36 | reader | semmle.label | reader |
|
||||
| test.go:162:11:162:32 | call to Param : string | semmle.label | call to Param : string |
|
||||
| test.go:163:23:163:35 | type conversion | semmle.label | type conversion |
|
||||
#select
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE-079/ReflectedXss.ql
|
||||
7
ql/test/library-tests/semmle/go/frameworks/Echo/go.mod
Normal file
7
ql/test/library-tests/semmle/go/frameworks/Echo/go.mod
Normal file
@@ -0,0 +1,7 @@
|
||||
go 1.14
|
||||
|
||||
module test
|
||||
|
||||
require (
|
||||
github.com/labstack/echo/v4 v4.1.17
|
||||
)
|
||||
213
ql/test/library-tests/semmle/go/frameworks/Echo/test.go
Normal file
213
ql/test/library-tests/semmle/go/frameworks/Echo/test.go
Normal file
@@ -0,0 +1,213 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// Section: testing echo-specific user-controlled inputs, written as HTML.
|
||||
// All are XSS vulnerabilities, except as specifically noted.
|
||||
|
||||
func testParam(ctx echo.Context) error {
|
||||
param := ctx.Param("someParam")
|
||||
ctx.HTML(200, param)
|
||||
return nil
|
||||
}
|
||||
|
||||
func testParamValues(ctx echo.Context) error {
|
||||
param := ctx.ParamValues()[0]
|
||||
ctx.HTML(200, param)
|
||||
return nil
|
||||
}
|
||||
|
||||
func testQueryParam(ctx echo.Context) error {
|
||||
param := ctx.QueryParam("someParam")
|
||||
ctx.HTML(200, param)
|
||||
return nil
|
||||
}
|
||||
|
||||
func testQueryParams(ctx echo.Context) error {
|
||||
param := ctx.QueryParams()["someParam"][0]
|
||||
ctx.HTML(200, param)
|
||||
return nil
|
||||
}
|
||||
|
||||
func testQueryString(ctx echo.Context) error {
|
||||
qstr := ctx.QueryString()
|
||||
ctx.HTML(200, qstr)
|
||||
return nil
|
||||
}
|
||||
|
||||
func testFormValue(ctx echo.Context) error {
|
||||
val := ctx.FormValue("someField")
|
||||
ctx.HTML(200, val)
|
||||
return nil
|
||||
}
|
||||
|
||||
func testFormParams(ctx echo.Context) error {
|
||||
params, _ := ctx.FormParams()
|
||||
ctx.HTML(200, params["someField"][0])
|
||||
return nil
|
||||
}
|
||||
|
||||
func testFormFile(ctx echo.Context) error {
|
||||
fileHeader, _ := ctx.FormFile("someFilename")
|
||||
file, _ := fileHeader.Open()
|
||||
buffer := make([]byte, 100)
|
||||
file.Read(buffer)
|
||||
ctx.HTMLBlob(200, buffer)
|
||||
return nil
|
||||
}
|
||||
|
||||
func testMultipartFormValue(ctx echo.Context) error {
|
||||
form, _ := ctx.MultipartForm()
|
||||
ctx.HTML(200, form.Value["someField"][0])
|
||||
return nil
|
||||
}
|
||||
|
||||
func testMultipartFormFile(ctx echo.Context) error {
|
||||
form, _ := ctx.MultipartForm()
|
||||
fileHeader := form.File["someFilename"][0]
|
||||
file, _ := fileHeader.Open()
|
||||
buffer := make([]byte, 100)
|
||||
file.Read(buffer)
|
||||
ctx.HTMLBlob(200, buffer)
|
||||
return nil
|
||||
}
|
||||
|
||||
func testCookie(ctx echo.Context) error {
|
||||
val, _ := ctx.Cookie("someKey")
|
||||
ctx.HTML(200, val.Value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func testCookies(ctx echo.Context) error {
|
||||
cookies := ctx.Cookies()
|
||||
ctx.HTML(200, cookies[0].Value)
|
||||
return nil
|
||||
}
|
||||
|
||||
type myStruct struct {
|
||||
s string
|
||||
}
|
||||
|
||||
func testBind(ctx echo.Context) error {
|
||||
data := myStruct{}
|
||||
ctx.Bind(&data)
|
||||
ctx.HTML(200, data.s)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Section: testing Context as a generic map. The empty context is clean;
|
||||
// once tainted data is written to it it becomes a problem.
|
||||
|
||||
func testGetSetEmpty(ctx echo.Context) error {
|
||||
ctx.HTML(200, ctx.Get("someKey").(string)) // OK, the context is empty
|
||||
return nil
|
||||
}
|
||||
|
||||
func testGetSet(ctx echo.Context) error {
|
||||
ctx.Set("someKey", ctx.Param("someParam"))
|
||||
ctx.HTML(200, ctx.Get("someKey").(string)) // BAD, the context is tainted
|
||||
return nil
|
||||
}
|
||||
|
||||
// Section: testing output methods defined on echo.Context. I only test HTML output methods,
|
||||
// as we don't have any queries at the moment checking for data that shouldn't go to the user,
|
||||
// even in JSON or text form.
|
||||
// All are XSS vulnerabilities, except as specifically noted.
|
||||
|
||||
func testHTML(ctx echo.Context) error {
|
||||
param := ctx.Param("someParam")
|
||||
ctx.HTML(200, param)
|
||||
return nil
|
||||
}
|
||||
|
||||
func testHTMLBlob(ctx echo.Context) error {
|
||||
param := ctx.Param("someParam")
|
||||
ctx.HTMLBlob(200, []byte(param))
|
||||
return nil
|
||||
}
|
||||
|
||||
func testBlob(ctx echo.Context) error {
|
||||
param := ctx.Param("someParam")
|
||||
ctx.Blob(200, "text/html", []byte(param)) // BAD, the content-type is HTML
|
||||
return nil
|
||||
}
|
||||
|
||||
func testBlobSafe(ctx echo.Context) error {
|
||||
param := ctx.Param("someParam")
|
||||
ctx.Blob(200, "text/plain", []byte(param)) // OK, the content-type prevents XSS
|
||||
return nil
|
||||
}
|
||||
|
||||
func testStream(ctx echo.Context) error {
|
||||
param := ctx.Param("someParam")
|
||||
reader := strings.NewReader(param)
|
||||
ctx.Stream(200, "text/html", reader) // BAD, the content-type is HTML
|
||||
return nil
|
||||
}
|
||||
|
||||
func testStreamSafe(ctx echo.Context) error {
|
||||
param := ctx.Param("someParam")
|
||||
reader := strings.NewReader(param)
|
||||
ctx.Stream(200, "text/plain", reader) // OK, the content-type prevents XSS
|
||||
return nil
|
||||
}
|
||||
|
||||
// Section: testing output methods defined on Response (XSS vulnerability)
|
||||
|
||||
func testResponseWrite(ctx echo.Context) error {
|
||||
param := ctx.Param("someParam")
|
||||
ctx.Response().Write([]byte(param))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Section: test detecting an open redirect using the Context.Redirect function:
|
||||
|
||||
func testRedirect(ctx echo.Context) error {
|
||||
param := ctx.Param("someParam")
|
||||
ctx.Redirect(301, param)
|
||||
return nil
|
||||
}
|
||||
|
||||
func testLocalRedirects(ctx echo.Context) error {
|
||||
param := ctx.Param("someParam")
|
||||
// GOOD: local redirects are unproblematic
|
||||
ctx.Redirect(301, "/local"+param)
|
||||
// BAD: this could be a non-local redirect
|
||||
ctx.Redirect(301, "/"+param)
|
||||
// GOOD: localhost redirects are unproblematic
|
||||
ctx.Redirect(301, "//localhost/"+param)
|
||||
return nil
|
||||
}
|
||||
|
||||
func testSafeSchemeChange(ctx echo.Context) error {
|
||||
// GOOD: Only safe parts of the URL are used
|
||||
url := *ctx.Request().URL
|
||||
if url.Scheme == "http" {
|
||||
url.Scheme = "https"
|
||||
ctx.Redirect(301, url.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func testNonExploitableFields(ctx echo.Context) error {
|
||||
// GOOD: all these fields and methods is disregarded for OpenRedirect attacks:
|
||||
ctx.Redirect(301, ctx.FormValue("someField"))
|
||||
params, _ := ctx.FormParams()
|
||||
ctx.Redirect(301, params["someField"][0])
|
||||
fileHeader, _ := ctx.FormFile("someFile")
|
||||
file, _ := fileHeader.Open()
|
||||
buffer := make([]byte, 100)
|
||||
file.Read(buffer)
|
||||
ctx.Redirect(301, string(buffer))
|
||||
form, _ := ctx.MultipartForm()
|
||||
ctx.Redirect(301, form.Value["someField"][0])
|
||||
val, _ := ctx.Cookie("someKey")
|
||||
ctx.Redirect(301, val.Value)
|
||||
cookies := ctx.Cookies()
|
||||
ctx.Redirect(301, cookies[0].Value)
|
||||
return nil
|
||||
}
|
||||
21
ql/test/library-tests/semmle/go/frameworks/Echo/vendor/LICENSE
vendored
Normal file
21
ql/test/library-tests/semmle/go/frameworks/Echo/vendor/LICENSE
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 LabStack
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
380
ql/test/library-tests/semmle/go/frameworks/Echo/vendor/github.com/labstack/echo/v4/stub.go
generated
vendored
Normal file
380
ql/test/library-tests/semmle/go/frameworks/Echo/vendor/github.com/labstack/echo/v4/stub.go
generated
vendored
Normal file
@@ -0,0 +1,380 @@
|
||||
// Code generated by depstubber. DO NOT EDIT.
|
||||
// This is a simple stub for github.com/labstack/echo/v4, strictly for use in testing.
|
||||
|
||||
// See the LICENSE file for information about the licensing of the original library.
|
||||
// Source: github.com/labstack/echo/v4 (exports: Context,Response; functions: )
|
||||
|
||||
// Package echo is a stub of github.com/labstack/echo/v4, generated by depstubber.
|
||||
package echo
|
||||
|
||||
import (
|
||||
bufio "bufio"
|
||||
context "context"
|
||||
io "io"
|
||||
log "log"
|
||||
multipart "mime/multipart"
|
||||
net "net"
|
||||
http "net/http"
|
||||
url "net/url"
|
||||
)
|
||||
|
||||
type Binder interface {
|
||||
Bind(_ interface{}, _ Context) error
|
||||
}
|
||||
|
||||
type Context interface {
|
||||
Attachment(_ string, _ string) error
|
||||
Bind(_ interface{}) error
|
||||
Blob(_ int, _ string, _ []byte) error
|
||||
Cookie(_ string) (*http.Cookie, error)
|
||||
Cookies() []*http.Cookie
|
||||
Echo() *Echo
|
||||
Error(_ error)
|
||||
File(_ string) error
|
||||
FormFile(_ string) (*multipart.FileHeader, error)
|
||||
FormParams() (url.Values, error)
|
||||
FormValue(_ string) string
|
||||
Get(_ string) interface{}
|
||||
HTML(_ int, _ string) error
|
||||
HTMLBlob(_ int, _ []byte) error
|
||||
Handler() HandlerFunc
|
||||
Inline(_ string, _ string) error
|
||||
IsTLS() bool
|
||||
IsWebSocket() bool
|
||||
JSON(_ int, _ interface{}) error
|
||||
JSONBlob(_ int, _ []byte) error
|
||||
JSONP(_ int, _ string, _ interface{}) error
|
||||
JSONPBlob(_ int, _ string, _ []byte) error
|
||||
JSONPretty(_ int, _ interface{}, _ string) error
|
||||
Logger() Logger
|
||||
MultipartForm() (*multipart.Form, error)
|
||||
NoContent(_ int) error
|
||||
Param(_ string) string
|
||||
ParamNames() []string
|
||||
ParamValues() []string
|
||||
Path() string
|
||||
QueryParam(_ string) string
|
||||
QueryParams() url.Values
|
||||
QueryString() string
|
||||
RealIP() string
|
||||
Redirect(_ int, _ string) error
|
||||
Render(_ int, _ string, _ interface{}) error
|
||||
Request() *http.Request
|
||||
Reset(_ *http.Request, _ http.ResponseWriter)
|
||||
Response() *Response
|
||||
Scheme() string
|
||||
Set(_ string, _ interface{})
|
||||
SetCookie(_ *http.Cookie)
|
||||
SetHandler(_ HandlerFunc)
|
||||
SetLogger(_ Logger)
|
||||
SetParamNames(_ ...string)
|
||||
SetParamValues(_ ...string)
|
||||
SetPath(_ string)
|
||||
SetRequest(_ *http.Request)
|
||||
SetResponse(_ *Response)
|
||||
Stream(_ int, _ string, _ io.Reader) error
|
||||
String(_ int, _ string) error
|
||||
Validate(_ interface{}) error
|
||||
XML(_ int, _ interface{}) error
|
||||
XMLBlob(_ int, _ []byte) error
|
||||
XMLPretty(_ int, _ interface{}, _ string) error
|
||||
}
|
||||
|
||||
type Echo struct {
|
||||
StdLogger *log.Logger
|
||||
Server *http.Server
|
||||
TLSServer *http.Server
|
||||
Listener net.Listener
|
||||
TLSListener net.Listener
|
||||
AutoTLSManager interface{}
|
||||
DisableHTTP2 bool
|
||||
Debug bool
|
||||
HideBanner bool
|
||||
HidePort bool
|
||||
HTTPErrorHandler HTTPErrorHandler
|
||||
Binder Binder
|
||||
Validator Validator
|
||||
Renderer Renderer
|
||||
Logger Logger
|
||||
IPExtractor IPExtractor
|
||||
}
|
||||
|
||||
func (_ *Echo) AcquireContext() Context {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) Add(_ string, _ string, _ HandlerFunc, _ ...MiddlewareFunc) *Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) Any(_ string, _ HandlerFunc, _ ...MiddlewareFunc) []*Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) CONNECT(_ string, _ HandlerFunc, _ ...MiddlewareFunc) *Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) DELETE(_ string, _ HandlerFunc, _ ...MiddlewareFunc) *Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) DefaultHTTPErrorHandler(_ error, _ Context) {}
|
||||
|
||||
func (_ *Echo) File(_ string, _ string, _ ...MiddlewareFunc) *Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) GET(_ string, _ HandlerFunc, _ ...MiddlewareFunc) *Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) Group(_ string, _ ...MiddlewareFunc) *Group {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) HEAD(_ string, _ HandlerFunc, _ ...MiddlewareFunc) *Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) Host(_ string, _ ...MiddlewareFunc) *Group {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) Match(_ []string, _ string, _ HandlerFunc, _ ...MiddlewareFunc) []*Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) NewContext(_ *http.Request, _ http.ResponseWriter) Context {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) OPTIONS(_ string, _ HandlerFunc, _ ...MiddlewareFunc) *Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) PATCH(_ string, _ HandlerFunc, _ ...MiddlewareFunc) *Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) POST(_ string, _ HandlerFunc, _ ...MiddlewareFunc) *Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) PUT(_ string, _ HandlerFunc, _ ...MiddlewareFunc) *Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) Pre(_ ...MiddlewareFunc) {}
|
||||
|
||||
func (_ *Echo) ReleaseContext(_ Context) {}
|
||||
|
||||
func (_ *Echo) Reverse(_ string, _ ...interface{}) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (_ *Echo) Router() *Router {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) Routers() map[string]*Router {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) Routes() []*Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) ServeHTTP(_ http.ResponseWriter, _ *http.Request) {}
|
||||
|
||||
func (_ *Echo) Shutdown(_ context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) Start(_ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) StartAutoTLS(_ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) StartH2CServer(_ string, _ interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) StartServer(_ *http.Server) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) StartTLS(_ string, _ interface{}, _ interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) Static(_ string, _ string) *Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) TRACE(_ string, _ HandlerFunc, _ ...MiddlewareFunc) *Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Echo) URI(_ HandlerFunc, _ ...interface{}) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (_ *Echo) URL(_ HandlerFunc, _ ...interface{}) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (_ *Echo) Use(_ ...MiddlewareFunc) {}
|
||||
|
||||
type Group struct{}
|
||||
|
||||
func (_ *Group) Add(_ string, _ string, _ HandlerFunc, _ ...MiddlewareFunc) *Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Group) Any(_ string, _ HandlerFunc, _ ...MiddlewareFunc) []*Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Group) CONNECT(_ string, _ HandlerFunc, _ ...MiddlewareFunc) *Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Group) DELETE(_ string, _ HandlerFunc, _ ...MiddlewareFunc) *Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Group) File(_ string, _ string) {}
|
||||
|
||||
func (_ *Group) GET(_ string, _ HandlerFunc, _ ...MiddlewareFunc) *Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Group) Group(_ string, _ ...MiddlewareFunc) *Group {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Group) HEAD(_ string, _ HandlerFunc, _ ...MiddlewareFunc) *Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Group) Match(_ []string, _ string, _ HandlerFunc, _ ...MiddlewareFunc) []*Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Group) OPTIONS(_ string, _ HandlerFunc, _ ...MiddlewareFunc) *Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Group) PATCH(_ string, _ HandlerFunc, _ ...MiddlewareFunc) *Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Group) POST(_ string, _ HandlerFunc, _ ...MiddlewareFunc) *Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Group) PUT(_ string, _ HandlerFunc, _ ...MiddlewareFunc) *Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Group) Static(_ string, _ string) {}
|
||||
|
||||
func (_ *Group) TRACE(_ string, _ HandlerFunc, _ ...MiddlewareFunc) *Route {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Group) Use(_ ...MiddlewareFunc) {}
|
||||
|
||||
type HTTPErrorHandler func(error, Context)
|
||||
|
||||
type HandlerFunc func(Context) error
|
||||
|
||||
type IPExtractor func(*http.Request) string
|
||||
|
||||
type Logger interface {
|
||||
Debug(_ ...interface{})
|
||||
Debugf(_ string, _ ...interface{})
|
||||
Debugj(_ interface{})
|
||||
Error(_ ...interface{})
|
||||
Errorf(_ string, _ ...interface{})
|
||||
Errorj(_ interface{})
|
||||
Fatal(_ ...interface{})
|
||||
Fatalf(_ string, _ ...interface{})
|
||||
Fatalj(_ interface{})
|
||||
Info(_ ...interface{})
|
||||
Infof(_ string, _ ...interface{})
|
||||
Infoj(_ interface{})
|
||||
Level() interface{}
|
||||
Output() io.Writer
|
||||
Panic(_ ...interface{})
|
||||
Panicf(_ string, _ ...interface{})
|
||||
Panicj(_ interface{})
|
||||
Prefix() string
|
||||
Print(_ ...interface{})
|
||||
Printf(_ string, _ ...interface{})
|
||||
Printj(_ interface{})
|
||||
SetHeader(_ string)
|
||||
SetLevel(_ interface{})
|
||||
SetOutput(_ io.Writer)
|
||||
SetPrefix(_ string)
|
||||
Warn(_ ...interface{})
|
||||
Warnf(_ string, _ ...interface{})
|
||||
Warnj(_ interface{})
|
||||
}
|
||||
|
||||
type MiddlewareFunc func(HandlerFunc) HandlerFunc
|
||||
|
||||
type Renderer interface {
|
||||
Render(_ io.Writer, _ string, _ interface{}, _ Context) error
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Writer http.ResponseWriter
|
||||
Status int
|
||||
Size int64
|
||||
Committed bool
|
||||
}
|
||||
|
||||
func (_ *Response) After(_ func()) {}
|
||||
|
||||
func (_ *Response) Before(_ func()) {}
|
||||
|
||||
func (_ *Response) Flush() {}
|
||||
|
||||
func (_ *Response) Header() http.Header {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func (_ *Response) Write(_ []byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (_ *Response) WriteHeader(_ int) {}
|
||||
|
||||
type Route struct {
|
||||
Method string
|
||||
Path string
|
||||
Name string
|
||||
}
|
||||
|
||||
type Router struct{}
|
||||
|
||||
func (_ *Router) Add(_ string, _ string, _ HandlerFunc) {}
|
||||
|
||||
func (_ *Router) Find(_ string, _ string, _ Context) {}
|
||||
|
||||
type Validator interface {
|
||||
Validate(_ interface{}) error
|
||||
}
|
||||
3
ql/test/library-tests/semmle/go/frameworks/Echo/vendor/modules.txt
vendored
Normal file
3
ql/test/library-tests/semmle/go/frameworks/Echo/vendor/modules.txt
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# github.com/labstack/echo/v4 v4.1.17
|
||||
## explicit
|
||||
github.com/labstack/echo/v4
|
||||
Reference in New Issue
Block a user