From bb2d5ea6b5b4f54d962a510408bf51dc7b7710a5 Mon Sep 17 00:00:00 2001 From: Ricter Z Date: Tue, 9 Jun 2020 14:54:31 +0800 Subject: [PATCH 1/8] add some sinks in commonly-used SQL libraries --- ql/src/go.qll | 3 ++ ql/src/semmle/go/frameworks/HTTP.qll | 1 + .../go/frameworks/thirdpartlib/Encoding.qll | 25 ++++++++++ .../go/frameworks/thirdpartlib/HTTP.qll | 27 +++++++++++ .../semmle/go/frameworks/thirdpartlib/SQL.qll | 48 +++++++++++++++++++ 5 files changed, 104 insertions(+) create mode 100644 ql/src/semmle/go/frameworks/thirdpartlib/Encoding.qll create mode 100644 ql/src/semmle/go/frameworks/thirdpartlib/HTTP.qll create mode 100644 ql/src/semmle/go/frameworks/thirdpartlib/SQL.qll diff --git a/ql/src/go.qll b/ql/src/go.qll index 512fd06d..5016eb5c 100644 --- a/ql/src/go.qll +++ b/ql/src/go.qll @@ -37,4 +37,7 @@ import semmle.go.frameworks.SystemCommandExecutors import semmle.go.frameworks.Testing import semmle.go.frameworks.WebSocket import semmle.go.frameworks.XPath +import semmle.go.frameworks.thirdpartlib.HTTP +import semmle.go.frameworks.thirdpartlib.SQL +import semmle.go.frameworks.thirdpartlib.Encoding import semmle.go.security.FlowSources diff --git a/ql/src/semmle/go/frameworks/HTTP.qll b/ql/src/semmle/go/frameworks/HTTP.qll index a159d75d..8a27dbf0 100644 --- a/ql/src/semmle/go/frameworks/HTTP.qll +++ b/ql/src/semmle/go/frameworks/HTTP.qll @@ -231,3 +231,4 @@ private module StdlibHttp { } } } + diff --git a/ql/src/semmle/go/frameworks/thirdpartlib/Encoding.qll b/ql/src/semmle/go/frameworks/thirdpartlib/Encoding.qll new file mode 100644 index 00000000..ebc329da --- /dev/null +++ b/ql/src/semmle/go/frameworks/thirdpartlib/Encoding.qll @@ -0,0 +1,25 @@ +/** + * Provides classes modeling security-relevant aspects of the third-part libraries. + */ + +import go + +module ThirdPartEncodingJson { + /** Provides models of some functions in the `github.com/json-iterator/go` package. */ + class JsoniterUnmarshalingFunction extends TaintTracking::FunctionModel, UnmarshalingFunction::Range { + + JsoniterUnmarshalingFunction() { + this.hasQualifiedName("github.com/json-iterator/go", "Unmarshal") + } + + override DataFlow::FunctionInput getAnInput() { result.isParameter(0) } + override DataFlow::FunctionOutput getOutput() { result.isParameter(1) } + + override string getFormat() { result = "JSON" } + + override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { + inp = getAnInput() and outp = getOutput() + } + } +} + diff --git a/ql/src/semmle/go/frameworks/thirdpartlib/HTTP.qll b/ql/src/semmle/go/frameworks/thirdpartlib/HTTP.qll new file mode 100644 index 00000000..fbc2a7a1 --- /dev/null +++ b/ql/src/semmle/go/frameworks/thirdpartlib/HTTP.qll @@ -0,0 +1,27 @@ +/** + * Provides classes for working with HTTP-related concepts such as requests and responses. + */ + +import go + +module ThirdPartHttpLib { + /** + * Source from go-resultful + * Document: https://github.com/emicklei/go-restful + */ + class GoRestfulSource extends DataFlow::Node, UntrustedFlowSource::Range { + GoRestfulSource() { + exists( + Method meth, string name | + meth.hasQualifiedName("github.com/emicklei/go-restful", "Request", name) and + asExpr() = meth.getACall().asExpr() and + ( + name = "QueryParameters" or name = "QueryParameter" or + name = "BodyParamater" or name = "HeaderParameter" or + name = "PathParameter" or name = "PathParameters" + ) + ) + } + } +} + diff --git a/ql/src/semmle/go/frameworks/thirdpartlib/SQL.qll b/ql/src/semmle/go/frameworks/thirdpartlib/SQL.qll new file mode 100644 index 00000000..438646a5 --- /dev/null +++ b/ql/src/semmle/go/frameworks/thirdpartlib/SQL.qll @@ -0,0 +1,48 @@ +/** + * Provides classes for working with SQL-related concepts such as queries. + */ + +import go + +module ThirdPartSQL { + + /** Sinks of github.com/jinzhu/gorm */ + class GormSink extends DataFlow::Node, SQL::QueryString::Range { + GormSink() { + exists( + Method meth, string name | + meth.hasQualifiedName("github.com/jinzhu/gorm", "DB", name) and + asExpr() = meth.getACall().getArgument(0).asExpr() and + ( + name = "Where" or name = "Raw" or name = "Order" or name = "Not" or name = "Or" or + name = "Select" or name = "Table" or name = "Group" or name = "Having" or name = "Joins" + ) + ) + } + } + + /** Sinks of github.com/jmoiron/sqlx */ + class SqlxSink extends DataFlow::Node, SQL::QueryString::Range { + SqlxSink() { + exists( + Method meth, string name, int n | + ( + meth.hasQualifiedName("github.com/jmoiron/sqlx", "DB", name) or + meth.hasQualifiedName("github.com/jmoiron/sqlx", "Tx", name) + ) and this = meth.getACall().getArgument(n) | + ( + (name = "Select" or name = "Get") and n = 1 + ) + or + ( + ( + name = "MustExec" or name = "Queryx" or + name = "NamedExec" or name = "NamedQuery" + ) + and n = 0 + ) + ) + } + } + +} From b96546b0f84358842130eecd3e0d2153c6bc5652 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Fri, 24 Jul 2020 09:35:17 +0100 Subject: [PATCH 2/8] Improve style of library models --- ql/src/go.qll | 4 +- ql/src/semmle/go/frameworks/Encoding.qll | 23 +++++++++ ql/src/semmle/go/frameworks/HTTP.qll | 23 +++++++++ ql/src/semmle/go/frameworks/SQL.qll | 25 ++++++++++ .../go/frameworks/thirdpartlib/Encoding.qll | 25 ---------- .../go/frameworks/thirdpartlib/HTTP.qll | 27 ----------- .../semmle/go/frameworks/thirdpartlib/SQL.qll | 48 ------------------- 7 files changed, 72 insertions(+), 103 deletions(-) create mode 100644 ql/src/semmle/go/frameworks/Encoding.qll delete mode 100644 ql/src/semmle/go/frameworks/thirdpartlib/Encoding.qll delete mode 100644 ql/src/semmle/go/frameworks/thirdpartlib/HTTP.qll delete mode 100644 ql/src/semmle/go/frameworks/thirdpartlib/SQL.qll diff --git a/ql/src/go.qll b/ql/src/go.qll index 5016eb5c..0f497ac8 100644 --- a/ql/src/go.qll +++ b/ql/src/go.qll @@ -26,6 +26,7 @@ import semmle.go.dataflow.GlobalValueNumbering import semmle.go.dataflow.SSA import semmle.go.dataflow.TaintTracking import semmle.go.frameworks.Email +import semmle.go.frameworks.Encoding import semmle.go.frameworks.Glog import semmle.go.frameworks.HTTP import semmle.go.frameworks.Macaron @@ -37,7 +38,4 @@ import semmle.go.frameworks.SystemCommandExecutors import semmle.go.frameworks.Testing import semmle.go.frameworks.WebSocket import semmle.go.frameworks.XPath -import semmle.go.frameworks.thirdpartlib.HTTP -import semmle.go.frameworks.thirdpartlib.SQL -import semmle.go.frameworks.thirdpartlib.Encoding import semmle.go.security.FlowSources diff --git a/ql/src/semmle/go/frameworks/Encoding.qll b/ql/src/semmle/go/frameworks/Encoding.qll new file mode 100644 index 00000000..2646b4f7 --- /dev/null +++ b/ql/src/semmle/go/frameworks/Encoding.qll @@ -0,0 +1,23 @@ +/** + * Provides classes modelling taint propagation through the `json-iterator` package. + */ + +import go + +/** Models json-iterator's Unmarshal function, propagating taint from the JSON input to the decoded object. */ +private class JsonIteratorUnmarshalFunction extends TaintTracking::FunctionModel, + UnmarshalingFunction::Range { + JsonIteratorUnmarshalFunction() { + this.hasQualifiedName("github.com/json-iterator/go", "Unmarshal") + } + + override DataFlow::FunctionInput getAnInput() { result.isParameter(0) } + + override DataFlow::FunctionOutput getOutput() { result.isParameter(1) } + + override string getFormat() { result = "JSON" } + + override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { + inp = getAnInput() and outp = getOutput() + } +} diff --git a/ql/src/semmle/go/frameworks/HTTP.qll b/ql/src/semmle/go/frameworks/HTTP.qll index 8a27dbf0..54c58809 100644 --- a/ql/src/semmle/go/frameworks/HTTP.qll +++ b/ql/src/semmle/go/frameworks/HTTP.qll @@ -232,3 +232,26 @@ private module StdlibHttp { } } +/** + * Provides models of the go-restful library (https://github.com/emicklei/go-restful). + */ +private module GoRestfulHttp { + /** + * A model for methods defined on go-restful's `Request` object that may return user-controlled data. + */ + private class GoRestfulSourceMethod extends Method { + GoRestfulSourceMethod() { + this + .hasQualifiedName("github.com/emicklei/go-restful", "Request", + ["QueryParameters", "QueryParameter", "BodyParameter", "HeaderParameter", + "PathParameter", "PathParameters"]) + } + } + + /** + * A model of go-restful's `Request` object as a source of user-controlled data. + */ + private class GoRestfulSource extends UntrustedFlowSource::Range { + GoRestfulSource() { this = any(GoRestfulSourceMethod g).getACall() } + } +} diff --git a/ql/src/semmle/go/frameworks/SQL.qll b/ql/src/semmle/go/frameworks/SQL.qll index 1244e5cc..a959f9be 100644 --- a/ql/src/semmle/go/frameworks/SQL.qll +++ b/ql/src/semmle/go/frameworks/SQL.qll @@ -160,4 +160,29 @@ module SQL { } } } + + /** A model for sinks of github.com/jinzhu/gorm. */ + private class GormSink extends SQL::QueryString::Range { + GormSink() { + exists(Method meth, string name | + meth.hasQualifiedName("github.com/jinzhu/gorm", "DB", name) and + this = meth.getACall().getArgument(0) and + name in ["Where", "Raw", "Order", "Not", "Or", "Select", "Table", "Group", "Having", "Joins"] + ) + } + } + + /** A model for sinks of github.com/jmoiron/sqlx. */ + private class SqlxSink extends SQL::QueryString::Range { + SqlxSink() { + exists(Method meth, string name, int n | + meth.hasQualifiedName("github.com/jmoiron/sqlx", ["DB", "Tx"], name) and + this = meth.getACall().getArgument(n) + | + name = ["Select", "Get"] and n = 1 + or + name = ["MustExec", "Queryx", "NamedExec", "NamedQuery"] and n = 0 + ) + } + } } diff --git a/ql/src/semmle/go/frameworks/thirdpartlib/Encoding.qll b/ql/src/semmle/go/frameworks/thirdpartlib/Encoding.qll deleted file mode 100644 index ebc329da..00000000 --- a/ql/src/semmle/go/frameworks/thirdpartlib/Encoding.qll +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Provides classes modeling security-relevant aspects of the third-part libraries. - */ - -import go - -module ThirdPartEncodingJson { - /** Provides models of some functions in the `github.com/json-iterator/go` package. */ - class JsoniterUnmarshalingFunction extends TaintTracking::FunctionModel, UnmarshalingFunction::Range { - - JsoniterUnmarshalingFunction() { - this.hasQualifiedName("github.com/json-iterator/go", "Unmarshal") - } - - override DataFlow::FunctionInput getAnInput() { result.isParameter(0) } - override DataFlow::FunctionOutput getOutput() { result.isParameter(1) } - - override string getFormat() { result = "JSON" } - - override predicate hasTaintFlow(DataFlow::FunctionInput inp, DataFlow::FunctionOutput outp) { - inp = getAnInput() and outp = getOutput() - } - } -} - diff --git a/ql/src/semmle/go/frameworks/thirdpartlib/HTTP.qll b/ql/src/semmle/go/frameworks/thirdpartlib/HTTP.qll deleted file mode 100644 index fbc2a7a1..00000000 --- a/ql/src/semmle/go/frameworks/thirdpartlib/HTTP.qll +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Provides classes for working with HTTP-related concepts such as requests and responses. - */ - -import go - -module ThirdPartHttpLib { - /** - * Source from go-resultful - * Document: https://github.com/emicklei/go-restful - */ - class GoRestfulSource extends DataFlow::Node, UntrustedFlowSource::Range { - GoRestfulSource() { - exists( - Method meth, string name | - meth.hasQualifiedName("github.com/emicklei/go-restful", "Request", name) and - asExpr() = meth.getACall().asExpr() and - ( - name = "QueryParameters" or name = "QueryParameter" or - name = "BodyParamater" or name = "HeaderParameter" or - name = "PathParameter" or name = "PathParameters" - ) - ) - } - } -} - diff --git a/ql/src/semmle/go/frameworks/thirdpartlib/SQL.qll b/ql/src/semmle/go/frameworks/thirdpartlib/SQL.qll deleted file mode 100644 index 438646a5..00000000 --- a/ql/src/semmle/go/frameworks/thirdpartlib/SQL.qll +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Provides classes for working with SQL-related concepts such as queries. - */ - -import go - -module ThirdPartSQL { - - /** Sinks of github.com/jinzhu/gorm */ - class GormSink extends DataFlow::Node, SQL::QueryString::Range { - GormSink() { - exists( - Method meth, string name | - meth.hasQualifiedName("github.com/jinzhu/gorm", "DB", name) and - asExpr() = meth.getACall().getArgument(0).asExpr() and - ( - name = "Where" or name = "Raw" or name = "Order" or name = "Not" or name = "Or" or - name = "Select" or name = "Table" or name = "Group" or name = "Having" or name = "Joins" - ) - ) - } - } - - /** Sinks of github.com/jmoiron/sqlx */ - class SqlxSink extends DataFlow::Node, SQL::QueryString::Range { - SqlxSink() { - exists( - Method meth, string name, int n | - ( - meth.hasQualifiedName("github.com/jmoiron/sqlx", "DB", name) or - meth.hasQualifiedName("github.com/jmoiron/sqlx", "Tx", name) - ) and this = meth.getACall().getArgument(n) | - ( - (name = "Select" or name = "Get") and n = 1 - ) - or - ( - ( - name = "MustExec" or name = "Queryx" or - name = "NamedExec" or name = "NamedQuery" - ) - and n = 0 - ) - ) - } - } - -} From 3c4a1b90fe1625782ffcbf41601c5e42314308d2 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Fri, 24 Jul 2020 15:58:11 +0100 Subject: [PATCH 3/8] Add test for Go-restful --- ql/src/semmle/go/frameworks/HTTP.qll | 2 +- .../go/frameworks/HTTP/Gorestful/go.mod | 8 +++ .../HTTP/Gorestful/gorestful.expected | 12 +++++ .../go/frameworks/HTTP/Gorestful/gorestful.go | 16 ++++++ .../go/frameworks/HTTP/Gorestful/gorestful.ql | 7 +++ .../frameworks/HTTP/Gorestful/gorestful_v2.go | 16 ++++++ .../github.com/emicklei/go-restful/LICENSE | 22 ++++++++ .../github.com/emicklei/go-restful/stub.go | 54 +++++++++++++++++++ .../github.com/emicklei/go-restful/v3/stub.go | 54 +++++++++++++++++++ .../HTTP/Gorestful/vendor/modules.txt | 6 +++ 10 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/go.mod create mode 100644 ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful.expected create mode 100644 ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful.go create mode 100644 ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful.ql create mode 100644 ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful_v2.go create mode 100644 ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/vendor/github.com/emicklei/go-restful/LICENSE create mode 100644 ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/vendor/github.com/emicklei/go-restful/stub.go create mode 100644 ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/vendor/github.com/emicklei/go-restful/v3/stub.go create mode 100644 ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/vendor/modules.txt diff --git a/ql/src/semmle/go/frameworks/HTTP.qll b/ql/src/semmle/go/frameworks/HTTP.qll index 54c58809..73709f58 100644 --- a/ql/src/semmle/go/frameworks/HTTP.qll +++ b/ql/src/semmle/go/frameworks/HTTP.qll @@ -242,7 +242,7 @@ private module GoRestfulHttp { private class GoRestfulSourceMethod extends Method { GoRestfulSourceMethod() { this - .hasQualifiedName("github.com/emicklei/go-restful", "Request", + .hasQualifiedName(package("github.com/emicklei/go-restful", ""), "Request", ["QueryParameters", "QueryParameter", "BodyParameter", "HeaderParameter", "PathParameter", "PathParameters"]) } diff --git a/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/go.mod b/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/go.mod new file mode 100644 index 00000000..421f0669 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/go.mod @@ -0,0 +1,8 @@ +module gorestfultest + +go 1.14 + +require ( + github.com/emicklei/go-restful/v3 v3.2.0 + github.com/json-iterator/go v1.1.10 // indirect +) diff --git a/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful.expected b/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful.expected new file mode 100644 index 00000000..067ca0e5 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful.expected @@ -0,0 +1,12 @@ +| gorestful.go:9:15:9:47 | index expression | gorestful.go:9:15:9:44 | call to QueryParameters : slice type | gorestful.go:9:15:9:47 | index expression | This command depends on $@. | gorestful.go:9:15:9:44 | call to QueryParameters | a user-provided value | +| gorestful.go:10:15:10:43 | call to QueryParameter | gorestful.go:10:15:10:43 | call to QueryParameter | gorestful.go:10:15:10:43 | call to QueryParameter | This command depends on $@. | gorestful.go:10:15:10:43 | call to QueryParameter | a user-provided value | +| gorestful.go:12:15:12:17 | val | gorestful.go:11:12:11:39 | call to BodyParameter : tuple type | gorestful.go:12:15:12:17 | val | This command depends on $@. | gorestful.go:11:12:11:39 | call to BodyParameter | a user-provided value | +| gorestful.go:13:15:13:44 | call to HeaderParameter | gorestful.go:13:15:13:44 | call to HeaderParameter | gorestful.go:13:15:13:44 | call to HeaderParameter | This command depends on $@. | gorestful.go:13:15:13:44 | call to HeaderParameter | a user-provided value | +| gorestful.go:14:15:14:42 | call to PathParameter | gorestful.go:14:15:14:42 | call to PathParameter | gorestful.go:14:15:14:42 | call to PathParameter | This command depends on $@. | gorestful.go:14:15:14:42 | call to PathParameter | a user-provided value | +| gorestful.go:15:15:15:45 | index expression | gorestful.go:15:15:15:38 | call to PathParameters : map type | gorestful.go:15:15:15:45 | index expression | This command depends on $@. | gorestful.go:15:15:15:38 | call to PathParameters | a user-provided value | +| gorestful_v2.go:9:15:9:47 | index expression | gorestful_v2.go:9:15:9:44 | call to QueryParameters : slice type | gorestful_v2.go:9:15:9:47 | index expression | This command depends on $@. | gorestful_v2.go:9:15:9:44 | call to QueryParameters | a user-provided value | +| gorestful_v2.go:10:15:10:43 | call to QueryParameter | gorestful_v2.go:10:15:10:43 | call to QueryParameter | gorestful_v2.go:10:15:10:43 | call to QueryParameter | This command depends on $@. | gorestful_v2.go:10:15:10:43 | call to QueryParameter | a user-provided value | +| gorestful_v2.go:12:15:12:17 | val | gorestful_v2.go:11:12:11:39 | call to BodyParameter : tuple type | gorestful_v2.go:12:15:12:17 | val | This command depends on $@. | gorestful_v2.go:11:12:11:39 | call to BodyParameter | a user-provided value | +| gorestful_v2.go:13:15:13:44 | call to HeaderParameter | gorestful_v2.go:13:15:13:44 | call to HeaderParameter | gorestful_v2.go:13:15:13:44 | call to HeaderParameter | This command depends on $@. | gorestful_v2.go:13:15:13:44 | call to HeaderParameter | a user-provided value | +| gorestful_v2.go:14:15:14:42 | call to PathParameter | gorestful_v2.go:14:15:14:42 | call to PathParameter | gorestful_v2.go:14:15:14:42 | call to PathParameter | This command depends on $@. | gorestful_v2.go:14:15:14:42 | call to PathParameter | a user-provided value | +| gorestful_v2.go:15:15:15:45 | index expression | gorestful_v2.go:15:15:15:38 | call to PathParameters : map type | gorestful_v2.go:15:15:15:45 | index expression | This command depends on $@. | gorestful_v2.go:15:15:15:38 | call to PathParameters | a user-provided value | diff --git a/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful.go b/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful.go new file mode 100644 index 00000000..e0d0567d --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful.go @@ -0,0 +1,16 @@ +package gorestfultest + +import ( + restful "github.com/emicklei/go-restful/v3" + "os/exec" +) + +func requestHandler(request *restful.Request, response *restful.Response) { + exec.Command(request.QueryParameters("xyz")[0]) // BAD + exec.Command(request.QueryParameter("xyz")) // BAD + val, _ := request.BodyParameter("xyz") + exec.Command(val) // BAD + exec.Command(request.HeaderParameter("xyz")) // BAD + exec.Command(request.PathParameter("xyz")) // BAD + exec.Command(request.PathParameters()["xyz"]) // BAD +} diff --git a/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful.ql b/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful.ql new file mode 100644 index 00000000..4fa5f250 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful.ql @@ -0,0 +1,7 @@ +import go +import semmle.go.security.CommandInjection + +from CommandInjection::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink +where cfg.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "This command depends on $@.", source.getNode(), + "a user-provided value" diff --git a/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful_v2.go b/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful_v2.go new file mode 100644 index 00000000..4dd65f8d --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful_v2.go @@ -0,0 +1,16 @@ +package gorestfultest + +import ( + restful "github.com/emicklei/go-restful" + "os/exec" +) + +func requestHandlerV2(request *restful.Request, response *restful.Response) { + exec.Command(request.QueryParameters("xyz")[0]) // BAD + exec.Command(request.QueryParameter("xyz")) // BAD + val, _ := request.BodyParameter("xyz") + exec.Command(val) // BAD + exec.Command(request.HeaderParameter("xyz")) // BAD + exec.Command(request.PathParameter("xyz")) // BAD + exec.Command(request.PathParameters()["xyz"]) // BAD +} diff --git a/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/vendor/github.com/emicklei/go-restful/LICENSE b/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/vendor/github.com/emicklei/go-restful/LICENSE new file mode 100644 index 00000000..ece7ec61 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/vendor/github.com/emicklei/go-restful/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2012,2013 Ernest Micklei + +MIT License + +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. \ No newline at end of file diff --git a/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/vendor/github.com/emicklei/go-restful/stub.go b/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/vendor/github.com/emicklei/go-restful/stub.go new file mode 100644 index 00000000..7cde3962 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/vendor/github.com/emicklei/go-restful/stub.go @@ -0,0 +1,54 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/emicklei/go-restful/v3, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/emicklei/go-restful/v3 (exports: Request; functions: ) + +// Package gorestfulstub is a stub of github.com/emicklei/go-restful, generated by depstubber. +package gorestfulstub + +import ( + http "net/http" +) + +type Request struct { + Request *http.Request +} + +func (_ Request) Attribute(_ string) interface{} { + return nil +} + +func (_ Request) SelectedRoutePath() string { + return "" +} + +func (_ *Request) BodyParameter(_ string) (string, error) { + return "", nil +} + +func (_ *Request) HeaderParameter(_ string) string { + return "" +} + +func (_ *Request) PathParameter(_ string) string { + return "" +} + +func (_ *Request) PathParameters() map[string]string { + return nil +} + +func (_ *Request) QueryParameter(_ string) string { + return "" +} + +func (_ *Request) QueryParameters(_ string) []string { + return nil +} + +func (_ *Request) ReadEntity(_ interface{}) error { + return nil +} + +func (_ *Request) SetAttribute(_ string, _ interface{}) {} diff --git a/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/vendor/github.com/emicklei/go-restful/v3/stub.go b/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/vendor/github.com/emicklei/go-restful/v3/stub.go new file mode 100644 index 00000000..71d25f09 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/vendor/github.com/emicklei/go-restful/v3/stub.go @@ -0,0 +1,54 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/emicklei/go-restful/v3, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/emicklei/go-restful/v3 (exports: Request; functions: ) + +// Package gorestfulstub is a stub of github.com/emicklei/go-restful/v3, generated by depstubber. +package gorestfulstub + +import ( + http "net/http" +) + +type Request struct { + Request *http.Request +} + +func (_ Request) Attribute(_ string) interface{} { + return nil +} + +func (_ Request) SelectedRoutePath() string { + return "" +} + +func (_ *Request) BodyParameter(_ string) (string, error) { + return "", nil +} + +func (_ *Request) HeaderParameter(_ string) string { + return "" +} + +func (_ *Request) PathParameter(_ string) string { + return "" +} + +func (_ *Request) PathParameters() map[string]string { + return nil +} + +func (_ *Request) QueryParameter(_ string) string { + return "" +} + +func (_ *Request) QueryParameters(_ string) []string { + return nil +} + +func (_ *Request) ReadEntity(_ interface{}) error { + return nil +} + +func (_ *Request) SetAttribute(_ string, _ interface{}) {} diff --git a/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/vendor/modules.txt b/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/vendor/modules.txt new file mode 100644 index 00000000..ed7043d9 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/vendor/modules.txt @@ -0,0 +1,6 @@ +# github.com/emicklei/go-restful/v3 v3.2.0 +## explicit +github.com/emicklei/go-restful/v3 +# github.com/json-iterator/go v1.1.10 +## explicit +github.com/json-iterator/go From a813607a767e6b3b18baae88dcb824798e35624e Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Mon, 27 Jul 2020 11:05:20 +0100 Subject: [PATCH 4/8] go-restful model: Add support for ReadEntity method --- ql/src/semmle/go/frameworks/HTTP.qll | 15 +++++++++++ .../HTTP/Gorestful/gorestful.expected | 26 ++++++++++--------- .../go/frameworks/HTTP/Gorestful/gorestful.go | 7 +++++ .../frameworks/HTTP/Gorestful/gorestful_v2.go | 7 +++++ 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/ql/src/semmle/go/frameworks/HTTP.qll b/ql/src/semmle/go/frameworks/HTTP.qll index 73709f58..0b1175f0 100644 --- a/ql/src/semmle/go/frameworks/HTTP.qll +++ b/ql/src/semmle/go/frameworks/HTTP.qll @@ -254,4 +254,19 @@ private module GoRestfulHttp { private class GoRestfulSource extends UntrustedFlowSource::Range { GoRestfulSource() { this = any(GoRestfulSourceMethod g).getACall() } } + + /** + * A model of go-restful's `Request.ReadEntity` method as a source of user-controlled data. + */ + private class GoRestfulReadEntitySource extends UntrustedFlowSource::Range { + GoRestfulReadEntitySource() { + exists(DataFlow::MethodCallNode call | + call + .getTarget() + .hasQualifiedName(package("github.com/emicklei/go-restful", ""), "Request", "ReadEntity") + | + this = any(FunctionOutput output | output.isParameter(0)).getExitNode(call) + ) + } + } } diff --git a/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful.expected b/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful.expected index 067ca0e5..ee3137d7 100644 --- a/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful.expected +++ b/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful.expected @@ -1,12 +1,14 @@ -| gorestful.go:9:15:9:47 | index expression | gorestful.go:9:15:9:44 | call to QueryParameters : slice type | gorestful.go:9:15:9:47 | index expression | This command depends on $@. | gorestful.go:9:15:9:44 | call to QueryParameters | a user-provided value | -| gorestful.go:10:15:10:43 | call to QueryParameter | gorestful.go:10:15:10:43 | call to QueryParameter | gorestful.go:10:15:10:43 | call to QueryParameter | This command depends on $@. | gorestful.go:10:15:10:43 | call to QueryParameter | a user-provided value | -| gorestful.go:12:15:12:17 | val | gorestful.go:11:12:11:39 | call to BodyParameter : tuple type | gorestful.go:12:15:12:17 | val | This command depends on $@. | gorestful.go:11:12:11:39 | call to BodyParameter | a user-provided value | -| gorestful.go:13:15:13:44 | call to HeaderParameter | gorestful.go:13:15:13:44 | call to HeaderParameter | gorestful.go:13:15:13:44 | call to HeaderParameter | This command depends on $@. | gorestful.go:13:15:13:44 | call to HeaderParameter | a user-provided value | -| gorestful.go:14:15:14:42 | call to PathParameter | gorestful.go:14:15:14:42 | call to PathParameter | gorestful.go:14:15:14:42 | call to PathParameter | This command depends on $@. | gorestful.go:14:15:14:42 | call to PathParameter | a user-provided value | -| gorestful.go:15:15:15:45 | index expression | gorestful.go:15:15:15:38 | call to PathParameters : map type | gorestful.go:15:15:15:45 | index expression | This command depends on $@. | gorestful.go:15:15:15:38 | call to PathParameters | a user-provided value | -| gorestful_v2.go:9:15:9:47 | index expression | gorestful_v2.go:9:15:9:44 | call to QueryParameters : slice type | gorestful_v2.go:9:15:9:47 | index expression | This command depends on $@. | gorestful_v2.go:9:15:9:44 | call to QueryParameters | a user-provided value | -| gorestful_v2.go:10:15:10:43 | call to QueryParameter | gorestful_v2.go:10:15:10:43 | call to QueryParameter | gorestful_v2.go:10:15:10:43 | call to QueryParameter | This command depends on $@. | gorestful_v2.go:10:15:10:43 | call to QueryParameter | a user-provided value | -| gorestful_v2.go:12:15:12:17 | val | gorestful_v2.go:11:12:11:39 | call to BodyParameter : tuple type | gorestful_v2.go:12:15:12:17 | val | This command depends on $@. | gorestful_v2.go:11:12:11:39 | call to BodyParameter | a user-provided value | -| gorestful_v2.go:13:15:13:44 | call to HeaderParameter | gorestful_v2.go:13:15:13:44 | call to HeaderParameter | gorestful_v2.go:13:15:13:44 | call to HeaderParameter | This command depends on $@. | gorestful_v2.go:13:15:13:44 | call to HeaderParameter | a user-provided value | -| gorestful_v2.go:14:15:14:42 | call to PathParameter | gorestful_v2.go:14:15:14:42 | call to PathParameter | gorestful_v2.go:14:15:14:42 | call to PathParameter | This command depends on $@. | gorestful_v2.go:14:15:14:42 | call to PathParameter | a user-provided value | -| gorestful_v2.go:15:15:15:45 | index expression | gorestful_v2.go:15:15:15:38 | call to PathParameters : map type | gorestful_v2.go:15:15:15:45 | index expression | This command depends on $@. | gorestful_v2.go:15:15:15:38 | call to PathParameters | a user-provided value | +| gorestful.go:13:15:13:47 | index expression | gorestful.go:13:15:13:44 | call to QueryParameters : slice type | gorestful.go:13:15:13:47 | index expression | This command depends on $@. | gorestful.go:13:15:13:44 | call to QueryParameters | a user-provided value | +| gorestful.go:14:15:14:43 | call to QueryParameter | gorestful.go:14:15:14:43 | call to QueryParameter | gorestful.go:14:15:14:43 | call to QueryParameter | This command depends on $@. | gorestful.go:14:15:14:43 | call to QueryParameter | a user-provided value | +| gorestful.go:16:15:16:17 | val | gorestful.go:15:12:15:39 | call to BodyParameter : tuple type | gorestful.go:16:15:16:17 | val | This command depends on $@. | gorestful.go:15:12:15:39 | call to BodyParameter | a user-provided value | +| gorestful.go:17:15:17:44 | call to HeaderParameter | gorestful.go:17:15:17:44 | call to HeaderParameter | gorestful.go:17:15:17:44 | call to HeaderParameter | This command depends on $@. | gorestful.go:17:15:17:44 | call to HeaderParameter | a user-provided value | +| gorestful.go:18:15:18:42 | call to PathParameter | gorestful.go:18:15:18:42 | call to PathParameter | gorestful.go:18:15:18:42 | call to PathParameter | This command depends on $@. | gorestful.go:18:15:18:42 | call to PathParameter | a user-provided value | +| gorestful.go:19:15:19:45 | index expression | gorestful.go:19:15:19:38 | call to PathParameters : map type | gorestful.go:19:15:19:45 | index expression | This command depends on $@. | gorestful.go:19:15:19:38 | call to PathParameters | a user-provided value | +| gorestful.go:22:15:22:21 | selection of cmd | gorestful.go:21:21:21:24 | &... : pointer type | gorestful.go:22:15:22:21 | selection of cmd | This command depends on $@. | gorestful.go:21:21:21:24 | &... | a user-provided value | +| gorestful_v2.go:13:15:13:47 | index expression | gorestful_v2.go:13:15:13:44 | call to QueryParameters : slice type | gorestful_v2.go:13:15:13:47 | index expression | This command depends on $@. | gorestful_v2.go:13:15:13:44 | call to QueryParameters | a user-provided value | +| gorestful_v2.go:14:15:14:43 | call to QueryParameter | gorestful_v2.go:14:15:14:43 | call to QueryParameter | gorestful_v2.go:14:15:14:43 | call to QueryParameter | This command depends on $@. | gorestful_v2.go:14:15:14:43 | call to QueryParameter | a user-provided value | +| gorestful_v2.go:16:15:16:17 | val | gorestful_v2.go:15:12:15:39 | call to BodyParameter : tuple type | gorestful_v2.go:16:15:16:17 | val | This command depends on $@. | gorestful_v2.go:15:12:15:39 | call to BodyParameter | a user-provided value | +| gorestful_v2.go:17:15:17:44 | call to HeaderParameter | gorestful_v2.go:17:15:17:44 | call to HeaderParameter | gorestful_v2.go:17:15:17:44 | call to HeaderParameter | This command depends on $@. | gorestful_v2.go:17:15:17:44 | call to HeaderParameter | a user-provided value | +| gorestful_v2.go:18:15:18:42 | call to PathParameter | gorestful_v2.go:18:15:18:42 | call to PathParameter | gorestful_v2.go:18:15:18:42 | call to PathParameter | This command depends on $@. | gorestful_v2.go:18:15:18:42 | call to PathParameter | a user-provided value | +| gorestful_v2.go:19:15:19:45 | index expression | gorestful_v2.go:19:15:19:38 | call to PathParameters : map type | gorestful_v2.go:19:15:19:45 | index expression | This command depends on $@. | gorestful_v2.go:19:15:19:38 | call to PathParameters | a user-provided value | +| gorestful_v2.go:22:15:22:21 | selection of cmd | gorestful_v2.go:21:21:21:24 | &... : pointer type | gorestful_v2.go:22:15:22:21 | selection of cmd | This command depends on $@. | gorestful_v2.go:21:21:21:24 | &... | a user-provided value | diff --git a/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful.go b/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful.go index e0d0567d..fa2a1959 100644 --- a/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful.go +++ b/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful.go @@ -5,6 +5,10 @@ import ( "os/exec" ) +type myObject struct { + cmd string +} + func requestHandler(request *restful.Request, response *restful.Response) { exec.Command(request.QueryParameters("xyz")[0]) // BAD exec.Command(request.QueryParameter("xyz")) // BAD @@ -13,4 +17,7 @@ func requestHandler(request *restful.Request, response *restful.Response) { exec.Command(request.HeaderParameter("xyz")) // BAD exec.Command(request.PathParameter("xyz")) // BAD exec.Command(request.PathParameters()["xyz"]) // BAD + obj := myObject{} + request.ReadEntity(&obj) + exec.Command(obj.cmd) // BAD } diff --git a/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful_v2.go b/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful_v2.go index 4dd65f8d..884c037b 100644 --- a/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful_v2.go +++ b/ql/test/library-tests/semmle/go/frameworks/HTTP/Gorestful/gorestful_v2.go @@ -5,6 +5,10 @@ import ( "os/exec" ) +type myObjectV2 struct { + cmd string +} + func requestHandlerV2(request *restful.Request, response *restful.Response) { exec.Command(request.QueryParameters("xyz")[0]) // BAD exec.Command(request.QueryParameter("xyz")) // BAD @@ -13,4 +17,7 @@ func requestHandlerV2(request *restful.Request, response *restful.Response) { exec.Command(request.HeaderParameter("xyz")) // BAD exec.Command(request.PathParameter("xyz")) // BAD exec.Command(request.PathParameters()["xyz"]) // BAD + obj := myObjectV2{} + request.ReadEntity(&obj) + exec.Command(obj.cmd) // BAD } From f5caf7e9e21fced9a5403f68efcc082924e22bcf Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Mon, 27 Jul 2020 11:47:45 +0100 Subject: [PATCH 5/8] Add test for Gorm --- .../semmle/go/frameworks/SQL/Gorm/go.mod | 7 + .../go/frameworks/SQL/Gorm/gorm.expected | 9 + .../semmle/go/frameworks/SQL/Gorm/gorm.go | 25 + .../semmle/go/frameworks/SQL/Gorm/gorm.ql | 4 + .../vendor/github.com/jinzhu/gorm/stub.go | 698 ++++++++++++++++++ .../go/frameworks/SQL/Gorm/vendor/modules.txt | 3 + 6 files changed, 746 insertions(+) create mode 100644 ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/go.mod create mode 100644 ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.expected create mode 100644 ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.go create mode 100644 ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.ql create mode 100644 ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/vendor/github.com/jinzhu/gorm/stub.go create mode 100644 ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/vendor/modules.txt diff --git a/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/go.mod b/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/go.mod new file mode 100644 index 00000000..fad4c47a --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/go.mod @@ -0,0 +1,7 @@ +module gormtest + +go 1.14 + +require ( + github.com/jinzhu/gorm v1.9.15 +) diff --git a/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.expected b/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.expected new file mode 100644 index 00000000..55c7a9ff --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.expected @@ -0,0 +1,9 @@ +| gorm.go:15:11:15:19 | untrusted | +| gorm.go:16:9:16:17 | untrusted | +| gorm.go:17:11:17:19 | untrusted | +| gorm.go:18:8:18:16 | untrusted | +| gorm.go:19:12:19:20 | untrusted | +| gorm.go:20:11:20:19 | untrusted | +| gorm.go:21:11:21:19 | untrusted | +| gorm.go:22:12:22:20 | untrusted | +| gorm.go:23:11:23:19 | untrusted | diff --git a/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.go b/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.go new file mode 100644 index 00000000..f6389e9d --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.go @@ -0,0 +1,25 @@ +package gormtest + +import ( + "github.com/jinzhu/gorm" +) + +func getUntrustedString() string { + return "trouble" +} + +func main() { + + db := gorm.DB{} + untrusted := getUntrustedString() + db.Where(untrusted) + db.Not(untrusted) + db.Order(untrusted) + db.Or(untrusted) + db.Select(untrusted) + db.Table(untrusted) + db.Group(untrusted) + db.Having(untrusted) + db.Joins(untrusted) + +} diff --git a/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.ql b/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.ql new file mode 100644 index 00000000..7b56fd97 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/gorm.ql @@ -0,0 +1,4 @@ +import go + +from SQL::QueryString qs +select qs diff --git a/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/vendor/github.com/jinzhu/gorm/stub.go b/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/vendor/github.com/jinzhu/gorm/stub.go new file mode 100644 index 00000000..04def438 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/vendor/github.com/jinzhu/gorm/stub.go @@ -0,0 +1,698 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/jinzhu/gorm, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/jinzhu/gorm (exports: DB; functions: ) + +// Package gorm is a stub of github.com/jinzhu/gorm, generated by depstubber. +package gorm + +import ( + context "context" + sql "database/sql" + reflect "reflect" + sync "sync" + time "time" +) + +type Association struct { + Error error +} + +func (_ *Association) Append(_ ...interface{}) *Association { + return nil +} + +func (_ *Association) Clear() *Association { + return nil +} + +func (_ *Association) Count() int { + return 0 +} + +func (_ *Association) Delete(_ ...interface{}) *Association { + return nil +} + +func (_ *Association) Find(_ interface{}) *Association { + return nil +} + +func (_ *Association) Replace(_ ...interface{}) *Association { + return nil +} + +type Callback struct{} + +func (_ *Callback) Create() *CallbackProcessor { + return nil +} + +func (_ *Callback) Delete() *CallbackProcessor { + return nil +} + +func (_ *Callback) Query() *CallbackProcessor { + return nil +} + +func (_ *Callback) RowQuery() *CallbackProcessor { + return nil +} + +func (_ *Callback) Update() *CallbackProcessor { + return nil +} + +type CallbackProcessor struct{} + +func (_ *CallbackProcessor) After(_ string) *CallbackProcessor { + return nil +} + +func (_ *CallbackProcessor) Before(_ string) *CallbackProcessor { + return nil +} + +func (_ *CallbackProcessor) Get(_ string) func(*Scope) { + return nil +} + +func (_ *CallbackProcessor) Register(_ string, _ func(*Scope)) {} + +func (_ *CallbackProcessor) Remove(_ string) {} + +func (_ *CallbackProcessor) Replace(_ string, _ func(*Scope)) {} + +type DB struct { + RWMutex sync.RWMutex + Value interface{} + Error error + RowsAffected int64 +} + +func (_ *DB) AddError(_ error) error { + return nil +} + +func (_ *DB) AddForeignKey(_ string, _ string, _ string, _ string) *DB { + return nil +} + +func (_ *DB) AddIndex(_ string, _ ...string) *DB { + return nil +} + +func (_ *DB) AddUniqueIndex(_ string, _ ...string) *DB { + return nil +} + +func (_ *DB) Assign(_ ...interface{}) *DB { + return nil +} + +func (_ *DB) Association(_ string) *Association { + return nil +} + +func (_ *DB) Attrs(_ ...interface{}) *DB { + return nil +} + +func (_ *DB) AutoMigrate(_ ...interface{}) *DB { + return nil +} + +func (_ *DB) Begin() *DB { + return nil +} + +func (_ *DB) BeginTx(_ context.Context, _ *sql.TxOptions) *DB { + return nil +} + +func (_ *DB) BlockGlobalUpdate(_ bool) *DB { + return nil +} + +func (_ *DB) Callback() *Callback { + return nil +} + +func (_ *DB) Close() error { + return nil +} + +func (_ *DB) Commit() *DB { + return nil +} + +func (_ *DB) CommonDB() SQLCommon { + return nil +} + +func (_ *DB) Count(_ interface{}) *DB { + return nil +} + +func (_ *DB) Create(_ interface{}) *DB { + return nil +} + +func (_ *DB) CreateTable(_ ...interface{}) *DB { + return nil +} + +func (_ *DB) DB() *sql.DB { + return nil +} + +func (_ *DB) Debug() *DB { + return nil +} + +func (_ *DB) Delete(_ interface{}, _ ...interface{}) *DB { + return nil +} + +func (_ *DB) Dialect() Dialect { + return nil +} + +func (_ *DB) DropColumn(_ string) *DB { + return nil +} + +func (_ *DB) DropTable(_ ...interface{}) *DB { + return nil +} + +func (_ *DB) DropTableIfExists(_ ...interface{}) *DB { + return nil +} + +func (_ *DB) Exec(_ string, _ ...interface{}) *DB { + return nil +} + +func (_ *DB) Find(_ interface{}, _ ...interface{}) *DB { + return nil +} + +func (_ *DB) First(_ interface{}, _ ...interface{}) *DB { + return nil +} + +func (_ *DB) FirstOrCreate(_ interface{}, _ ...interface{}) *DB { + return nil +} + +func (_ *DB) FirstOrInit(_ interface{}, _ ...interface{}) *DB { + return nil +} + +func (_ *DB) Get(_ string) (interface{}, bool) { + return nil, false +} + +func (_ *DB) GetErrors() []error { + return nil +} + +func (_ *DB) Group(_ string) *DB { + return nil +} + +func (_ *DB) HasBlockGlobalUpdate() bool { + return false +} + +func (_ *DB) HasTable(_ interface{}) bool { + return false +} + +func (_ *DB) Having(_ interface{}, _ ...interface{}) *DB { + return nil +} + +func (_ *DB) InstantSet(_ string, _ interface{}) *DB { + return nil +} + +func (_ *DB) Joins(_ string, _ ...interface{}) *DB { + return nil +} + +func (_ *DB) Last(_ interface{}, _ ...interface{}) *DB { + return nil +} + +func (_ *DB) Limit(_ interface{}) *DB { + return nil +} + +func (_ *DB) Lock() {} + +func (_ *DB) LogMode(_ bool) *DB { + return nil +} + +func (_ *DB) Model(_ interface{}) *DB { + return nil +} + +func (_ *DB) ModifyColumn(_ string, _ string) *DB { + return nil +} + +func (_ *DB) New() *DB { + return nil +} + +func (_ *DB) NewRecord(_ interface{}) bool { + return false +} + +func (_ *DB) NewScope(_ interface{}) *Scope { + return nil +} + +func (_ *DB) Not(_ interface{}, _ ...interface{}) *DB { + return nil +} + +func (_ *DB) Offset(_ interface{}) *DB { + return nil +} + +func (_ *DB) Omit(_ ...string) *DB { + return nil +} + +func (_ *DB) Or(_ interface{}, _ ...interface{}) *DB { + return nil +} + +func (_ *DB) Order(_ interface{}, _ ...bool) *DB { + return nil +} + +func (_ *DB) Pluck(_ string, _ interface{}) *DB { + return nil +} + +func (_ *DB) Preload(_ string, _ ...interface{}) *DB { + return nil +} + +func (_ *DB) Preloads(_ interface{}) *DB { + return nil +} + +func (_ *DB) QueryExpr() *SqlExpr { + return nil +} + +func (_ *DB) RLock() {} + +func (_ *DB) RLocker() sync.Locker { + return nil +} + +func (_ *DB) RUnlock() {} + +func (_ *DB) Raw(_ string, _ ...interface{}) *DB { + return nil +} + +func (_ *DB) RecordNotFound() bool { + return false +} + +func (_ *DB) Related(_ interface{}, _ ...string) *DB { + return nil +} + +func (_ *DB) RemoveForeignKey(_ string, _ string) *DB { + return nil +} + +func (_ *DB) RemoveIndex(_ string) *DB { + return nil +} + +func (_ *DB) Rollback() *DB { + return nil +} + +func (_ *DB) RollbackUnlessCommitted() *DB { + return nil +} + +func (_ *DB) Row() *sql.Row { + return nil +} + +func (_ *DB) Rows() (*sql.Rows, error) { + return nil, nil +} + +func (_ *DB) Save(_ interface{}) *DB { + return nil +} + +func (_ *DB) Scan(_ interface{}) *DB { + return nil +} + +func (_ *DB) ScanRows(_ *sql.Rows, _ interface{}) error { + return nil +} + +func (_ *DB) Scopes(_ ...func(*DB) *DB) *DB { + return nil +} + +func (_ *DB) Select(_ interface{}, _ ...interface{}) *DB { + return nil +} + +func (_ *DB) Set(_ string, _ interface{}) *DB { + return nil +} + +func (_ *DB) SetJoinTableHandler(_ interface{}, _ string, _ JoinTableHandlerInterface) {} + +func (_ *DB) SetLogger(_ interface{}) {} + +func (_ *DB) SetNowFuncOverride(_ func() time.Time) *DB { + return nil +} + +func (_ *DB) SingularTable(_ bool) {} + +func (_ *DB) SubQuery() *SqlExpr { + return nil +} + +func (_ *DB) Table(_ string) *DB { + return nil +} + +func (_ *DB) Take(_ interface{}, _ ...interface{}) *DB { + return nil +} + +func (_ *DB) Transaction(_ func(*DB) error) error { + return nil +} + +func (_ *DB) Unlock() {} + +func (_ *DB) Unscoped() *DB { + return nil +} + +func (_ *DB) Update(_ ...interface{}) *DB { + return nil +} + +func (_ *DB) UpdateColumn(_ ...interface{}) *DB { + return nil +} + +func (_ *DB) UpdateColumns(_ interface{}) *DB { + return nil +} + +func (_ *DB) Updates(_ interface{}, _ ...bool) *DB { + return nil +} + +func (_ *DB) Where(_ interface{}, _ ...interface{}) *DB { + return nil +} + +type Dialect interface { + BindVar(_ int) string + BuildKeyName(_ string, _ string, _ ...string) string + CurrentDatabase() string + DataTypeOf(_ *StructField) string + DefaultValueStr() string + GetName() string + HasColumn(_ string, _ string) bool + HasForeignKey(_ string, _ string) bool + HasIndex(_ string, _ string) bool + HasTable(_ string) bool + LastInsertIDOutputInterstitial(_ string, _ string, _ []string) string + LastInsertIDReturningSuffix(_ string, _ string) string + LimitAndOffsetSQL(_ interface{}, _ interface{}) (string, error) + ModifyColumn(_ string, _ string, _ string) error + NormalizeIndexAndColumn(_ string, _ string) (string, string) + Quote(_ string) string + RemoveIndex(_ string, _ string) error + SelectFromDummyTable() string + SetDB(_ SQLCommon) +} + +type Field struct { + StructField *StructField + IsBlank bool + Field reflect.Value +} + +func (_ Field) TagSettingsDelete(_ string) {} + +func (_ Field) TagSettingsGet(_ string) (string, bool) { + return "", false +} + +func (_ Field) TagSettingsSet(_ string, _ string) {} + +func (_ *Field) Set(_ interface{}) error { + return nil +} + +type JoinTableForeignKey struct { + DBName string + AssociationDBName string +} + +type JoinTableHandlerInterface interface { + Add(_ JoinTableHandlerInterface, _ *DB, _ interface{}, _ interface{}) error + Delete(_ JoinTableHandlerInterface, _ *DB, _ ...interface{}) error + DestinationForeignKeys() []JoinTableForeignKey + JoinWith(_ JoinTableHandlerInterface, _ *DB, _ interface{}) *DB + Setup(_ *Relationship, _ string, _ reflect.Type, _ reflect.Type) + SourceForeignKeys() []JoinTableForeignKey + Table(_ *DB) string +} + +type ModelStruct struct { + PrimaryFields []*StructField + StructFields []*StructField + ModelType reflect.Type +} + +func (_ *ModelStruct) TableName(_ *DB) string { + return "" +} + +type Relationship struct { + Kind string + PolymorphicType string + PolymorphicDBName string + PolymorphicValue string + ForeignFieldNames []string + ForeignDBNames []string + AssociationForeignFieldNames []string + AssociationForeignDBNames []string + JoinTableHandler JoinTableHandlerInterface +} + +type SQLCommon interface { + Exec(_ string, _ ...interface{}) (sql.Result, error) + Prepare(_ string) (*sql.Stmt, error) + Query(_ string, _ ...interface{}) (*sql.Rows, error) + QueryRow(_ string, _ ...interface{}) *sql.Row +} + +type Scope struct { + Search interface{} + Value interface{} + SQL string + SQLVars []interface{} +} + +func (_ *Scope) AddToVars(_ interface{}) string { + return "" +} + +func (_ *Scope) Begin() *Scope { + return nil +} + +func (_ *Scope) CallMethod(_ string) {} + +func (_ *Scope) CombinedConditionSql() string { + return "" +} + +func (_ *Scope) CommitOrRollback() *Scope { + return nil +} + +func (_ *Scope) DB() *DB { + return nil +} + +func (_ *Scope) Dialect() Dialect { + return nil +} + +func (_ *Scope) Err(_ error) error { + return nil +} + +func (_ *Scope) Exec() *Scope { + return nil +} + +func (_ *Scope) FieldByName(_ string) (*Field, bool) { + return nil, false +} + +func (_ *Scope) Fields() []*Field { + return nil +} + +func (_ *Scope) Get(_ string) (interface{}, bool) { + return nil, false +} + +func (_ *Scope) GetModelStruct() *ModelStruct { + return nil +} + +func (_ *Scope) GetStructFields() []*StructField { + return nil +} + +func (_ *Scope) HasColumn(_ string) bool { + return false +} + +func (_ *Scope) HasError() bool { + return false +} + +func (_ *Scope) IndirectValue() reflect.Value { + return reflect.Value{} +} + +func (_ *Scope) InstanceGet(_ string) (interface{}, bool) { + return nil, false +} + +func (_ *Scope) InstanceID() string { + return "" +} + +func (_ *Scope) InstanceSet(_ string, _ interface{}) *Scope { + return nil +} + +func (_ *Scope) Log(_ ...interface{}) {} + +func (_ *Scope) New(_ interface{}) *Scope { + return nil +} + +func (_ *Scope) NewDB() *DB { + return nil +} + +func (_ *Scope) OmitAttrs() []string { + return nil +} + +func (_ *Scope) PrimaryField() *Field { + return nil +} + +func (_ *Scope) PrimaryFields() []*Field { + return nil +} + +func (_ *Scope) PrimaryKey() string { + return "" +} + +func (_ *Scope) PrimaryKeyValue() interface{} { + return nil +} + +func (_ *Scope) PrimaryKeyZero() bool { + return false +} + +func (_ *Scope) Quote(_ string) string { + return "" +} + +func (_ *Scope) QuotedTableName() string { + return "" +} + +func (_ *Scope) Raw(_ string) *Scope { + return nil +} + +func (_ *Scope) SQLDB() SQLCommon { + return nil +} + +func (_ *Scope) SelectAttrs() []string { + return nil +} + +func (_ *Scope) Set(_ string, _ interface{}) *Scope { + return nil +} + +func (_ *Scope) SetColumn(_ interface{}, _ interface{}) error { + return nil +} + +func (_ *Scope) SkipLeft() {} + +func (_ *Scope) TableName() string { + return "" +} + +type SqlExpr struct{} + +type StructField struct { + DBName string + Name string + Names []string + IsPrimaryKey bool + IsNormal bool + IsIgnored bool + IsScanner bool + HasDefaultValue bool + Tag reflect.StructTag + TagSettings map[string]string + Struct reflect.StructField + IsForeignKey bool + Relationship *Relationship +} + +func (_ *StructField) TagSettingsDelete(_ string) {} + +func (_ *StructField) TagSettingsGet(_ string) (string, bool) { + return "", false +} + +func (_ *StructField) TagSettingsSet(_ string, _ string) {} diff --git a/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/vendor/modules.txt b/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/vendor/modules.txt new file mode 100644 index 00000000..13d6ffb9 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/SQL/Gorm/vendor/modules.txt @@ -0,0 +1,3 @@ +# github.com/jinzhu/gorm v1.9.15 +## explicit +github.com/jinzhu/gorm From e19f47634129e48a468fa87a794e538eabc15a85 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Mon, 27 Jul 2020 12:08:55 +0100 Subject: [PATCH 6/8] Add test for Sqlx --- .../semmle/go/frameworks/SQL/Sqlx/go.mod | 7 + .../go/frameworks/SQL/Sqlx/sqlx.expected | 12 + .../semmle/go/frameworks/SQL/Sqlx/sqlx.go | 30 + .../semmle/go/frameworks/SQL/Sqlx/sqlx.ql | 4 + .../vendor/github.com/jmoiron/sqlx/stub.go | 581 ++++++++++++++++++ .../go/frameworks/SQL/Sqlx/vendor/modules.txt | 3 + 6 files changed, 637 insertions(+) create mode 100644 ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/go.mod create mode 100644 ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.expected create mode 100644 ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.go create mode 100644 ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.ql create mode 100644 ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/vendor/github.com/jmoiron/sqlx/stub.go create mode 100644 ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/vendor/modules.txt diff --git a/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/go.mod b/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/go.mod new file mode 100644 index 00000000..991ce37d --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/go.mod @@ -0,0 +1,7 @@ +module gormtest + +go 1.14 + +require ( + github.com/jmoiron/sqlx v1.2.0 +) diff --git a/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.expected b/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.expected new file mode 100644 index 00000000..0540a78f --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.expected @@ -0,0 +1,12 @@ +| sqlx.go:15:17:15:25 | untrusted | +| sqlx.go:16:14:16:22 | untrusted | +| sqlx.go:17:14:17:22 | untrusted | +| sqlx.go:18:12:18:20 | untrusted | +| sqlx.go:19:15:19:23 | untrusted | +| sqlx.go:20:16:20:24 | untrusted | +| sqlx.go:23:17:23:25 | untrusted | +| sqlx.go:24:14:24:22 | untrusted | +| sqlx.go:25:14:25:22 | untrusted | +| sqlx.go:26:12:26:20 | untrusted | +| sqlx.go:27:15:27:23 | untrusted | +| sqlx.go:28:16:28:24 | untrusted | diff --git a/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.go b/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.go new file mode 100644 index 00000000..edc29c4d --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.go @@ -0,0 +1,30 @@ +package sqlxtest + +import ( + "github.com/jmoiron/sqlx" +) + +func getUntrustedString() string { + return "trouble" +} + +func main() { + + db := sqlx.DB{} + untrusted := getUntrustedString() + db.Select(nil, untrusted) + db.Get(nil, untrusted) + db.MustExec(untrusted) + db.Queryx(untrusted) + db.NamedExec(untrusted, nil) + db.NamedQuery(untrusted, nil) + + tx := sqlx.Tx{} + tx.Select(nil, untrusted) + tx.Get(nil, untrusted) + tx.MustExec(untrusted) + tx.Queryx(untrusted) + tx.NamedExec(untrusted, nil) + tx.NamedQuery(untrusted, nil) + +} diff --git a/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.ql b/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.ql new file mode 100644 index 00000000..7b56fd97 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/sqlx.ql @@ -0,0 +1,4 @@ +import go + +from SQL::QueryString qs +select qs diff --git a/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/vendor/github.com/jmoiron/sqlx/stub.go b/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/vendor/github.com/jmoiron/sqlx/stub.go new file mode 100644 index 00000000..e4a15105 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/vendor/github.com/jmoiron/sqlx/stub.go @@ -0,0 +1,581 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/jmoiron/sqlx, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/jmoiron/sqlx (exports: DB,Tx; functions: ) + +// Package sqlx is a stub of github.com/jmoiron/sqlx, generated by depstubber. +package sqlx + +import ( + context "context" + sql "database/sql" + driver "database/sql/driver" + time "time" +) + +type DB struct { + DB *sql.DB + Mapper interface{} +} + +func (_ DB) Begin() (*sql.Tx, error) { + return nil, nil +} + +func (_ DB) BeginTx(_ context.Context, _ *sql.TxOptions) (*sql.Tx, error) { + return nil, nil +} + +func (_ DB) Close() error { + return nil +} + +func (_ DB) Conn(_ context.Context) (*sql.Conn, error) { + return nil, nil +} + +func (_ DB) Driver() driver.Driver { + return nil +} + +func (_ DB) Exec(_ string, _ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ DB) ExecContext(_ context.Context, _ string, _ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ DB) Ping() error { + return nil +} + +func (_ DB) PingContext(_ context.Context) error { + return nil +} + +func (_ DB) Prepare(_ string) (*sql.Stmt, error) { + return nil, nil +} + +func (_ DB) PrepareContext(_ context.Context, _ string) (*sql.Stmt, error) { + return nil, nil +} + +func (_ DB) Query(_ string, _ ...interface{}) (*sql.Rows, error) { + return nil, nil +} + +func (_ DB) QueryContext(_ context.Context, _ string, _ ...interface{}) (*sql.Rows, error) { + return nil, nil +} + +func (_ DB) QueryRow(_ string, _ ...interface{}) *sql.Row { + return nil +} + +func (_ DB) QueryRowContext(_ context.Context, _ string, _ ...interface{}) *sql.Row { + return nil +} + +func (_ DB) SetConnMaxLifetime(_ time.Duration) {} + +func (_ DB) SetMaxIdleConns(_ int) {} + +func (_ DB) SetMaxOpenConns(_ int) {} + +func (_ DB) Stats() sql.DBStats { + return sql.DBStats{} +} + +func (_ *DB) BeginTxx(_ context.Context, _ *sql.TxOptions) (*Tx, error) { + return nil, nil +} + +func (_ *DB) Beginx() (*Tx, error) { + return nil, nil +} + +func (_ *DB) BindNamed(_ string, _ interface{}) (string, []interface{}, error) { + return "", nil, nil +} + +func (_ *DB) DriverName() string { + return "" +} + +func (_ *DB) Get(_ interface{}, _ string, _ ...interface{}) error { + return nil +} + +func (_ *DB) GetContext(_ context.Context, _ interface{}, _ string, _ ...interface{}) error { + return nil +} + +func (_ *DB) MapperFunc(_ func(string) string) {} + +func (_ *DB) MustBegin() *Tx { + return nil +} + +func (_ *DB) MustBeginTx(_ context.Context, _ *sql.TxOptions) *Tx { + return nil +} + +func (_ *DB) MustExec(_ string, _ ...interface{}) sql.Result { + return nil +} + +func (_ *DB) MustExecContext(_ context.Context, _ string, _ ...interface{}) sql.Result { + return nil +} + +func (_ *DB) NamedExec(_ string, _ interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ *DB) NamedExecContext(_ context.Context, _ string, _ interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ *DB) NamedQuery(_ string, _ interface{}) (*Rows, error) { + return nil, nil +} + +func (_ *DB) NamedQueryContext(_ context.Context, _ string, _ interface{}) (*Rows, error) { + return nil, nil +} + +func (_ *DB) PrepareNamed(_ string) (*NamedStmt, error) { + return nil, nil +} + +func (_ *DB) PrepareNamedContext(_ context.Context, _ string) (*NamedStmt, error) { + return nil, nil +} + +func (_ *DB) Preparex(_ string) (*Stmt, error) { + return nil, nil +} + +func (_ *DB) PreparexContext(_ context.Context, _ string) (*Stmt, error) { + return nil, nil +} + +func (_ *DB) QueryRowx(_ string, _ ...interface{}) *Row { + return nil +} + +func (_ *DB) QueryRowxContext(_ context.Context, _ string, _ ...interface{}) *Row { + return nil +} + +func (_ *DB) Queryx(_ string, _ ...interface{}) (*Rows, error) { + return nil, nil +} + +func (_ *DB) QueryxContext(_ context.Context, _ string, _ ...interface{}) (*Rows, error) { + return nil, nil +} + +func (_ *DB) Rebind(_ string) string { + return "" +} + +func (_ *DB) Select(_ interface{}, _ string, _ ...interface{}) error { + return nil +} + +func (_ *DB) SelectContext(_ context.Context, _ interface{}, _ string, _ ...interface{}) error { + return nil +} + +func (_ *DB) Unsafe() *DB { + return nil +} + +type NamedStmt struct { + Params []string + QueryString string + Stmt *Stmt +} + +func (_ *NamedStmt) Close() error { + return nil +} + +func (_ *NamedStmt) Exec(_ interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ *NamedStmt) ExecContext(_ context.Context, _ interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ *NamedStmt) Get(_ interface{}, _ interface{}) error { + return nil +} + +func (_ *NamedStmt) GetContext(_ context.Context, _ interface{}, _ interface{}) error { + return nil +} + +func (_ *NamedStmt) MustExec(_ interface{}) sql.Result { + return nil +} + +func (_ *NamedStmt) MustExecContext(_ context.Context, _ interface{}) sql.Result { + return nil +} + +func (_ *NamedStmt) Query(_ interface{}) (*sql.Rows, error) { + return nil, nil +} + +func (_ *NamedStmt) QueryContext(_ context.Context, _ interface{}) (*sql.Rows, error) { + return nil, nil +} + +func (_ *NamedStmt) QueryRow(_ interface{}) *Row { + return nil +} + +func (_ *NamedStmt) QueryRowContext(_ context.Context, _ interface{}) *Row { + return nil +} + +func (_ *NamedStmt) QueryRowx(_ interface{}) *Row { + return nil +} + +func (_ *NamedStmt) QueryRowxContext(_ context.Context, _ interface{}) *Row { + return nil +} + +func (_ *NamedStmt) Queryx(_ interface{}) (*Rows, error) { + return nil, nil +} + +func (_ *NamedStmt) QueryxContext(_ context.Context, _ interface{}) (*Rows, error) { + return nil, nil +} + +func (_ *NamedStmt) Select(_ interface{}, _ interface{}) error { + return nil +} + +func (_ *NamedStmt) SelectContext(_ context.Context, _ interface{}, _ interface{}) error { + return nil +} + +func (_ *NamedStmt) Unsafe() *NamedStmt { + return nil +} + +type Row struct { + Mapper interface{} +} + +func (_ *Row) ColumnTypes() ([]*sql.ColumnType, error) { + return nil, nil +} + +func (_ *Row) Columns() ([]string, error) { + return nil, nil +} + +func (_ *Row) Err() error { + return nil +} + +func (_ *Row) MapScan(_ map[string]interface{}) error { + return nil +} + +func (_ *Row) Scan(_ ...interface{}) error { + return nil +} + +func (_ *Row) SliceScan() ([]interface{}, error) { + return nil, nil +} + +func (_ *Row) StructScan(_ interface{}) error { + return nil +} + +type Rows struct { + Rows *sql.Rows + Mapper interface{} +} + +func (_ Rows) Close() error { + return nil +} + +func (_ Rows) ColumnTypes() ([]*sql.ColumnType, error) { + return nil, nil +} + +func (_ Rows) Columns() ([]string, error) { + return nil, nil +} + +func (_ Rows) Err() error { + return nil +} + +func (_ Rows) Next() bool { + return false +} + +func (_ Rows) NextResultSet() bool { + return false +} + +func (_ Rows) Scan(_ ...interface{}) error { + return nil +} + +func (_ *Rows) MapScan(_ map[string]interface{}) error { + return nil +} + +func (_ *Rows) SliceScan() ([]interface{}, error) { + return nil, nil +} + +func (_ *Rows) StructScan(_ interface{}) error { + return nil +} + +type Stmt struct { + Stmt *sql.Stmt + Mapper interface{} +} + +func (_ Stmt) Close() error { + return nil +} + +func (_ Stmt) Exec(_ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ Stmt) ExecContext(_ context.Context, _ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ Stmt) Query(_ ...interface{}) (*sql.Rows, error) { + return nil, nil +} + +func (_ Stmt) QueryContext(_ context.Context, _ ...interface{}) (*sql.Rows, error) { + return nil, nil +} + +func (_ Stmt) QueryRow(_ ...interface{}) *sql.Row { + return nil +} + +func (_ Stmt) QueryRowContext(_ context.Context, _ ...interface{}) *sql.Row { + return nil +} + +func (_ *Stmt) Get(_ interface{}, _ ...interface{}) error { + return nil +} + +func (_ *Stmt) GetContext(_ context.Context, _ interface{}, _ ...interface{}) error { + return nil +} + +func (_ *Stmt) MustExec(_ ...interface{}) sql.Result { + return nil +} + +func (_ *Stmt) MustExecContext(_ context.Context, _ ...interface{}) sql.Result { + return nil +} + +func (_ *Stmt) QueryRowx(_ ...interface{}) *Row { + return nil +} + +func (_ *Stmt) QueryRowxContext(_ context.Context, _ ...interface{}) *Row { + return nil +} + +func (_ *Stmt) Queryx(_ ...interface{}) (*Rows, error) { + return nil, nil +} + +func (_ *Stmt) QueryxContext(_ context.Context, _ ...interface{}) (*Rows, error) { + return nil, nil +} + +func (_ *Stmt) Select(_ interface{}, _ ...interface{}) error { + return nil +} + +func (_ *Stmt) SelectContext(_ context.Context, _ interface{}, _ ...interface{}) error { + return nil +} + +func (_ *Stmt) Unsafe() *Stmt { + return nil +} + +type Tx struct { + Tx *sql.Tx + Mapper interface{} +} + +func (_ Tx) Commit() error { + return nil +} + +func (_ Tx) Exec(_ string, _ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ Tx) ExecContext(_ context.Context, _ string, _ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ Tx) Prepare(_ string) (*sql.Stmt, error) { + return nil, nil +} + +func (_ Tx) PrepareContext(_ context.Context, _ string) (*sql.Stmt, error) { + return nil, nil +} + +func (_ Tx) Query(_ string, _ ...interface{}) (*sql.Rows, error) { + return nil, nil +} + +func (_ Tx) QueryContext(_ context.Context, _ string, _ ...interface{}) (*sql.Rows, error) { + return nil, nil +} + +func (_ Tx) QueryRow(_ string, _ ...interface{}) *sql.Row { + return nil +} + +func (_ Tx) QueryRowContext(_ context.Context, _ string, _ ...interface{}) *sql.Row { + return nil +} + +func (_ Tx) Rollback() error { + return nil +} + +func (_ Tx) Stmt(_ *sql.Stmt) *sql.Stmt { + return nil +} + +func (_ Tx) StmtContext(_ context.Context, _ *sql.Stmt) *sql.Stmt { + return nil +} + +func (_ *Tx) BindNamed(_ string, _ interface{}) (string, []interface{}, error) { + return "", nil, nil +} + +func (_ *Tx) DriverName() string { + return "" +} + +func (_ *Tx) Get(_ interface{}, _ string, _ ...interface{}) error { + return nil +} + +func (_ *Tx) GetContext(_ context.Context, _ interface{}, _ string, _ ...interface{}) error { + return nil +} + +func (_ *Tx) MustExec(_ string, _ ...interface{}) sql.Result { + return nil +} + +func (_ *Tx) MustExecContext(_ context.Context, _ string, _ ...interface{}) sql.Result { + return nil +} + +func (_ *Tx) NamedExec(_ string, _ interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ *Tx) NamedExecContext(_ context.Context, _ string, _ interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ *Tx) NamedQuery(_ string, _ interface{}) (*Rows, error) { + return nil, nil +} + +func (_ *Tx) NamedStmt(_ *NamedStmt) *NamedStmt { + return nil +} + +func (_ *Tx) NamedStmtContext(_ context.Context, _ *NamedStmt) *NamedStmt { + return nil +} + +func (_ *Tx) PrepareNamed(_ string) (*NamedStmt, error) { + return nil, nil +} + +func (_ *Tx) PrepareNamedContext(_ context.Context, _ string) (*NamedStmt, error) { + return nil, nil +} + +func (_ *Tx) Preparex(_ string) (*Stmt, error) { + return nil, nil +} + +func (_ *Tx) PreparexContext(_ context.Context, _ string) (*Stmt, error) { + return nil, nil +} + +func (_ *Tx) QueryRowx(_ string, _ ...interface{}) *Row { + return nil +} + +func (_ *Tx) QueryRowxContext(_ context.Context, _ string, _ ...interface{}) *Row { + return nil +} + +func (_ *Tx) Queryx(_ string, _ ...interface{}) (*Rows, error) { + return nil, nil +} + +func (_ *Tx) QueryxContext(_ context.Context, _ string, _ ...interface{}) (*Rows, error) { + return nil, nil +} + +func (_ *Tx) Rebind(_ string) string { + return "" +} + +func (_ *Tx) Select(_ interface{}, _ string, _ ...interface{}) error { + return nil +} + +func (_ *Tx) SelectContext(_ context.Context, _ interface{}, _ string, _ ...interface{}) error { + return nil +} + +func (_ *Tx) Stmtx(_ interface{}) *Stmt { + return nil +} + +func (_ *Tx) StmtxContext(_ context.Context, _ interface{}) *Stmt { + return nil +} + +func (_ *Tx) Unsafe() *Tx { + return nil +} diff --git a/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/vendor/modules.txt b/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/vendor/modules.txt new file mode 100644 index 00000000..4b671d77 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/SQL/Sqlx/vendor/modules.txt @@ -0,0 +1,3 @@ +# github.com/jmoiron/sqlx v1.2.0 +## explicit +github.com/jmoiron/sqlx From 0e6feb923c12fefc5491727918e4006ecdc33a92 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Mon, 27 Jul 2020 16:46:11 +0100 Subject: [PATCH 7/8] Add test for json-iterator package, and support more of its API Specifically the top-level functions Unmarshal and UnmarshalFromString are just convenience wrappers around the type API, which is the usual documented way to use the library. --- ql/src/semmle/go/frameworks/Encoding.qll | 11 +- .../semmle/go/frameworks/Encoding/go.mod | 7 + .../go/frameworks/Encoding/jsoniter.expected | 4 + .../semmle/go/frameworks/Encoding/jsoniter.go | 42 ++ .../semmle/go/frameworks/Encoding/jsoniter.ql | 15 + .../github.com/json-iterator/go/LICENSE | 21 + .../github.com/json-iterator/go/stub.go | 368 ++++++++++++++++++ .../go/frameworks/Encoding/vendor/modules.txt | 3 + 8 files changed, 468 insertions(+), 3 deletions(-) create mode 100644 ql/test/library-tests/semmle/go/frameworks/Encoding/go.mod create mode 100644 ql/test/library-tests/semmle/go/frameworks/Encoding/jsoniter.expected create mode 100644 ql/test/library-tests/semmle/go/frameworks/Encoding/jsoniter.go create mode 100644 ql/test/library-tests/semmle/go/frameworks/Encoding/jsoniter.ql create mode 100644 ql/test/library-tests/semmle/go/frameworks/Encoding/vendor/github.com/json-iterator/go/LICENSE create mode 100644 ql/test/library-tests/semmle/go/frameworks/Encoding/vendor/github.com/json-iterator/go/stub.go create mode 100644 ql/test/library-tests/semmle/go/frameworks/Encoding/vendor/modules.txt diff --git a/ql/src/semmle/go/frameworks/Encoding.qll b/ql/src/semmle/go/frameworks/Encoding.qll index 2646b4f7..5d912539 100644 --- a/ql/src/semmle/go/frameworks/Encoding.qll +++ b/ql/src/semmle/go/frameworks/Encoding.qll @@ -1,14 +1,19 @@ /** - * Provides classes modelling taint propagation through the `json-iterator` package. + * Provides classes modelling taint propagation through marshalling and encoding functions. */ import go -/** Models json-iterator's Unmarshal function, propagating taint from the JSON input to the decoded object. */ +/** A model of json-iterator's `Unmarshal` function, propagating taint from the JSON input to the decoded object. */ private class JsonIteratorUnmarshalFunction extends TaintTracking::FunctionModel, UnmarshalingFunction::Range { JsonIteratorUnmarshalFunction() { - this.hasQualifiedName("github.com/json-iterator/go", "Unmarshal") + this.hasQualifiedName("github.com/json-iterator/go", ["Unmarshal", "UnmarshalFromString"]) + or + exists(Method m | + m.hasQualifiedName("github.com/json-iterator/go", "API", ["Unmarshal", "UnmarshalFromString"]) and + this.(Method).implements(m) + ) } override DataFlow::FunctionInput getAnInput() { result.isParameter(0) } diff --git a/ql/test/library-tests/semmle/go/frameworks/Encoding/go.mod b/ql/test/library-tests/semmle/go/frameworks/Encoding/go.mod new file mode 100644 index 00000000..91013339 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Encoding/go.mod @@ -0,0 +1,7 @@ +module jsonittest + +go 1.14 + +require ( + github.com/json-iterator/go v1.1.10 +) diff --git a/ql/test/library-tests/semmle/go/frameworks/Encoding/jsoniter.expected b/ql/test/library-tests/semmle/go/frameworks/Encoding/jsoniter.expected new file mode 100644 index 00000000..af6b6712 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Encoding/jsoniter.expected @@ -0,0 +1,4 @@ +| jsoniter.go:28:15:28:24 | selection of field | jsoniter.go:23:20:23:38 | call to getUntrustedBytes : slice type | jsoniter.go:28:15:28:24 | selection of field | This command depends on $@. | jsoniter.go:23:20:23:38 | call to getUntrustedBytes | a user-provided value | +| jsoniter.go:32:15:32:25 | selection of field | jsoniter.go:23:20:23:38 | call to getUntrustedBytes : slice type | jsoniter.go:32:15:32:25 | selection of field | This command depends on $@. | jsoniter.go:23:20:23:38 | call to getUntrustedBytes | a user-provided value | +| jsoniter.go:36:15:36:25 | selection of field | jsoniter.go:24:21:24:40 | call to getUntrustedString : string | jsoniter.go:36:15:36:25 | selection of field | This command depends on $@. | jsoniter.go:24:21:24:40 | call to getUntrustedString | a user-provided value | +| jsoniter.go:40:15:40:25 | selection of field | jsoniter.go:24:21:24:40 | call to getUntrustedString : string | jsoniter.go:40:15:40:25 | selection of field | This command depends on $@. | jsoniter.go:24:21:24:40 | call to getUntrustedString | a user-provided value | diff --git a/ql/test/library-tests/semmle/go/frameworks/Encoding/jsoniter.go b/ql/test/library-tests/semmle/go/frameworks/Encoding/jsoniter.go new file mode 100644 index 00000000..068a6709 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Encoding/jsoniter.go @@ -0,0 +1,42 @@ +package jsonittest + +import ( + jsoniter "github.com/json-iterator/go" + "os/exec" +) + +func getUntrustedString() string { + return "trouble" +} + +func getUntrustedBytes() []byte { + return []byte{} +} + +type myData struct { + field string +} + +func main() { + + var json = jsoniter.ExampleConfig{} + untrustedInput := getUntrustedBytes() + untrustedString := getUntrustedString() + + data := myData{} + json.Unmarshal(untrustedInput, &data) + exec.Command(data.field) + + data2 := myData{} + jsoniter.Unmarshal(untrustedInput, &data2) + exec.Command(data2.field) + + data3 := myData{} + json.UnmarshalFromString(untrustedString, &data3) + exec.Command(data3.field) + + data4 := myData{} + jsoniter.UnmarshalFromString(untrustedString, &data4) + exec.Command(data4.field) + +} diff --git a/ql/test/library-tests/semmle/go/frameworks/Encoding/jsoniter.ql b/ql/test/library-tests/semmle/go/frameworks/Encoding/jsoniter.ql new file mode 100644 index 00000000..a02f8593 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Encoding/jsoniter.ql @@ -0,0 +1,15 @@ +import go +import semmle.go.security.CommandInjection + +class UntrustedFunction extends Function { + UntrustedFunction() { this.getName() = ["getUntrustedString", "getUntrustedBytes"] } +} + +class UntrustedSource extends DataFlow::Node, UntrustedFlowSource::Range { + UntrustedSource() { this = any(UntrustedFunction f).getACall() } +} + +from CommandInjection::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink +where cfg.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "This command depends on $@.", source.getNode(), + "a user-provided value" diff --git a/ql/test/library-tests/semmle/go/frameworks/Encoding/vendor/github.com/json-iterator/go/LICENSE b/ql/test/library-tests/semmle/go/frameworks/Encoding/vendor/github.com/json-iterator/go/LICENSE new file mode 100644 index 00000000..2cf4f5ab --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Encoding/vendor/github.com/json-iterator/go/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 json-iterator + +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/library-tests/semmle/go/frameworks/Encoding/vendor/github.com/json-iterator/go/stub.go b/ql/test/library-tests/semmle/go/frameworks/Encoding/vendor/github.com/json-iterator/go/stub.go new file mode 100644 index 00000000..fef9f755 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Encoding/vendor/github.com/json-iterator/go/stub.go @@ -0,0 +1,368 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/json-iterator/go, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/json-iterator/go (exports: API; functions: Unmarshal,UnmarshalFromString) + +// Package gopkg is a stub of github.com/json-iterator/go, generated by depstubber. +package gopkg + +import ( + json "encoding/json" + io "io" + big "math/big" +) + +// Manually trimmed to only functions of interest +type API interface { + Unmarshal(_ []byte, _ interface{}) error + UnmarshalFromString(_ string, _ interface{}) error +} + +// Manually added, as the real library's concrete API types are all private +type ExampleConfig struct{} + +func (_ *ExampleConfig) Unmarshal(_ []byte, _ interface{}) error { return nil } +func (_ *ExampleConfig) UnmarshalFromString(_ string, _ interface{}) error { return nil } + +type Any interface { + Get(_ ...interface{}) Any + GetInterface() interface{} + Keys() []string + LastError() error + MustBeValid() Any + Size() int + ToBool() bool + ToFloat32() float32 + ToFloat64() float64 + ToInt() int + ToInt32() int32 + ToInt64() int64 + ToString() string + ToUint() uint + ToUint32() uint32 + ToUint64() uint64 + ToVal(_ interface{}) + ValueType() ValueType + WriteTo(_ *Stream) +} + +type Binding struct { + Field interface{} + FromNames []string + ToNames []string + Encoder ValEncoder + Decoder ValDecoder +} + +type Decoder struct{} + +func (_ *Decoder) Buffered() io.Reader { + return nil +} + +func (_ *Decoder) Decode(_ interface{}) error { + return nil +} + +func (_ *Decoder) DisallowUnknownFields() {} + +func (_ *Decoder) More() bool { + return false +} + +func (_ *Decoder) UseNumber() {} + +type Encoder struct{} + +func (_ *Encoder) Encode(_ interface{}) error { + return nil +} + +func (_ *Encoder) SetEscapeHTML(_ bool) {} + +func (_ *Encoder) SetIndent(_ string, _ string) {} + +type Extension interface { + CreateDecoder(_ interface{}) ValDecoder + CreateEncoder(_ interface{}) ValEncoder + CreateMapKeyDecoder(_ interface{}) ValDecoder + CreateMapKeyEncoder(_ interface{}) ValEncoder + DecorateDecoder(_ interface{}, _ ValDecoder) ValDecoder + DecorateEncoder(_ interface{}, _ ValEncoder) ValEncoder + UpdateStructDescriptor(_ *StructDescriptor) +} + +type Iterator struct { + Error error + Attachment interface{} +} + +func (_ *Iterator) CurrentBuffer() string { + return "" +} + +func (_ *Iterator) Pool() IteratorPool { + return nil +} + +func (_ *Iterator) Read() interface{} { + return nil +} + +func (_ *Iterator) ReadAny() Any { + return nil +} + +func (_ *Iterator) ReadArray() bool { + return false +} + +func (_ *Iterator) ReadArrayCB(_ func(*Iterator) bool) bool { + return false +} + +func (_ *Iterator) ReadBigFloat() *big.Float { + return nil +} + +func (_ *Iterator) ReadBigInt() *big.Int { + return nil +} + +func (_ *Iterator) ReadBool() bool { + return false +} + +func (_ *Iterator) ReadFloat32() float32 { + return 0 +} + +func (_ *Iterator) ReadFloat64() float64 { + return 0 +} + +func (_ *Iterator) ReadInt() int { + return 0 +} + +func (_ *Iterator) ReadInt16() int16 { + return 0 +} + +func (_ *Iterator) ReadInt32() int32 { + return 0 +} + +func (_ *Iterator) ReadInt64() int64 { + return 0 +} + +func (_ *Iterator) ReadInt8() int8 { + return 0 +} + +func (_ *Iterator) ReadMapCB(_ func(*Iterator, string) bool) bool { + return false +} + +func (_ *Iterator) ReadNil() bool { + return false +} + +func (_ *Iterator) ReadNumber() json.Number { + return "" +} + +func (_ *Iterator) ReadObject() string { + return "" +} + +func (_ *Iterator) ReadObjectCB(_ func(*Iterator, string) bool) bool { + return false +} + +func (_ *Iterator) ReadString() string { + return "" +} + +func (_ *Iterator) ReadStringAsSlice() []byte { + return nil +} + +func (_ *Iterator) ReadUint() uint { + return 0 +} + +func (_ *Iterator) ReadUint16() uint16 { + return 0 +} + +func (_ *Iterator) ReadUint32() uint32 { + return 0 +} + +func (_ *Iterator) ReadUint64() uint64 { + return 0 +} + +func (_ *Iterator) ReadUint8() byte { + return 0 +} + +func (_ *Iterator) ReadVal(_ interface{}) {} + +func (_ *Iterator) ReportError(_ string, _ string) {} + +func (_ *Iterator) Reset(_ io.Reader) *Iterator { + return nil +} + +func (_ *Iterator) ResetBytes(_ []byte) *Iterator { + return nil +} + +func (_ *Iterator) Skip() {} + +func (_ *Iterator) SkipAndAppendBytes(_ []byte) []byte { + return nil +} + +func (_ *Iterator) SkipAndReturnBytes() []byte { + return nil +} + +func (_ *Iterator) WhatIsNext() ValueType { + return 0 +} + +type IteratorPool interface { + BorrowIterator(_ []byte) *Iterator + ReturnIterator(_ *Iterator) +} + +type Stream struct { + Error error + Attachment interface{} +} + +func (_ *Stream) Available() int { + return 0 +} + +func (_ *Stream) Buffer() []byte { + return nil +} + +func (_ *Stream) Buffered() int { + return 0 +} + +func (_ *Stream) Flush() error { + return nil +} + +func (_ *Stream) Pool() StreamPool { + return nil +} + +func (_ *Stream) Reset(_ io.Writer) {} + +func (_ *Stream) SetBuffer(_ []byte) {} + +func (_ *Stream) Write(_ []byte) (int, error) { + return 0, nil +} + +func (_ *Stream) WriteArrayEnd() {} + +func (_ *Stream) WriteArrayStart() {} + +func (_ *Stream) WriteBool(_ bool) {} + +func (_ *Stream) WriteEmptyArray() {} + +func (_ *Stream) WriteEmptyObject() {} + +func (_ *Stream) WriteFalse() {} + +func (_ *Stream) WriteFloat32(_ float32) {} + +func (_ *Stream) WriteFloat32Lossy(_ float32) {} + +func (_ *Stream) WriteFloat64(_ float64) {} + +func (_ *Stream) WriteFloat64Lossy(_ float64) {} + +func (_ *Stream) WriteInt(_ int) {} + +func (_ *Stream) WriteInt16(_ int16) {} + +func (_ *Stream) WriteInt32(_ int32) {} + +func (_ *Stream) WriteInt64(_ int64) {} + +func (_ *Stream) WriteInt8(_ int8) {} + +func (_ *Stream) WriteMore() {} + +func (_ *Stream) WriteNil() {} + +func (_ *Stream) WriteObjectEnd() {} + +func (_ *Stream) WriteObjectField(_ string) {} + +func (_ *Stream) WriteObjectStart() {} + +func (_ *Stream) WriteRaw(_ string) {} + +func (_ *Stream) WriteString(_ string) {} + +func (_ *Stream) WriteStringWithHTMLEscaped(_ string) {} + +func (_ *Stream) WriteTrue() {} + +func (_ *Stream) WriteUint(_ uint) {} + +func (_ *Stream) WriteUint16(_ uint16) {} + +func (_ *Stream) WriteUint32(_ uint32) {} + +func (_ *Stream) WriteUint64(_ uint64) {} + +func (_ *Stream) WriteUint8(_ byte) {} + +func (_ *Stream) WriteVal(_ interface{}) {} + +type StreamPool interface { + BorrowStream(_ io.Writer) *Stream + ReturnStream(_ *Stream) +} + +type StructDescriptor struct { + Type interface{} + Fields []*Binding +} + +func (_ *StructDescriptor) GetField(_ string) *Binding { + return nil +} + +func Unmarshal(_ []byte, _ interface{}) error { + return nil +} + +func UnmarshalFromString(_ string, _ interface{}) error { + return nil +} + +type ValDecoder interface { + Decode(_ interface{}, _ *Iterator) +} + +type ValEncoder interface { + Encode(_ interface{}, _ *Stream) + IsEmpty(_ interface{}) bool +} + +type ValueType int diff --git a/ql/test/library-tests/semmle/go/frameworks/Encoding/vendor/modules.txt b/ql/test/library-tests/semmle/go/frameworks/Encoding/vendor/modules.txt new file mode 100644 index 00000000..c8d2d7c5 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Encoding/vendor/modules.txt @@ -0,0 +1,3 @@ +# github.com/json-iterator/go v1.1.10 +## explicit +github.com/json-iterator/go From 026dc5c97f9b49d72957bbb1d08e32b639676362 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Tue, 28 Jul 2020 14:57:14 +0100 Subject: [PATCH 8/8] Add changelog notes regarding added library support --- change-notes/2020-07-28-library-models.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 change-notes/2020-07-28-library-models.md diff --git a/change-notes/2020-07-28-library-models.md b/change-notes/2020-07-28-library-models.md new file mode 100644 index 00000000..33b16e40 --- /dev/null +++ b/change-notes/2020-07-28-library-models.md @@ -0,0 +1,11 @@ +lgtm,codescanning +* Basic support for the [Go-restful](https://github.com/emicklei/go-restful) HTTP library has been added, which + may lead to more results from the security queries. +* Basic support for the [Gorm](https://github.com/go-gorm/gorm) ORM library has been added (specifically, its SQL statement building facilities), which + may lead to more results from the security queries. +* Basic support for the [Sqlx](https://github.com/jmoiron/sqlx) database access library has been added, which + may lead to more results from the security queries. +* Basic support for the [Json-iterator](https://github.com/json-iterator/go) JSON library has been added, which + may lead to more results from the security queries. + +