mirror of
https://github.com/github/codeql.git
synced 2026-01-29 22:32:58 +01:00
Add models for github.com/elazarl/goproxy
This commit is contained in:
@@ -34,6 +34,7 @@ import semmle.go.frameworks.BeegoOrm
|
||||
import semmle.go.frameworks.Chi
|
||||
import semmle.go.frameworks.Couchbase
|
||||
import semmle.go.frameworks.Echo
|
||||
import semmle.go.frameworks.ElazarlGoproxy
|
||||
import semmle.go.frameworks.Email
|
||||
import semmle.go.frameworks.Encoding
|
||||
import semmle.go.frameworks.EvanphxJsonPatch
|
||||
|
||||
135
ql/src/semmle/go/frameworks/ElazarlGoproxy.qll
Normal file
135
ql/src/semmle/go/frameworks/ElazarlGoproxy.qll
Normal file
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* Provides classes for working with concepts relating to the [github.com/elazarl/goproxy](https://pkg.go.dev/github.com/elazarl/goproxy) package.
|
||||
*/
|
||||
|
||||
import go
|
||||
|
||||
/**
|
||||
* Provides classes for working with concepts relating to the [github.com/elazarl/goproxy](https://pkg.go.dev/github.com/elazarl/goproxy) package.
|
||||
*/
|
||||
module ElazarlGoproxy {
|
||||
/** Gets the package name. */
|
||||
bindingset[result]
|
||||
string packagePath() { result = package("github.com/elazarl/goproxy", "") }
|
||||
|
||||
private class NewResponse extends HTTP::HeaderWrite::Range, DataFlow::CallNode {
|
||||
NewResponse() { this.getTarget().hasQualifiedName(packagePath(), "NewResponse") }
|
||||
|
||||
override string getHeaderName() { this.definesHeader(result, _) }
|
||||
|
||||
override string getHeaderValue() { this.definesHeader(_, result) }
|
||||
|
||||
override DataFlow::Node getName() { none() }
|
||||
|
||||
override DataFlow::Node getValue() { result = this.getArgument([1, 2]) }
|
||||
|
||||
override predicate definesHeader(string header, string value) {
|
||||
header = "status" and value = this.getArgument(2).getIntValue().toString()
|
||||
or
|
||||
header = "content-type" and value = this.getArgument(1).getStringValue()
|
||||
}
|
||||
|
||||
override HTTP::ResponseWriter getResponseWriter() { none() }
|
||||
}
|
||||
|
||||
/** A body argument to a `NewResponse` call. */
|
||||
private class NewResponseBody extends HTTP::ResponseBody::Range {
|
||||
NewResponse call;
|
||||
|
||||
NewResponseBody() { this = call.getArgument(3) }
|
||||
|
||||
override DataFlow::Node getAContentTypeNode() { result = call.getArgument(1) }
|
||||
|
||||
override HTTP::ResponseWriter getResponseWriter() { none() }
|
||||
}
|
||||
|
||||
private class TextResponse extends HTTP::HeaderWrite::Range, DataFlow::CallNode {
|
||||
TextResponse() { this.getTarget().hasQualifiedName(packagePath(), "TextResponse") }
|
||||
|
||||
override string getHeaderName() { this.definesHeader(result, _) }
|
||||
|
||||
override string getHeaderValue() { this.definesHeader(_, result) }
|
||||
|
||||
override DataFlow::Node getName() { none() }
|
||||
|
||||
override DataFlow::Node getValue() { none() }
|
||||
|
||||
override predicate definesHeader(string header, string value) {
|
||||
header = "status" and value = "200"
|
||||
or
|
||||
header = "content-type" and value = "text/plain"
|
||||
}
|
||||
|
||||
override HTTP::ResponseWriter getResponseWriter() { none() }
|
||||
}
|
||||
|
||||
/** A body argument to a `TextResponse` call. */
|
||||
private class TextResponseBody extends HTTP::ResponseBody::Range, TextResponse {
|
||||
TextResponse call;
|
||||
|
||||
TextResponseBody() { this = call.getArgument(2) }
|
||||
|
||||
override DataFlow::Node getAContentTypeNode() { result = call.getArgument(1) }
|
||||
|
||||
override HTTP::ResponseWriter getResponseWriter() { none() }
|
||||
}
|
||||
|
||||
/** A handler attached to a goproxy proxy type. */
|
||||
private class ProxyHandler extends HTTP::RequestHandler::Range {
|
||||
DataFlow::MethodCallNode handlerReg;
|
||||
|
||||
ProxyHandler() {
|
||||
handlerReg
|
||||
.getTarget()
|
||||
.hasQualifiedName(packagePath(), "ReqProxyConds", ["Do", "DoFunc", "HandleConnect"]) and
|
||||
this = handlerReg.getArgument(0)
|
||||
}
|
||||
|
||||
override predicate guardedBy(DataFlow::Node check) {
|
||||
// note OnResponse is not modeled, as that server responses are not currently considered untrusted input
|
||||
exists(DataFlow::MethodCallNode onreqcall |
|
||||
onreqcall.getTarget().hasQualifiedName(packagePath(), "ProxyHttpServer", "OnRequest")
|
||||
|
|
||||
handlerReg.getReceiver() = onreqcall.getASuccessor*() and
|
||||
check = onreqcall.getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class UserControlledRequestData extends UntrustedFlowSource::Range {
|
||||
UserControlledRequestData() {
|
||||
exists(DataFlow::FieldReadNode frn | this = frn |
|
||||
// liberally consider ProxyCtx.UserData to be untrusted; it's a data field set by a request handler
|
||||
frn.getField().hasQualifiedName(packagePath(), "ProxyCtx", "UserData")
|
||||
)
|
||||
or
|
||||
exists(DataFlow::MethodCallNode call | this = call |
|
||||
call.getTarget().hasQualifiedName(packagePath(), "ProxyCtx", "Charset")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class ProxyLog extends LoggerCall::Range, DataFlow::MethodCallNode {
|
||||
ProxyLog() { this.getTarget().hasQualifiedName(packagePath(), "ProxyCtx", ["Logf", "Warnf"]) }
|
||||
|
||||
override DataFlow::Node getAMessageComponent() { result = this.getAnArgument() }
|
||||
}
|
||||
|
||||
private class MethodModels extends TaintTracking::FunctionModel, Method {
|
||||
FunctionInput inp;
|
||||
FunctionOutput outp;
|
||||
|
||||
MethodModels() {
|
||||
// Methods:
|
||||
// signature: func CertStorage.Fetch(hostname string, gen func() (*tls.Certificate, error)) (*tls.Certificate, error)
|
||||
//
|
||||
// `hostname` excluded because if the cert storage or generator function themselves have not
|
||||
// been tainted, `hostname` would be unlikely to fetch user-controlled data
|
||||
this.hasQualifiedName(packagePath(), "CertStorage", "Fetch") and
|
||||
(inp.isReceiver() or inp.isParameter(1)) and
|
||||
outp.isResult(0)
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput i, FunctionOutput o) { i = inp and o = outp }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
module main
|
||||
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/elazarl/goproxy v0.0.0-20201021153353-00ad82a08272
|
||||
github.com/github/depstubber v0.0.0-20201214172518-12c3da4b7c9d // indirect
|
||||
)
|
||||
@@ -0,0 +1,27 @@
|
||||
//go:generate depstubber -vendor github.com/elazarl/goproxy ProxyCtx NewResponse,TextResponse,AlwaysReject,ContentTypeText,ContentTypeHtml,ReqHostMatches
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/elazarl/goproxy"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func handler(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
|
||||
data := ctx.UserData // $untrustedflowsource=selection of UserData
|
||||
|
||||
// note no content type result here because we don't seem to extract the value of `ContentTypeHtml`
|
||||
return r, goproxy.NewResponse(r, goproxy.ContentTypeHtml, http.StatusForbidden, fmt.Sprintf("<body>Bad request: %v</body>", data)) // $headerwrite=status:403
|
||||
}
|
||||
|
||||
func handler1(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
|
||||
ctx.Logf("test") // $logger="test"
|
||||
ctx.Warnf("test1") // $logger="test1"
|
||||
|
||||
return r, goproxy.TextResponse(r, "Hello!") // $headerwrite=status:200 $headerwrite=content-type:text/plain
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import go
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
class UntrustedFlowSourceTest extends InlineExpectationsTest {
|
||||
UntrustedFlowSourceTest() { this = "untrustedflowsource" }
|
||||
|
||||
override string getARelevantTag() { result = "untrustedflowsource" }
|
||||
|
||||
override predicate hasActualResult(string file, int line, string element, string tag, string value) {
|
||||
tag = "untrustedflowsource" and
|
||||
value = element and
|
||||
exists(UntrustedFlowSource src | value = src.toString() |
|
||||
src.hasLocationInfo(file, line, _, _, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class HeaderWriteTest extends InlineExpectationsTest {
|
||||
HeaderWriteTest() { this = "headerwrite" }
|
||||
|
||||
override string getARelevantTag() { result = "headerwrite" }
|
||||
|
||||
override predicate hasActualResult(string file, int line, string element, string tag, string value) {
|
||||
tag = "headerwrite" and
|
||||
exists(HTTP::HeaderWrite hw, string name, string val | element = hw.toString() |
|
||||
hw.definesHeader(name, val) and
|
||||
value = name + ":" + val and
|
||||
hw.hasLocationInfo(file, line, _, _, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class LoggerTest extends InlineExpectationsTest {
|
||||
LoggerTest() { this = "LoggerTest" }
|
||||
|
||||
override string getARelevantTag() { result = "logger" }
|
||||
|
||||
override predicate hasActualResult(string file, int line, string element, string tag, string value) {
|
||||
exists(LoggerCall log |
|
||||
log.hasLocationInfo(file, line, _, _, _) and
|
||||
element = log.toString() and
|
||||
value = log.getAMessageComponent().toString() and
|
||||
tag = "logger"
|
||||
)
|
||||
}
|
||||
}
|
||||
2
ql/test/library-tests/semmle/go/frameworks/ElazarlGoproxy/vendor/github.com/elazarl/goproxy/.gitignore
generated
vendored
Normal file
2
ql/test/library-tests/semmle/go/frameworks/ElazarlGoproxy/vendor/github.com/elazarl/goproxy/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
bin
|
||||
*.swp
|
||||
27
ql/test/library-tests/semmle/go/frameworks/ElazarlGoproxy/vendor/github.com/elazarl/goproxy/LICENSE
generated
vendored
Normal file
27
ql/test/library-tests/semmle/go/frameworks/ElazarlGoproxy/vendor/github.com/elazarl/goproxy/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2012 Elazar Leibovich. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Elazar Leibovich. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
159
ql/test/library-tests/semmle/go/frameworks/ElazarlGoproxy/vendor/github.com/elazarl/goproxy/stub.go
generated
vendored
Normal file
159
ql/test/library-tests/semmle/go/frameworks/ElazarlGoproxy/vendor/github.com/elazarl/goproxy/stub.go
generated
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
// Code generated by depstubber. DO NOT EDIT.
|
||||
// This is a simple stub for github.com/elazarl/goproxy, strictly for use in testing.
|
||||
|
||||
// See the LICENSE file for information about the licensing of the original library.
|
||||
// Source: github.com/elazarl/goproxy (exports: ProxyCtx; functions: NewResponse,TextResponse,AlwaysReject,ContentTypeText,ContentTypeHtml,ReqHostMatches)
|
||||
|
||||
// Package goproxy is a stub of github.com/elazarl/goproxy, generated by depstubber.
|
||||
package goproxy
|
||||
|
||||
import (
|
||||
tls "crypto/tls"
|
||||
net "net"
|
||||
http "net/http"
|
||||
regexp "regexp"
|
||||
)
|
||||
|
||||
var AlwaysReject FuncHttpsHandler = nil
|
||||
|
||||
type CertStorage interface {
|
||||
Fetch(_ string, _ func() (*tls.Certificate, error)) (*tls.Certificate, error)
|
||||
}
|
||||
|
||||
type ConnectAction struct {
|
||||
Action ConnectActionLiteral
|
||||
Hijack func(*http.Request, net.Conn, *ProxyCtx)
|
||||
TLSConfig func(string, *ProxyCtx) (*tls.Config, error)
|
||||
}
|
||||
|
||||
type ConnectActionLiteral int
|
||||
|
||||
var ContentTypeHtml string = ""
|
||||
|
||||
var ContentTypeText string = ""
|
||||
|
||||
type FuncHttpsHandler func(string, *ProxyCtx) (*ConnectAction, string)
|
||||
|
||||
func (_ FuncHttpsHandler) HandleConnect(_ string, _ *ProxyCtx) (*ConnectAction, string) {
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
type HttpsHandler interface {
|
||||
HandleConnect(_ string, _ *ProxyCtx) (*ConnectAction, string)
|
||||
}
|
||||
|
||||
type Logger interface {
|
||||
Printf(_ string, _ ...interface{})
|
||||
}
|
||||
|
||||
func NewResponse(_ *http.Request, _ string, _ int, _ string) *http.Response {
|
||||
return nil
|
||||
}
|
||||
|
||||
type ProxyConds struct{}
|
||||
|
||||
func (_ *ProxyConds) Do(_ RespHandler) {}
|
||||
|
||||
func (_ *ProxyConds) DoFunc(_ func(*http.Response, *ProxyCtx) *http.Response) {}
|
||||
|
||||
type ProxyCtx struct {
|
||||
Req *http.Request
|
||||
Resp *http.Response
|
||||
RoundTripper RoundTripper
|
||||
Error error
|
||||
UserData interface{}
|
||||
Session int64
|
||||
Proxy *ProxyHttpServer
|
||||
}
|
||||
|
||||
func (_ *ProxyCtx) Charset() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (_ *ProxyCtx) Logf(_ string, _ ...interface{}) {}
|
||||
|
||||
func (_ *ProxyCtx) RoundTrip(_ *http.Request) (*http.Response, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_ *ProxyCtx) Warnf(_ string, _ ...interface{}) {}
|
||||
|
||||
type ProxyHttpServer struct {
|
||||
KeepDestinationHeaders bool
|
||||
Verbose bool
|
||||
Logger Logger
|
||||
NonproxyHandler http.Handler
|
||||
Tr *http.Transport
|
||||
ConnectDial func(string, string) (net.Conn, error)
|
||||
CertStore CertStorage
|
||||
KeepHeader bool
|
||||
}
|
||||
|
||||
func (_ *ProxyHttpServer) NewConnectDialToProxy(_ string) func(string, string) (net.Conn, error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *ProxyHttpServer) NewConnectDialToProxyWithHandler(_ string, _ func(*http.Request)) func(string, string) (net.Conn, error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *ProxyHttpServer) OnRequest(_ ...ReqCondition) *ReqProxyConds {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *ProxyHttpServer) OnResponse(_ ...RespCondition) *ProxyConds {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *ProxyHttpServer) ServeHTTP(_ http.ResponseWriter, _ *http.Request) {}
|
||||
|
||||
type ReqCondition interface {
|
||||
HandleReq(_ *http.Request, _ *ProxyCtx) bool
|
||||
HandleResp(_ *http.Response, _ *ProxyCtx) bool
|
||||
}
|
||||
|
||||
type ReqConditionFunc func(*http.Request, *ProxyCtx) bool
|
||||
|
||||
func (_ ReqConditionFunc) HandleReq(_ *http.Request, _ *ProxyCtx) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (_ ReqConditionFunc) HandleResp(_ *http.Response, _ *ProxyCtx) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type ReqHandler interface {
|
||||
Handle(_ *http.Request, _ *ProxyCtx) (*http.Request, *http.Response)
|
||||
}
|
||||
|
||||
func ReqHostMatches(_ ...*regexp.Regexp) ReqConditionFunc {
|
||||
return nil
|
||||
}
|
||||
|
||||
type ReqProxyConds struct{}
|
||||
|
||||
func (_ *ReqProxyConds) Do(_ ReqHandler) {}
|
||||
|
||||
func (_ *ReqProxyConds) DoFunc(_ func(*http.Request, *ProxyCtx) (*http.Request, *http.Response)) {}
|
||||
|
||||
func (_ *ReqProxyConds) HandleConnect(_ HttpsHandler) {}
|
||||
|
||||
func (_ *ReqProxyConds) HandleConnectFunc(_ func(string, *ProxyCtx) (*ConnectAction, string)) {}
|
||||
|
||||
func (_ *ReqProxyConds) HijackConnect(_ func(*http.Request, net.Conn, *ProxyCtx)) {}
|
||||
|
||||
type RespCondition interface {
|
||||
HandleResp(_ *http.Response, _ *ProxyCtx) bool
|
||||
}
|
||||
|
||||
type RespHandler interface {
|
||||
Handle(_ *http.Response, _ *ProxyCtx) *http.Response
|
||||
}
|
||||
|
||||
type RoundTripper interface {
|
||||
RoundTrip(_ *http.Request, _ *ProxyCtx) (*http.Response, error)
|
||||
}
|
||||
|
||||
func TextResponse(_ *http.Request, _ string) *http.Response {
|
||||
return nil
|
||||
}
|
||||
5
ql/test/library-tests/semmle/go/frameworks/ElazarlGoproxy/vendor/modules.txt
vendored
Normal file
5
ql/test/library-tests/semmle/go/frameworks/ElazarlGoproxy/vendor/modules.txt
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# github.com/elazarl/goproxy v0.0.0-20201021153353-00ad82a08272
|
||||
## explicit
|
||||
github.com/elazarl/goproxy
|
||||
# github.com/github/depstubber v0.0.0-20201214172518-12c3da4b7c9d
|
||||
## explicit
|
||||
Reference in New Issue
Block a user