Add models for github.com/elazarl/goproxy

This commit is contained in:
Sauyon Lee
2020-12-18 01:38:35 -08:00
parent b0ddf4b68b
commit 4712afae83
10 changed files with 410 additions and 0 deletions

View File

@@ -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

View 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 }
}
}

View File

@@ -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
)

View File

@@ -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() {
}

View File

@@ -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"
)
}
}

View File

@@ -0,0 +1,2 @@
bin
*.swp

View 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.

View 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
}

View 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