diff --git a/ql/src/experimental/frameworks/Gin.qll b/ql/src/experimental/frameworks/Gin.qll new file mode 100644 index 00000000000..019e4473774 --- /dev/null +++ b/ql/src/experimental/frameworks/Gin.qll @@ -0,0 +1,173 @@ +/** + * Provides classes for working with untrusted flow sources from the `github.com/gin-gonic/gin` package. + */ + +import go + +private module Gin { + /** + * Data from a `Context` struct, considered as a source of untrusted flow. + */ + private class GithubComGinGonicGinContextSource extends UntrustedFlowSource::Range { + GithubComGinGonicGinContextSource() { + exists(string packagePath, string typeName | + packagePath = "github.com/gin-gonic/gin" and + typeName = "Context" + | + // Method calls: + exists(DataFlow::MethodCallNode call, string methodName | + call.getTarget().hasQualifiedName(packagePath, typeName, methodName) and + ( + methodName = "FullPath" + or + methodName = "GetHeader" + or + methodName = "QueryArray" + or + methodName = "Query" + or + methodName = "PostFormArray" + or + methodName = "PostForm" + or + methodName = "Param" + or + methodName = "GetStringSlice" + or + methodName = "GetString" + or + methodName = "GetRawData" + or + methodName = "ClientIP" + or + methodName = "ContentType" + or + methodName = "Cookie" + or + methodName = "GetQueryArray" + or + methodName = "GetQuery" + or + methodName = "GetPostFormArray" + or + methodName = "GetPostForm" + or + methodName = "DefaultPostForm" + or + methodName = "DefaultQuery" + or + methodName = "GetPostFormMap" + or + methodName = "GetQueryMap" + or + methodName = "GetStringMap" + or + methodName = "GetStringMapString" + or + methodName = "GetStringMapStringSlice" + or + methodName = "PostFormMap" + or + methodName = "QueryMap" + ) + | + this = call.getResult(0) + or + this = call.getResult() + ) + or + // Field reads: + exists(DataFlow::Field fld | + fld.hasQualifiedName(packagePath, typeName, "Accepted") and + this = fld.getARead() + ) + ) + } + } + + /** + * Data from a `Params` slice, considered as a source of untrusted flow. + */ + private class GithubComGinGonicGinParamsSource extends UntrustedFlowSource::Range { + GithubComGinGonicGinParamsSource() { + exists(string packagePath, string typeName | + packagePath = "github.com/gin-gonic/gin" and + typeName = "Params" + | + // Any read of a variable of this type: + exists(DataFlow::ReadNode read | read.getType().hasQualifiedName(packagePath, typeName) | + this = read + ) + or + // Method calls: + exists(DataFlow::MethodCallNode call | + call.getTarget().hasQualifiedName(packagePath, typeName, ["ByName", "Get"]) + | + this = call.getResult(0) + or + this = call.getResult() + ) + ) + } + } + + /** + * Data from a `Param` struct, considered as a source of untrusted flow. + */ + private class GithubComGinGonicGinParamSource extends UntrustedFlowSource::Range { + GithubComGinGonicGinParamSource() { + exists(string packagePath, string typeName | + packagePath = "github.com/gin-gonic/gin" and + typeName = "Param" + | + // Any read of a variable of this type: + exists(DataFlow::ReadNode read | read.getType().hasQualifiedName(packagePath, typeName) | + this = read + ) + or + // Field reads: + exists(DataFlow::Field fld | fld.hasQualifiedName(packagePath, typeName, ["Key", "Value"]) | + this = fld.getARead() + ) + ) + } + } + + /** + * A call to a method on `Context` struct that unmarshals data into a target. + */ + private class GithubComGinGonicGinContextBindSource extends UntrustedFlowSource::Range { + GithubComGinGonicGinContextBindSource() { + exists(string packagePath, string typeName | + packagePath = "github.com/gin-gonic/gin" and + typeName = "Context" + | + exists(DataFlow::MethodCallNode call, string methodName | + call.getTarget().hasQualifiedName(packagePath, typeName, methodName) and + ( + methodName = "BindJSON" or + methodName = "BindYAML" or + methodName = "BindXML" or + methodName = "BindUri" or + methodName = "BindQuery" or + methodName = "BindWith" or + methodName = "BindHeader" or + methodName = "MustBindWith" or + methodName = "Bind" or + methodName = "ShouldBind" or + methodName = "ShouldBindBodyWith" or + methodName = "ShouldBindJSON" or + methodName = "ShouldBindQuery" or + methodName = "ShouldBindUri" or + methodName = "ShouldBindHeader" or + methodName = "ShouldBindWith" or + methodName = "ShouldBindXML" or + methodName = "ShouldBindYAML" + ) + | + this = call.getArgument(0) + ) + ) + } + } +} diff --git a/ql/test/experimental/frameworks/Gin/Gin.expected b/ql/test/experimental/frameworks/Gin/Gin.expected new file mode 100644 index 00000000000..b30b4861d87 --- /dev/null +++ b/ql/test/experimental/frameworks/Gin/Gin.expected @@ -0,0 +1,63 @@ +| Gin.go:23:10:23:29 | call to GetHeader | +| Gin.go:27:10:27:30 | call to QueryArray | +| Gin.go:31:10:31:25 | call to Query | +| Gin.go:35:10:35:33 | call to PostFormArray | +| Gin.go:39:10:39:28 | call to PostForm | +| Gin.go:43:10:43:25 | call to Param | +| Gin.go:47:10:47:34 | call to GetStringSlice | +| Gin.go:51:10:51:29 | call to GetString | +| Gin.go:55:3:55:28 | ... := ...[0] | +| Gin.go:59:10:59:23 | call to ClientIP | +| Gin.go:63:10:63:26 | call to ContentType | +| Gin.go:67:3:67:29 | ... := ...[0] | +| Gin.go:71:3:71:36 | ... := ...[0] | +| Gin.go:75:3:75:31 | ... := ...[0] | +| Gin.go:79:3:79:39 | ... := ...[0] | +| Gin.go:83:3:83:34 | ... := ...[0] | +| Gin.go:87:10:87:52 | call to DefaultPostForm | +| Gin.go:91:10:91:49 | call to DefaultQuery | +| Gin.go:95:3:95:37 | ... := ...[0] | +| Gin.go:99:3:99:34 | ... := ...[0] | +| Gin.go:103:10:103:32 | call to GetStringMap | +| Gin.go:107:10:107:38 | call to GetStringMapString | +| Gin.go:111:10:111:43 | call to GetStringMapStringSlice | +| Gin.go:115:10:115:31 | call to PostFormMap | +| Gin.go:119:10:119:28 | call to QueryMap | +| Gin.go:123:10:123:23 | call to FullPath | +| Gin.go:129:10:129:21 | selection of Accepted | +| Gin.go:133:10:133:19 | selection of Params | +| Gin.go:134:7:134:9 | val | +| Gin.go:134:7:134:12 | index expression | +| Gin.go:134:7:134:18 | selection of Value | +| Gin.go:139:10:139:19 | selection of Params | +| Gin.go:139:10:139:22 | index expression | +| Gin.go:140:7:140:9 | val | +| Gin.go:140:7:140:15 | selection of Value | +| Gin.go:143:10:143:19 | selection of Params | +| Gin.go:143:10:143:34 | call to ByName | +| Gin.go:147:3:147:34 | ... := ...[0] | +| Gin.go:147:13:147:22 | selection of Params | +| Gin.go:153:12:153:21 | selection of Params | +| Gin.go:153:12:153:24 | index expression | +| Gin.go:154:10:154:14 | param | +| Gin.go:154:10:154:18 | selection of Key | +| Gin.go:155:10:155:14 | param | +| Gin.go:155:10:155:20 | selection of Value | +| Gin.go:163:16:163:22 | &... | +| Gin.go:168:15:168:21 | &... | +| Gin.go:173:16:173:22 | &... | +| Gin.go:178:15:178:21 | &... | +| Gin.go:183:17:183:23 | &... | +| Gin.go:188:20:188:26 | &... | +| Gin.go:193:16:193:22 | &... | +| Gin.go:198:12:198:18 | &... | +| Gin.go:203:18:203:24 | &... | +| Gin.go:208:26:208:32 | &... | +| Gin.go:213:22:213:28 | &... | +| Gin.go:218:23:218:29 | &... | +| Gin.go:223:21:223:27 | &... | +| Gin.go:228:22:228:28 | &... | +| Gin.go:233:21:233:27 | &... | +| Gin.go:238:22:238:28 | &... | +| Gin.go:243:18:243:24 | &... | +| Gin.go:248:24:248:30 | &... | diff --git a/ql/test/experimental/frameworks/Gin/Gin.go b/ql/test/experimental/frameworks/Gin/Gin.go new file mode 100644 index 00000000000..774ce47e822 --- /dev/null +++ b/ql/test/experimental/frameworks/Gin/Gin.go @@ -0,0 +1,251 @@ +package main + +//go:generate depstubber -vendor github.com/gin-gonic/gin Context +//go:generate depstubber -vendor github.com/gin-gonic/gin/binding "" YAML + +import ( + "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" +) + +func main() {} + +type Person struct { + Name string `form:"name"` + Address string `form:"address"` +} + +func use(val string) {} + +// gin +func ginHandler(ctx *gin.Context) { + { + val := ctx.GetHeader("key") + use(val) + } + { + val := ctx.QueryArray("key") + use(val[0]) + } + { + val := ctx.Query("key") + use(val) + } + { + val := ctx.PostFormArray("key") + use(val[0]) + } + { + val := ctx.PostForm("key") + use(val) + } + { + val := ctx.Param("key") + use(val) + } + { + val := ctx.GetStringSlice("key") + use(val[0]) + } + { + val := ctx.GetString("key") + use(val) + } + { + val, _ := ctx.GetRawData() + use(string(val)) + } + { + val := ctx.ClientIP() + use(val) + } + { + val := ctx.ContentType() + use(val) + } + { + val, _ := ctx.Cookie("key") + use(val) + } + { + val, _ := ctx.GetQueryArray("key") + use(val[0]) + } + { + val, _ := ctx.GetQuery("key") + use(val) + } + { + val, _ := ctx.GetPostFormArray("key") + use(val[0]) + } + { + val, _ := ctx.GetPostForm("key") + use(val) + } + { + val := ctx.DefaultPostForm("key", "default-value") + use(val) + } + { + val := ctx.DefaultQuery("key", "default-value") + use(val) + } + { + val, _ := ctx.GetPostFormMap("key") + use(val["a"]) + } + { + val, _ := ctx.GetQueryMap("key") + use(val["a"]) + } + { + val := ctx.GetStringMap("key") + use(val["a"].(string)) + } + { + val := ctx.GetStringMapString("key") + use(val["a"]) + } + { + val := ctx.GetStringMapStringSlice("key") + use(val["a"][0]) + } + { + val := ctx.PostFormMap("key") + use(val["a"]) + } + { + val := ctx.QueryMap("key") + use(val["a"]) + } + { + val := ctx.FullPath() + use(val) + } + + // fields: + { + val := ctx.Accepted + use(val[0]) + } + { + val := ctx.Params + use(val[0].Value) + } + + // Params: + { + val := ctx.Params[0] + use(val.Value) + } + { + val := ctx.Params.ByName("name") + use(val) + } + { + val, _ := ctx.Params.Get("name") + use(val) + } + + // Param: + { + param := ctx.Params[0] + key := param.Key + val := param.Value + use(key) + use(val) + } + + // bind: + { + var person Person + ctx.BindYAML(&person) + use(person.Name) + } + { + var person Person + ctx.BindXML(&person) + use(person.Name) + } + { + var person Person + ctx.BindWith(&person, binding.YAML) + use(person.Name) + } + { + var person Person + ctx.BindUri(&person) + use(person.Name) + } + { + var person Person + ctx.BindQuery(&person) + use(person.Name) + } + { + var person Person + ctx.MustBindWith(&person, binding.YAML) + use(person.Name) + } + { + var person Person + ctx.BindJSON(&person) + use(person.Name) + } + { + var person Person + ctx.Bind(&person) + use(person.Name) + } + { + var person Person + ctx.ShouldBind(&person) + use(person.Name) + } + { + var person Person + ctx.ShouldBindBodyWith(&person, binding.YAML) + use(person.Name) + } + { + var person Person + ctx.ShouldBindJSON(&person) + use(person.Name) + } + { + var person Person + ctx.ShouldBindQuery(&person) + use(person.Name) + } + { + var person Person + ctx.ShouldBindUri(&person) + use(person.Name) + } + { + var person Person + ctx.ShouldBindWith(&person, binding.YAML) + use(person.Name) + } + { + var person Person + ctx.ShouldBindXML(&person) + use(person.Name) + } + { + var person Person + ctx.ShouldBindYAML(&person) + use(person.Name) + } + { + var person Person + ctx.BindHeader(&person) + use(person.Name) + } + { + var person Person + ctx.ShouldBindHeader(&person) + use(person.Name) + } +} diff --git a/ql/test/experimental/frameworks/Gin/Gin.ql b/ql/test/experimental/frameworks/Gin/Gin.ql new file mode 100644 index 00000000000..91f3324f23a --- /dev/null +++ b/ql/test/experimental/frameworks/Gin/Gin.ql @@ -0,0 +1,4 @@ +import go +import experimental.frameworks.Gin + +select any(UntrustedFlowSource src) diff --git a/ql/test/experimental/frameworks/Gin/go.mod b/ql/test/experimental/frameworks/Gin/go.mod new file mode 100644 index 00000000000..d6312810ebf --- /dev/null +++ b/ql/test/experimental/frameworks/Gin/go.mod @@ -0,0 +1,5 @@ +module example.com/m + +go 1.14 + +require github.com/gin-gonic/gin v1.6.2 diff --git a/ql/test/experimental/frameworks/Gin/vendor/github.com/gin-gonic/gin/LICENSE b/ql/test/experimental/frameworks/Gin/vendor/github.com/gin-gonic/gin/LICENSE new file mode 100644 index 00000000000..1ff7f370605 --- /dev/null +++ b/ql/test/experimental/frameworks/Gin/vendor/github.com/gin-gonic/gin/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Manuel Martínez-Almeida + +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. diff --git a/ql/test/experimental/frameworks/Gin/vendor/github.com/gin-gonic/gin/binding/stub.go b/ql/test/experimental/frameworks/Gin/vendor/github.com/gin-gonic/gin/binding/stub.go new file mode 100644 index 00000000000..43fd634edcd --- /dev/null +++ b/ql/test/experimental/frameworks/Gin/vendor/github.com/gin-gonic/gin/binding/stub.go @@ -0,0 +1,12 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/gin-gonic/gin/binding, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/gin-gonic/gin/binding (exports: ; functions: YAML) + +// Package binding is a stub of github.com/gin-gonic/gin/binding, generated by depstubber. +package binding + +import () + +var YAML interface{} = nil diff --git a/ql/test/experimental/frameworks/Gin/vendor/github.com/gin-gonic/gin/stub.go b/ql/test/experimental/frameworks/Gin/vendor/github.com/gin-gonic/gin/stub.go new file mode 100644 index 00000000000..eb68095e53e --- /dev/null +++ b/ql/test/experimental/frameworks/Gin/vendor/github.com/gin-gonic/gin/stub.go @@ -0,0 +1,500 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/gin-gonic/gin, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/gin-gonic/gin (exports: Context; functions: ) + +// Package gin is a stub of github.com/gin-gonic/gin, generated by depstubber. +package gin + +import ( + bufio "bufio" + io "io" + multipart "mime/multipart" + net "net" + http "net/http" + sync "sync" + time "time" +) + +type Context struct { + Request *http.Request + Writer ResponseWriter + Params Params + KeysMutex *sync.RWMutex + Keys map[string]interface{} + Errors interface{} + Accepted []string +} + +func (_ *Context) Abort() {} + +func (_ *Context) AbortWithError(_ int, _ interface { + Error() string +}) *Error { + return nil +} + +func (_ *Context) AbortWithStatus(_ int) {} + +func (_ *Context) AbortWithStatusJSON(_ int, _ interface{}) {} + +func (_ *Context) AsciiJSON(_ int, _ interface{}) {} + +func (_ *Context) Bind(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) BindHeader(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) BindJSON(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) BindQuery(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) BindUri(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) BindWith(_ interface{}, _ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) BindXML(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) BindYAML(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) ClientIP() string { + return "" +} + +func (_ *Context) ContentType() string { + return "" +} + +func (_ *Context) Cookie(_ string) (string, interface { + Error() string +}) { + return "", nil +} + +func (_ *Context) Copy() *Context { + return nil +} + +func (_ *Context) Data(_ int, _ string, _ []uint8) {} + +func (_ *Context) DataFromReader(_ int, _ int64, _ string, _ io.Reader, _ map[string]string) {} + +func (_ *Context) Deadline() (time.Time, bool) { + return time.Time{}, false +} + +func (_ *Context) DefaultPostForm(_ string, _ string) string { + return "" +} + +func (_ *Context) DefaultQuery(_ string, _ string) string { + return "" +} + +func (_ *Context) Done() <-chan struct{} { + return nil +} + +func (_ *Context) Err() interface { + Error() string +} { + return nil +} + +func (_ *Context) Error(_ interface { + Error() string +}) *Error { + return nil +} + +func (_ *Context) File(_ string) {} + +func (_ *Context) FileAttachment(_ string, _ string) {} + +func (_ *Context) FileFromFS(_ string, _ http.FileSystem) {} + +func (_ *Context) FormFile(_ string) (*multipart.FileHeader, interface { + Error() string +}) { + return nil, nil +} + +func (_ *Context) FullPath() string { + return "" +} + +func (_ *Context) Get(_ string) (interface{}, bool) { + return nil, false +} + +func (_ *Context) GetBool(_ string) bool { + return false +} + +func (_ *Context) GetDuration(_ string) time.Duration { + return 0 +} + +func (_ *Context) GetFloat64(_ string) float64 { + return 0 +} + +func (_ *Context) GetHeader(_ string) string { + return "" +} + +func (_ *Context) GetInt(_ string) int { + return 0 +} + +func (_ *Context) GetInt64(_ string) int64 { + return 0 +} + +func (_ *Context) GetPostForm(_ string) (string, bool) { + return "", false +} + +func (_ *Context) GetPostFormArray(_ string) ([]string, bool) { + return nil, false +} + +func (_ *Context) GetPostFormMap(_ string) (map[string]string, bool) { + return nil, false +} + +func (_ *Context) GetQuery(_ string) (string, bool) { + return "", false +} + +func (_ *Context) GetQueryArray(_ string) ([]string, bool) { + return nil, false +} + +func (_ *Context) GetQueryMap(_ string) (map[string]string, bool) { + return nil, false +} + +func (_ *Context) GetRawData() ([]uint8, interface { + Error() string +}) { + return nil, nil +} + +func (_ *Context) GetString(_ string) string { + return "" +} + +func (_ *Context) GetStringMap(_ string) map[string]interface{} { + return nil +} + +func (_ *Context) GetStringMapString(_ string) map[string]string { + return nil +} + +func (_ *Context) GetStringMapStringSlice(_ string) map[string][]string { + return nil +} + +func (_ *Context) GetStringSlice(_ string) []string { + return nil +} + +func (_ *Context) GetTime(_ string) time.Time { + return time.Time{} +} + +func (_ *Context) HTML(_ int, _ string, _ interface{}) {} + +func (_ *Context) Handler() HandlerFunc { + return nil +} + +func (_ *Context) HandlerName() string { + return "" +} + +func (_ *Context) HandlerNames() []string { + return nil +} + +func (_ *Context) Header(_ string, _ string) {} + +func (_ *Context) IndentedJSON(_ int, _ interface{}) {} + +func (_ *Context) IsAborted() bool { + return false +} + +func (_ *Context) IsWebsocket() bool { + return false +} + +func (_ *Context) JSON(_ int, _ interface{}) {} + +func (_ *Context) JSONP(_ int, _ interface{}) {} + +func (_ *Context) MultipartForm() (*multipart.Form, interface { + Error() string +}) { + return nil, nil +} + +func (_ *Context) MustBindWith(_ interface{}, _ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) MustGet(_ string) interface{} { + return nil +} + +func (_ *Context) Negotiate(_ int, _ Negotiate) {} + +func (_ *Context) NegotiateFormat(_ ...string) string { + return "" +} + +func (_ *Context) Next() {} + +func (_ *Context) Param(_ string) string { + return "" +} + +func (_ *Context) PostForm(_ string) string { + return "" +} + +func (_ *Context) PostFormArray(_ string) []string { + return nil +} + +func (_ *Context) PostFormMap(_ string) map[string]string { + return nil +} + +func (_ *Context) ProtoBuf(_ int, _ interface{}) {} + +func (_ *Context) PureJSON(_ int, _ interface{}) {} + +func (_ *Context) Query(_ string) string { + return "" +} + +func (_ *Context) QueryArray(_ string) []string { + return nil +} + +func (_ *Context) QueryMap(_ string) map[string]string { + return nil +} + +func (_ *Context) Redirect(_ int, _ string) {} + +func (_ *Context) Render(_ int, _ interface{}) {} + +func (_ *Context) SSEvent(_ string, _ interface{}) {} + +func (_ *Context) SaveUploadedFile(_ *multipart.FileHeader, _ string) interface { + Error() string +} { + return nil +} + +func (_ *Context) SecureJSON(_ int, _ interface{}) {} + +func (_ *Context) Set(_ string, _ interface{}) {} + +func (_ *Context) SetAccepted(_ ...string) {} + +func (_ *Context) SetCookie(_ string, _ string, _ int, _ string, _ string, _ bool, _ bool) {} + +func (_ *Context) SetSameSite(_ http.SameSite) {} + +func (_ *Context) ShouldBind(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) ShouldBindBodyWith(_ interface{}, _ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) ShouldBindHeader(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) ShouldBindJSON(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) ShouldBindQuery(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) ShouldBindUri(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) ShouldBindWith(_ interface{}, _ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) ShouldBindXML(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) ShouldBindYAML(_ interface{}) interface { + Error() string +} { + return nil +} + +func (_ *Context) Status(_ int) {} + +func (_ *Context) Stream(_ func(io.Writer) bool) bool { + return false +} + +func (_ *Context) String(_ int, _ string, _ ...interface{}) {} + +func (_ *Context) Value(_ interface{}) interface{} { + return nil +} + +func (_ *Context) XML(_ int, _ interface{}) {} + +func (_ *Context) YAML(_ int, _ interface{}) {} + +type Error struct { + Err interface { + Error() string + } + Type ErrorType + Meta interface{} +} + +func (_ Error) Error() string { + return "" +} + +func (_ *Error) IsType(_ ErrorType) bool { + return false +} + +func (_ *Error) JSON() interface{} { + return nil +} + +func (_ *Error) MarshalJSON() ([]uint8, interface { + Error() string +}) { + return nil, nil +} + +func (_ *Error) SetMeta(_ interface{}) *Error { + return nil +} + +func (_ *Error) SetType(_ ErrorType) *Error { + return nil +} + +type ErrorType uint64 + +type HandlerFunc func(*Context) + +type Negotiate struct { + Offered []string + HTMLName string + HTMLData interface{} + JSONData interface{} + XMLData interface{} + YAMLData interface{} + Data interface{} +} + +type Param struct { + Key string + Value string +} + +type Params []Param + +func (_ Params) ByName(_ string) string { + return "" +} + +func (_ Params) Get(_ string) (string, bool) { + return "", false +} + +type ResponseWriter interface { + CloseNotify() <-chan bool + Flush() + Header() http.Header + Hijack() (net.Conn, *bufio.ReadWriter, interface { + Error() string + }) + Pusher() http.Pusher + Size() int + Status() int + Write(_ []uint8) (int, interface { + Error() string + }) + WriteHeader(_ int) + WriteHeaderNow() + WriteString(_ string) (int, interface { + Error() string + }) + Written() bool +} diff --git a/ql/test/experimental/frameworks/Gin/vendor/modules.txt b/ql/test/experimental/frameworks/Gin/vendor/modules.txt new file mode 100644 index 00000000000..5f2816316f4 --- /dev/null +++ b/ql/test/experimental/frameworks/Gin/vendor/modules.txt @@ -0,0 +1,3 @@ +# github.com/gin-gonic/gin v1.6.2 +## explicit +github.com/gin-gonic/gin