diff --git a/change-notes/2020-09-15-chi.md b/change-notes/2020-09-15-chi.md new file mode 100644 index 00000000000..f8674951cfe --- /dev/null +++ b/change-notes/2020-09-15-chi.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Added support for the Chi web framework diff --git a/ql/src/go.qll b/ql/src/go.qll index d37c05e68b4..66b3336e40e 100644 --- a/ql/src/go.qll +++ b/ql/src/go.qll @@ -26,6 +26,7 @@ import semmle.go.dataflow.DataFlow import semmle.go.dataflow.GlobalValueNumbering import semmle.go.dataflow.SSA import semmle.go.dataflow.TaintTracking +import semmle.go.frameworks.Chi import semmle.go.frameworks.Email import semmle.go.frameworks.Encoding import semmle.go.frameworks.Gin diff --git a/ql/src/semmle/go/frameworks/Chi.qll b/ql/src/semmle/go/frameworks/Chi.qll new file mode 100644 index 00000000000..817ed14387f --- /dev/null +++ b/ql/src/semmle/go/frameworks/Chi.qll @@ -0,0 +1,26 @@ +/** + * Provides classes for working with untrusted flow sources from the `github.com/go-chi/chi` package. + */ + +import go + +private module Chi { + /** + * Functions that extract URL parameters, considered as a source of untrusted flow. + */ + private class UserControlledFunction extends UntrustedFlowSource::Range, DataFlow::CallNode { + UserControlledFunction() { + this.getTarget().hasQualifiedName("github.com/go-chi/chi", ["URLParam", "URLParamFromCtx"]) + } + } + + /** + * Methods that extract URL parameters, considered as a source of untrusted flow. + */ + private class UserControlledRequestMethod extends UntrustedFlowSource::Range, + DataFlow::MethodCallNode { + UserControlledRequestMethod() { + this.getTarget().hasQualifiedName("github.com/go-chi/chi", "Context", "URLParam") + } + } +} diff --git a/ql/test/library-tests/semmle/go/frameworks/Chi/LICENSE b/ql/test/library-tests/semmle/go/frameworks/Chi/LICENSE new file mode 100644 index 00000000000..d99f02ffac5 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Chi/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2015-present Peter Kieltyka (https://github.com/pkieltyka), Google Inc. + +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. diff --git a/ql/test/library-tests/semmle/go/frameworks/Chi/ReflectedXss.expected b/ql/test/library-tests/semmle/go/frameworks/Chi/ReflectedXss.expected new file mode 100644 index 00000000000..68cf414f652 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Chi/ReflectedXss.expected @@ -0,0 +1,29 @@ +edges +| test.go:13:12:13:16 | implicit dereference : URL | test.go:13:12:13:16 | implicit dereference : URL | +| test.go:13:12:13:16 | implicit dereference : URL | test.go:13:12:13:16 | selection of URL : pointer type | +| test.go:13:12:13:16 | implicit dereference : URL | test.go:13:12:13:21 | selection of Path : string | +| test.go:13:12:13:16 | selection of URL : pointer type | test.go:13:12:13:16 | implicit dereference : URL | +| test.go:13:12:13:16 | selection of URL : pointer type | test.go:13:12:13:16 | selection of URL : pointer type | +| test.go:13:12:13:16 | selection of URL : pointer type | test.go:13:12:13:21 | selection of Path : string | +| test.go:13:12:13:21 | selection of Path : string | test.go:21:18:21:23 | hidden : string | +| test.go:21:18:21:23 | hidden : string | test.go:21:11:21:24 | type conversion | +| test.go:22:18:22:45 | call to URLParam : string | test.go:22:11:22:46 | type conversion | +| test.go:23:18:23:60 | call to URLParamFromCtx : string | test.go:23:11:23:61 | type conversion | +| test.go:24:18:24:71 | call to URLParam : string | test.go:24:11:24:72 | type conversion | +nodes +| test.go:13:12:13:16 | implicit dereference : URL | semmle.label | implicit dereference : URL | +| test.go:13:12:13:16 | selection of URL : pointer type | semmle.label | selection of URL : pointer type | +| test.go:13:12:13:21 | selection of Path : string | semmle.label | selection of Path : string | +| test.go:21:11:21:24 | type conversion | semmle.label | type conversion | +| test.go:21:18:21:23 | hidden : string | semmle.label | hidden : string | +| test.go:22:11:22:46 | type conversion | semmle.label | type conversion | +| test.go:22:18:22:45 | call to URLParam : string | semmle.label | call to URLParam : string | +| test.go:23:11:23:61 | type conversion | semmle.label | type conversion | +| test.go:23:18:23:60 | call to URLParamFromCtx : string | semmle.label | call to URLParamFromCtx : string | +| test.go:24:11:24:72 | type conversion | semmle.label | type conversion | +| test.go:24:18:24:71 | call to URLParam : string | semmle.label | call to URLParam : string | +#select +| test.go:21:11:21:24 | type conversion | test.go:13:12:13:16 | selection of URL : pointer type | test.go:21:11:21:24 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:13:12:13:16 | selection of URL | user-provided value | +| test.go:22:11:22:46 | type conversion | test.go:22:18:22:45 | call to URLParam : string | test.go:22:11:22:46 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:22:18:22:45 | call to URLParam | user-provided value | +| test.go:23:11:23:61 | type conversion | test.go:23:18:23:60 | call to URLParamFromCtx : string | test.go:23:11:23:61 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:23:18:23:60 | call to URLParamFromCtx | user-provided value | +| test.go:24:11:24:72 | type conversion | test.go:24:18:24:71 | call to URLParam : string | test.go:24:11:24:72 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:24:18:24:71 | call to URLParam | user-provided value | diff --git a/ql/test/library-tests/semmle/go/frameworks/Chi/ReflectedXss.qlref b/ql/test/library-tests/semmle/go/frameworks/Chi/ReflectedXss.qlref new file mode 100644 index 00000000000..e0efe102416 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Chi/ReflectedXss.qlref @@ -0,0 +1 @@ +Security/CWE-079/ReflectedXss.ql diff --git a/ql/test/library-tests/semmle/go/frameworks/Chi/go.mod b/ql/test/library-tests/semmle/go/frameworks/Chi/go.mod new file mode 100644 index 00000000000..91b718eb776 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Chi/go.mod @@ -0,0 +1,7 @@ +go 1.14 + +module test + +require ( + github.com/go-chi/chi v4.1.2+incompatible +) diff --git a/ql/test/library-tests/semmle/go/frameworks/Chi/test.go b/ql/test/library-tests/semmle/go/frameworks/Chi/test.go new file mode 100644 index 00000000000..f02e0cdfb15 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Chi/test.go @@ -0,0 +1,27 @@ +package main + +import ( + "net/http" + + "github.com/go-chi/chi" +) + +var hidden string + +func hideUserData(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + hidden = r.URL.Path + next.ServeHTTP(w, r) + }) +} + +func main() { + r := chi.NewRouter() + r.With(hideUserData).Get("/", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(hidden)) + w.Write([]byte(chi.URLParam(r, "someParam"))) + w.Write([]byte(chi.URLParamFromCtx(r.Context(), "someKey"))) + w.Write([]byte(chi.RouteContext(r.Context()).URLParam("someOtherKey"))) + }) + http.ListenAndServe(":3000", r) +} diff --git a/ql/test/library-tests/semmle/go/frameworks/Chi/vendor/github.com/go-chi/chi/stub.go b/ql/test/library-tests/semmle/go/frameworks/Chi/vendor/github.com/go-chi/chi/stub.go new file mode 100644 index 00000000000..9b335a9c002 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Chi/vendor/github.com/go-chi/chi/stub.go @@ -0,0 +1,173 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/go-chi/chi, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/go-chi/chi (exports: Context; functions: URLParam,URLParamFromCtx,NewRouter,RouteContext) + +// Package chi is a stub of github.com/go-chi/chi, generated by depstubber. +package chi + +import ( + context "context" + http "net/http" +) + +type Context struct { + Routes Routes + RoutePath string + RouteMethod string + RoutePatterns []string + URLParams RouteParams +} + +func (_ *Context) Reset() {} + +func (_ *Context) RoutePattern() string { + return "" +} + +func (_ *Context) URLParam(_ string) string { + return "" +} + +type Middlewares []func(http.Handler) http.Handler + +func (_ Middlewares) Handler(_ http.Handler) http.Handler { + return nil +} + +func (_ Middlewares) HandlerFunc(_ http.HandlerFunc) http.Handler { + return nil +} + +type Mux struct{} + +func (_ *Mux) Connect(_ string, _ http.HandlerFunc) {} + +func (_ *Mux) Delete(_ string, _ http.HandlerFunc) {} + +func (_ *Mux) Get(_ string, _ http.HandlerFunc) {} + +func (_ *Mux) Group(_ func(Router)) Router { + return nil +} + +func (_ *Mux) Handle(_ string, _ http.Handler) {} + +func (_ *Mux) HandleFunc(_ string, _ http.HandlerFunc) {} + +func (_ *Mux) Head(_ string, _ http.HandlerFunc) {} + +func (_ *Mux) Match(_ *Context, _ string, _ string) bool { + return false +} + +func (_ *Mux) Method(_ string, _ string, _ http.Handler) {} + +func (_ *Mux) MethodFunc(_ string, _ string, _ http.HandlerFunc) {} + +func (_ *Mux) MethodNotAllowed(_ http.HandlerFunc) {} + +func (_ *Mux) MethodNotAllowedHandler() http.HandlerFunc { + return nil +} + +func (_ *Mux) Middlewares() Middlewares { + return nil +} + +func (_ *Mux) Mount(_ string, _ http.Handler) {} + +func (_ *Mux) NotFound(_ http.HandlerFunc) {} + +func (_ *Mux) NotFoundHandler() http.HandlerFunc { + return nil +} + +func (_ *Mux) Options(_ string, _ http.HandlerFunc) {} + +func (_ *Mux) Patch(_ string, _ http.HandlerFunc) {} + +func (_ *Mux) Post(_ string, _ http.HandlerFunc) {} + +func (_ *Mux) Put(_ string, _ http.HandlerFunc) {} + +func (_ *Mux) Route(_ string, _ func(Router)) Router { + return nil +} + +func (_ *Mux) Routes() []Route { + return nil +} + +func (_ *Mux) ServeHTTP(_ http.ResponseWriter, _ *http.Request) {} + +func (_ *Mux) Trace(_ string, _ http.HandlerFunc) {} + +func (_ *Mux) Use(_ ...func(http.Handler) http.Handler) {} + +func (_ *Mux) With(_ ...func(http.Handler) http.Handler) Router { + return nil +} + +func NewRouter() *Mux { + return nil +} + +type Route struct { + Pattern string + Handlers map[string]http.Handler + SubRoutes Routes +} + +func RouteContext(_ context.Context) *Context { + return nil +} + +type RouteParams struct { + Keys []string + Values []string +} + +func (_ *RouteParams) Add(_ string, _ string) {} + +type Router interface { + Connect(_ string, _ http.HandlerFunc) + Delete(_ string, _ http.HandlerFunc) + Get(_ string, _ http.HandlerFunc) + Group(_ func(Router)) Router + Handle(_ string, _ http.Handler) + HandleFunc(_ string, _ http.HandlerFunc) + Head(_ string, _ http.HandlerFunc) + Match(_ *Context, _ string, _ string) bool + Method(_ string, _ string, _ http.Handler) + MethodFunc(_ string, _ string, _ http.HandlerFunc) + MethodNotAllowed(_ http.HandlerFunc) + Middlewares() Middlewares + Mount(_ string, _ http.Handler) + NotFound(_ http.HandlerFunc) + Options(_ string, _ http.HandlerFunc) + Patch(_ string, _ http.HandlerFunc) + Post(_ string, _ http.HandlerFunc) + Put(_ string, _ http.HandlerFunc) + Route(_ string, _ func(Router)) Router + Routes() []Route + ServeHTTP(_ http.ResponseWriter, _ *http.Request) + Trace(_ string, _ http.HandlerFunc) + Use(_ ...func(http.Handler) http.Handler) + With(_ ...func(http.Handler) http.Handler) Router +} + +type Routes interface { + Match(_ *Context, _ string, _ string) bool + Middlewares() Middlewares + Routes() []Route +} + +func URLParam(_ *http.Request, _ string) string { + return "" +} + +func URLParamFromCtx(_ context.Context, _ string) string { + return "" +} diff --git a/ql/test/library-tests/semmle/go/frameworks/Chi/vendor/modules.txt b/ql/test/library-tests/semmle/go/frameworks/Chi/vendor/modules.txt new file mode 100644 index 00000000000..33ee43e4697 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Chi/vendor/modules.txt @@ -0,0 +1,3 @@ +# github.com/go-chi/chi v4.1.2+incompatible +## explicit +github.com/go-chi/chi