Merge pull request #573 from npesaresi/feature/SSRF

Yet another SSRF query for Golang
This commit is contained in:
Chris Smowton
2021-11-04 17:36:21 +00:00
committed by GitHub
18 changed files with 1897 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
package main
import (
"net/http"
)
func handler(w http.ResponseWriter, req *http.Request) {
target := req.FormValue("target")
// BAD: `target` is controlled by the attacker
resp, err := http.Get("https://example.com/current_api/" + target)
if err != nil {
// error handling
}
// process request response
use(resp)
}

View File

@@ -0,0 +1,50 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Directly incorporating user input into an HTTP request without validating the input can facilitate
server side request forgery attacks, where the attacker controls the request target.
</p>
</overview>
<recommendation>
<p>
To guard against server side request forgery, it is advisable to avoid putting user input directly into a
network request. If using user input is necessary, then it must be validated. It is recommended to only allow
user input consisting of alphanumeric characters. Simply URL-encoding other chracters is not always a solution,
for example because a downstream entity that is itself vulnerable may decode again before forwarding the request.
</p>
</recommendation>
<example>
<p>
The following example shows an HTTP request parameter being used directly in a URL request without
validating the input, which facilitates an SSRF attack. The request <code>http.Get("https://example.com/current_api/"+target)</code> is
vulnerable since attackers can choose the value of <code>target</code> to be anything they want. For
instance, the attacker can choose <code>"../super_secret_api"</code> as the target, causing the
URL to become <code>"https://example.com/super_secret_api"</code>.
</p>
<p>
A request to <code>https://example.com/super_secret_api</code> may be problematic if that api is not
meant to be directly accessible from the attacker's machine.
</p>
<sample src="SSRF.go"/>
<p>
One way to remedy the problem is to validate the user input to only allow alphanumeric values:
</p>
<sample src="SSRFGood.go"/>
</example>
<references>
<li>OWASP: <a href="https://www.owasp.org/www-community/attacks/Server_Side_Request_Forgery">SSRF</a></li>
</references>
</qhelp>

View File

@@ -0,0 +1,21 @@
/**
* @name Uncontrolled data used in network request
* @description Sending network requests with user-controlled data allows for request forgery attacks.
* @kind path-problem
* @problem.severity error
* @precision high
* @tags security
* external/cwe/cwe-918
*/
import go
import SSRF
import DataFlow::PathGraph
from
ServerSideRequestForgery::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink,
DataFlow::Node request
where
cfg.hasFlowPath(source, sink) and
request = sink.getNode().(ServerSideRequestForgery::Sink).getARequest()
select request, source, sink, "The URL of this request depends on a user-provided value"

View File

@@ -0,0 +1,164 @@
/**
* Provides a taint-tracking configuration for reasoning about request forgery
* (SSRF) vulnerabilities.
*/
import go
/**
* Provides a taint-tracking configuration for reasoning about request forgery
* (SSRF) vulnerabilities.
*/
module ServerSideRequestForgery {
private import semmle.go.frameworks.Gin
private import validator
private import semmle.go.security.UrlConcatenation
private import semmle.go.dataflow.barrierguardutil.RegexpCheck
private import semmle.go.dataflow.Properties
/**
* A taint-tracking configuration for reasoning about request forgery.
*/
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "SSRF" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
// propagate to a URL when its host is assigned to
exists(Write w, Field f, SsaWithFields v | f.hasQualifiedName("net/url", "URL", "Host") |
w.writesField(v.getAUse(), f, pred) and succ = v.getAUse()
)
}
override predicate isSanitizer(DataFlow::Node node) {
super.isSanitizer(node) or
node instanceof Sanitizer
}
override predicate isSanitizerOut(DataFlow::Node node) {
super.isSanitizerOut(node) or
node instanceof SanitizerEdge
}
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
super.isSanitizerGuard(guard) or guard instanceof SanitizerGuard
}
}
/** A data flow source for request forgery vulnerabilities. */
abstract class Source extends DataFlow::Node { }
/** A data flow sink for request forgery vulnerabilities. */
abstract class Sink extends DataFlow::Node {
/** Gets a request that uses this sink. */
abstract DataFlow::Node getARequest();
/**
* Gets the name of a part of the request that may be tainted by this sink,
* such as the URL or the host.
*/
abstract string getKind();
}
/** A sanitizer for request forgery vulnerabilities. */
abstract class Sanitizer extends DataFlow::Node { }
/** An outgoing sanitizer edge for request forgery vulnerabilities. */
abstract class SanitizerEdge extends DataFlow::Node { }
/**
* A sanitizer guard for request forgery vulnerabilities.
*/
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
/**
* An user controlled input, considered as a flow source for request forgery.
*/
class UntrustedFlowAsSource extends Source instanceof UntrustedFlowSource { }
/**
* The URL of an HTTP request, viewed as a sink for request forgery.
*/
private class ClientRequestUrlAsSink extends Sink {
HTTP::ClientRequest request;
ClientRequestUrlAsSink() { this = request.getUrl() }
override DataFlow::Node getARequest() { result = request }
override string getKind() { result = "URL" }
}
/**
* The URL of a WebSocket request, viewed as a sink for request forgery.
*/
class WebSocketCallAsSink extends Sink {
WebSocketRequestCall request;
WebSocketCallAsSink() { this = request.getRequestUrl() }
override DataFlow::Node getARequest() { result = request }
override string getKind() { result = "WebSocket URL" }
}
/**
* Result value of prepending a string that prevents any value from controlling the
* host of a URL.
*/
private class PathSanitizer extends SanitizerEdge {
PathSanitizer() { sanitizingPrefixEdge(this, _) }
}
/**
* A call to a regexp match function, considered as a barrier guard for sanitizing untrusted URLs.
*
* This is overapproximate: we do not attempt to reason about the correctness of the regexp.
*/
class RegexpCheckAsBarrierGuard extends RegexpCheck, SanitizerGuard { }
/**
* An equality check comparing a data-flow node against a constant string, considered as
* a barrier guard for sanitizing untrusted URLs.
*/
class EqualityAsSanitizerGuard extends SanitizerGuard, DataFlow::EqualityTestNode {
DataFlow::Node url;
EqualityAsSanitizerGuard() {
exists(this.getAnOperand().getStringValue()) and
url = this.getAnOperand()
}
override predicate checks(Expr e, boolean outcome) {
e = url.asExpr() and outcome = this.getPolarity()
}
}
/**
* If the tainted variable is a boolean or has numeric type is not possible to exploit a SSRF
*/
class NumSanitizer extends Sanitizer {
NumSanitizer() {
this.getType() instanceof NumericType or
this.getType() instanceof BoolType
}
}
/**
* When we receive a body from a request, we can use certain tags on our struct's fields to hint
* the binding function to run some validations for that field. If these binding functions returns
* no error, then we consider these fields safe for SSRF.
*/
class BodySanitizer extends Sanitizer instanceof CheckedAlphanumericStructFieldRead { }
/**
* The method Var of package validator is a sanitizer guard only if the check
* of the error binding exists, and the tag to check is one of "alpha", "alphanum", "alphaunicode", "alphanumunicode", "number", "numeric".
*/
class ValidatorAsSanitizer extends SanitizerGuard instanceof ValidatorVarCheck {
override predicate checks(Expr e, boolean branch) { this.checks(e, branch) }
}
}

View File

@@ -0,0 +1,20 @@
package main
import (
"github.com/go-playground/validator"
"net/http"
)
func goodHandler(w http.ResponseWriter, req *http.Request) {
validate := validator.New()
target := req.FormValue("target")
if validate.Var(target, "alphanum") == nil {
// GOOD: `target` is alphanumeric
resp, err := http.Get("https://example.com/current_api/" + target)
if err != nil {
// error handling
}
// process request response
use(resp)
}
}

View File

@@ -0,0 +1,159 @@
import go
private import semmle.go.dataflow.Properties
/**
* Holds if `validationKind` is a validation kind that restricts to alphanumeric characters,
* which we consider safe for use in a URL.
*/
private predicate isAlphanumericValidationKind(string validationKind) {
validationKind in [
"alpha", "alphanum", "alphaunicode", "alphanumunicode", "number", "numeric", "uuid"
]
}
private string getKeyAndValuesRegex() { result = "([a-zA-Z0-9]+):\"([a-zA-Z0-9,]+)\"" }
/**
* A struct field with json tags like `key:"value1,value2"`.
*/
class FieldWithTags extends FieldDecl {
FieldWithTags() { this.getTag().toString().regexpMatch("`([a-zA-Z0-9]+:\"[a-zA-Z0-9,]+\" *)+`") }
/**
* Holds if this field's tag maps `key` to `value`.
* For example: the tag `json:"word" binding:"required,alpha"` yields `key: "json", value: "word"`
* and `key: "binding" values: "required","alpha"`.
*/
predicate getTagByKeyValue(string key, string value) {
exists(string tag, string key_value, string values |
this.getTag().toString() = tag and
// Each key_value is like key:"value1,value2"
tag.regexpFind(getKeyAndValuesRegex(), _, _) = key_value and
// key is the "key" from key:"value1,value2"
key_value.regexpCapture(getKeyAndValuesRegex(), 1) = key and
// values are the value1,value2 (without the quotation marks) from key:"value1,value2"
key_value.regexpCapture(getKeyAndValuesRegex(), 2) = values and
// value is value1 or value2 from key:"value1,value2"
values.regexpFind("[a-zA-Z0-9]+", _, _) = value
)
}
}
/**
* A node that reads from a field with a tag indicating it
* must be alphanumeric (for example, having the tag `binding:"alpha"`).
*/
class AlphanumericStructFieldRead extends DataFlow::Node {
string key;
AlphanumericStructFieldRead() {
exists(FieldWithTags decl, Field field, string tag |
this = field.getARead() and
field.getDeclaration() = decl.getNameExpr(0) and
decl.getTagByKeyValue(key, tag) and
isAlphanumericValidationKind(tag)
)
}
string getKey() { result = key }
}
/**
* A node that is considered safe because it (a) reads a field with a tag indicating it should be
* alphanumeric, and (b) is guarded by a call to a validation function checking that it really
* is alphanumeric.
*
* See `AlphanumericStructFieldRead` and `isAlphanumericValidationKind` for supported tags.
* See `StructValidationFunction` for supported binding functions.
*/
class CheckedAlphanumericStructFieldRead extends AlphanumericStructFieldRead {
CheckedAlphanumericStructFieldRead() {
exists(StructValidationFunction guard, SelectorExpr selector |
guard.getAGuardedNode().asExpr() = selector.getBase() and
selector = this.asExpr() and
this.getKey() = guard.getValidationKindKey()
)
}
}
/**
* A function that validates a struct, checking that fields conform to restrictions given as a tag.
*
* The Gin `Context.Bind` family of functions apply checks according to a `binding:` tag, and the
* Go-Playground Validator checks fields that have a `validate:` tag.
*/
private class StructValidationFunction extends DataFlow::BarrierGuard, DataFlow::EqualityTestNode {
Expr checked;
boolean safeOutcome;
string validationKindKey;
StructValidationFunction() {
exists(Function bindFunction, DataFlow::CallNode bindCall, DataFlow::Node resultErr |
(
// Gin call
bindFunction
.(Method)
.hasQualifiedName("github.com/gin-gonic/gin", "Context",
[
"BindJSON", "MustBindWith", "BindWith", "Bind", "ShouldBind", "ShouldBindBodyWith",
"ShouldBindJSON", "ShouldBindWith"
]) and
validationKindKey = "binding"
or
// Validator Struct
bindFunction
.(Method)
.hasQualifiedName("github.com/go-playground/validator", "Validate", "Struct") and
validationKindKey = "validate"
) and
bindCall = bindFunction.getACall() and
checked = dereference(bindCall.getAnArgument()) and
resultErr = bindCall.getResult().getASuccessor*() and
nilProperty().checkOn(this, safeOutcome, resultErr)
)
}
override predicate checks(Expr e, boolean branch) { e = checked and branch = safeOutcome }
/**
* Returns the struct tag key from which this validation function draws its validation kind.
*
* For example, if this returns `xyz` then this function looks for a struct tag like
* `` mustBeNumeric string `xyz:"numeric"` ``
*/
string getValidationKindKey() { result = validationKindKey }
}
/**
* If `nd` is an address-of expression `&a`, returns expressions `&a` and `a`. Otherwise, returns `nd` as-is.
*/
private Expr dereference(DataFlow::Node nd) {
nd.asExpr().(AddressExpr).getOperand() = result
or
nd.asExpr() = result
}
/**
* A validation performed by package `validator`'s method `Var` to check that an expression is
* alphanumeric (see `isAlphanumericValidationKind` for more information) sanitizes guarded uses
* of the same variable.
*/
class ValidatorVarCheck extends DataFlow::BarrierGuard, DataFlow::EqualityTestNode {
DataFlow::CallNode callToValidator;
boolean outcome;
ValidatorVarCheck() {
exists(Method validatorMethod, DataFlow::Node resultErr |
validatorMethod.hasQualifiedName("github.com/go-playground/validator", "Validate", "Var") and
callToValidator = validatorMethod.getACall() and
isAlphanumericValidationKind(callToValidator.getArgument(1).getStringValue()) and
resultErr = callToValidator.getResult().getASuccessor*() and
nilProperty().checkOn(this, outcome, resultErr)
)
}
override predicate checks(Expr e, boolean branch) {
callToValidator.getArgument(0).asExpr() = e and
branch = outcome
}
}

View File

@@ -0,0 +1,73 @@
edges
| builtin.go:19:12:19:34 | call to FormValue : string | builtin.go:22:21:22:62 | ...+... |
| builtin.go:83:21:83:31 | call to Referer : string | builtin.go:88:27:88:40 | untrustedInput |
| builtin.go:97:21:97:31 | call to Referer : string | builtin.go:101:36:101:49 | untrustedInput |
| builtin.go:111:21:111:31 | call to Referer : string | builtin.go:114:15:114:28 | untrustedInput |
| builtin.go:129:21:129:31 | call to Referer : string | builtin.go:132:38:132:51 | untrustedInput |
| new-tests.go:26:26:26:30 | &... : pointer type | new-tests.go:31:11:31:57 | call to Sprintf |
| new-tests.go:26:26:26:30 | &... : pointer type | new-tests.go:32:11:32:57 | call to Sprintf |
| new-tests.go:26:26:26:30 | &... : pointer type | new-tests.go:35:12:35:58 | call to Sprintf |
| new-tests.go:39:18:39:30 | call to Param : string | new-tests.go:47:11:47:46 | ...+... |
| new-tests.go:49:18:49:30 | call to Query : string | new-tests.go:50:11:50:46 | ...+... |
| new-tests.go:62:31:62:38 | selection of Body : ReadCloser | new-tests.go:68:11:68:57 | call to Sprintf |
| new-tests.go:62:31:62:38 | selection of Body : ReadCloser | new-tests.go:69:11:69:57 | call to Sprintf |
| new-tests.go:62:31:62:38 | selection of Body : ReadCloser | new-tests.go:74:12:74:58 | call to Sprintf |
| new-tests.go:78:18:78:24 | selection of URL : pointer type | new-tests.go:79:11:79:46 | ...+... |
| new-tests.go:81:37:81:43 | implicit dereference : URL | new-tests.go:81:37:81:43 | implicit dereference : URL |
| new-tests.go:81:37:81:43 | implicit dereference : URL | new-tests.go:81:37:81:43 | selection of URL : pointer type |
| new-tests.go:81:37:81:43 | implicit dereference : URL | new-tests.go:82:11:82:46 | ...+... |
| new-tests.go:81:37:81:43 | selection of URL : pointer type | new-tests.go:81:37:81:43 | implicit dereference : URL |
| new-tests.go:81:37:81:43 | selection of URL : pointer type | new-tests.go:81:37:81:43 | selection of URL : pointer type |
| new-tests.go:81:37:81:43 | selection of URL : pointer type | new-tests.go:82:11:82:46 | ...+... |
| new-tests.go:86:10:86:20 | call to Vars : map type | new-tests.go:88:11:88:46 | ...+... |
| new-tests.go:95:18:95:45 | call to URLParam : string | new-tests.go:96:11:96:46 | ...+... |
nodes
| builtin.go:19:12:19:34 | call to FormValue : string | semmle.label | call to FormValue : string |
| builtin.go:22:21:22:62 | ...+... | semmle.label | ...+... |
| builtin.go:83:21:83:31 | call to Referer : string | semmle.label | call to Referer : string |
| builtin.go:88:27:88:40 | untrustedInput | semmle.label | untrustedInput |
| builtin.go:97:21:97:31 | call to Referer : string | semmle.label | call to Referer : string |
| builtin.go:101:36:101:49 | untrustedInput | semmle.label | untrustedInput |
| builtin.go:111:21:111:31 | call to Referer : string | semmle.label | call to Referer : string |
| builtin.go:114:15:114:28 | untrustedInput | semmle.label | untrustedInput |
| builtin.go:129:21:129:31 | call to Referer : string | semmle.label | call to Referer : string |
| builtin.go:132:38:132:51 | untrustedInput | semmle.label | untrustedInput |
| new-tests.go:26:26:26:30 | &... : pointer type | semmle.label | &... : pointer type |
| new-tests.go:31:11:31:57 | call to Sprintf | semmle.label | call to Sprintf |
| new-tests.go:32:11:32:57 | call to Sprintf | semmle.label | call to Sprintf |
| new-tests.go:35:12:35:58 | call to Sprintf | semmle.label | call to Sprintf |
| new-tests.go:39:18:39:30 | call to Param : string | semmle.label | call to Param : string |
| new-tests.go:47:11:47:46 | ...+... | semmle.label | ...+... |
| new-tests.go:49:18:49:30 | call to Query : string | semmle.label | call to Query : string |
| new-tests.go:50:11:50:46 | ...+... | semmle.label | ...+... |
| new-tests.go:62:31:62:38 | selection of Body : ReadCloser | semmle.label | selection of Body : ReadCloser |
| new-tests.go:68:11:68:57 | call to Sprintf | semmle.label | call to Sprintf |
| new-tests.go:69:11:69:57 | call to Sprintf | semmle.label | call to Sprintf |
| new-tests.go:74:12:74:58 | call to Sprintf | semmle.label | call to Sprintf |
| new-tests.go:78:18:78:24 | selection of URL : pointer type | semmle.label | selection of URL : pointer type |
| new-tests.go:79:11:79:46 | ...+... | semmle.label | ...+... |
| new-tests.go:81:37:81:43 | implicit dereference : URL | semmle.label | implicit dereference : URL |
| new-tests.go:81:37:81:43 | selection of URL : pointer type | semmle.label | selection of URL : pointer type |
| new-tests.go:82:11:82:46 | ...+... | semmle.label | ...+... |
| new-tests.go:86:10:86:20 | call to Vars : map type | semmle.label | call to Vars : map type |
| new-tests.go:88:11:88:46 | ...+... | semmle.label | ...+... |
| new-tests.go:95:18:95:45 | call to URLParam : string | semmle.label | call to URLParam : string |
| new-tests.go:96:11:96:46 | ...+... | semmle.label | ...+... |
#select
| builtin.go:22:12:22:63 | call to Get | builtin.go:19:12:19:34 | call to FormValue : string | builtin.go:22:21:22:62 | ...+... | The URL of this request depends on a user-provided value |
| builtin.go:88:12:88:53 | call to Dial | builtin.go:83:21:83:31 | call to Referer : string | builtin.go:88:27:88:40 | untrustedInput | The URL of this request depends on a user-provided value |
| builtin.go:102:13:102:40 | call to DialConfig | builtin.go:97:21:97:31 | call to Referer : string | builtin.go:101:36:101:49 | untrustedInput | The URL of this request depends on a user-provided value |
| builtin.go:114:3:114:39 | call to Dial | builtin.go:111:21:111:31 | call to Referer : string | builtin.go:114:15:114:28 | untrustedInput | The URL of this request depends on a user-provided value |
| builtin.go:132:3:132:62 | call to DialContext | builtin.go:129:21:129:31 | call to Referer : string | builtin.go:132:38:132:51 | untrustedInput | The URL of this request depends on a user-provided value |
| new-tests.go:31:2:31:58 | call to Get | new-tests.go:26:26:26:30 | &... : pointer type | new-tests.go:31:11:31:57 | call to Sprintf | The URL of this request depends on a user-provided value |
| new-tests.go:32:2:32:58 | call to Get | new-tests.go:26:26:26:30 | &... : pointer type | new-tests.go:32:11:32:57 | call to Sprintf | The URL of this request depends on a user-provided value |
| new-tests.go:35:3:35:59 | call to Get | new-tests.go:26:26:26:30 | &... : pointer type | new-tests.go:35:12:35:58 | call to Sprintf | The URL of this request depends on a user-provided value |
| new-tests.go:47:2:47:47 | call to Get | new-tests.go:39:18:39:30 | call to Param : string | new-tests.go:47:11:47:46 | ...+... | The URL of this request depends on a user-provided value |
| new-tests.go:50:2:50:47 | call to Get | new-tests.go:49:18:49:30 | call to Query : string | new-tests.go:50:11:50:46 | ...+... | The URL of this request depends on a user-provided value |
| new-tests.go:68:2:68:58 | call to Get | new-tests.go:62:31:62:38 | selection of Body : ReadCloser | new-tests.go:68:11:68:57 | call to Sprintf | The URL of this request depends on a user-provided value |
| new-tests.go:69:2:69:58 | call to Get | new-tests.go:62:31:62:38 | selection of Body : ReadCloser | new-tests.go:69:11:69:57 | call to Sprintf | The URL of this request depends on a user-provided value |
| new-tests.go:74:3:74:59 | call to Get | new-tests.go:62:31:62:38 | selection of Body : ReadCloser | new-tests.go:74:12:74:58 | call to Sprintf | The URL of this request depends on a user-provided value |
| new-tests.go:79:2:79:47 | call to Get | new-tests.go:78:18:78:24 | selection of URL : pointer type | new-tests.go:79:11:79:46 | ...+... | The URL of this request depends on a user-provided value |
| new-tests.go:82:2:82:47 | call to Get | new-tests.go:81:37:81:43 | selection of URL : pointer type | new-tests.go:82:11:82:46 | ...+... | The URL of this request depends on a user-provided value |
| new-tests.go:88:2:88:47 | call to Get | new-tests.go:86:10:86:20 | call to Vars : map type | new-tests.go:88:11:88:46 | ...+... | The URL of this request depends on a user-provided value |
| new-tests.go:96:2:96:47 | call to Get | new-tests.go:95:18:95:45 | call to URLParam : string | new-tests.go:96:11:96:46 | ...+... | The URL of this request depends on a user-provided value |

View File

@@ -0,0 +1 @@
experimental/CWE-918/SSRF.ql

View File

@@ -0,0 +1,147 @@
package main
//go:generate depstubber -vendor github.com/gorilla/websocket Dialer
//go:generate depstubber -vendor golang.org/x/net/websocket "" Dial,NewConfig,DialConfig
import (
"context"
"fmt"
"log"
"net/http"
"regexp"
"strings"
gorilla "github.com/gorilla/websocket"
"golang.org/x/net/websocket"
)
func handler(w http.ResponseWriter, req *http.Request) {
target := req.FormValue("target")
// BAD: `target` is controlled by the attacker
_, err := http.Get("https://" + target + ".example.com/data/")
if err != nil {
// error handling
}
// process request response
}
func handler1(w http.ResponseWriter, req *http.Request) {
target := req.FormValue("target")
var subdomain string
if target == "EU" {
subdomain = "europe"
} else {
subdomain = "world"
}
// GOOD: `subdomain` is controlled by the server
_, err := http.Get("https://" + subdomain + ".example.com/data/")
if err != nil {
// error handling
}
// process request response
}
func test() {
http.HandleFunc("/ex0", func(w http.ResponseWriter, r *http.Request) {
untrustedInput := r.Referer()
origin := "http://localhost/"
untrustedInputTrimmed := strings.TrimRight(untrustedInput, "\n\r")
if untrustedInputTrimmed == "ws://localhost:12345/ws" {
// good as input is checked against fixed set of urls.
ws, _ := websocket.Dial(untrustedInputTrimmed, "", origin) // OK
var msg = make([]byte, 512)
var n int
n, _ = ws.Read(msg)
fmt.Printf("Received: %s.\n", msg[:n])
}
})
// x net websocket DialConfig good
http.HandleFunc("/ex1", func(w http.ResponseWriter, r *http.Request) {
untrustedInput := r.Referer()
origin := "http://localhost/"
// good as input is tested against a regex
if m, _ := regexp.MatchString("ws://localhost:12345/*", untrustedInput); m {
config, _ := websocket.NewConfig(untrustedInput, origin) // OK? Regex
ws2, _ := websocket.DialConfig(config)
var msg = make([]byte, 512)
var n int
n, _ = ws2.Read(msg)
fmt.Printf("Received: %s.\n", msg[:n])
}
})
// x net websocket dial bad
http.HandleFunc("/ex2", func(w http.ResponseWriter, r *http.Request) {
untrustedInput := r.Referer()
origin := "http://localhost/"
// bad as input is directly passed to dial function
ws, _ := websocket.Dial(untrustedInput, "", origin) // SSRF
var msg = make([]byte, 512)
var n int
n, _ = ws.Read(msg)
fmt.Printf("Received: %s.\n", msg[:n])
})
// x net websocket dialConfig bad
http.HandleFunc("/ex3", func(w http.ResponseWriter, r *http.Request) {
untrustedInput := r.Referer()
origin := "http://localhost/"
// bad as input is directly used
config, _ := websocket.NewConfig(untrustedInput, origin) // SSRF
ws2, _ := websocket.DialConfig(config)
var msg = make([]byte, 512)
var n int
n, _ = ws2.Read(msg)
fmt.Printf("Received: %s.\n", msg[:n])
})
// gorilla websocket Dialer.Dial bad
http.HandleFunc("/ex6", func(w http.ResponseWriter, r *http.Request) {
untrustedInput := r.Referer()
dialer := gorilla.Dialer{}
dialer.Dial(untrustedInput, r.Header) //SSRF
})
// gorilla websocket Dialer.Dial good
http.HandleFunc("/ex7", func(w http.ResponseWriter, r *http.Request) {
untrustedInput := r.Referer()
if untrustedInput == "localhost" {
dialer := gorilla.Dialer{}
dialer.Dial(untrustedInput, r.Header) //OK
}
})
// gorilla websocket Dialer.DialContext bad
http.HandleFunc("/ex8", func(w http.ResponseWriter, r *http.Request) {
untrustedInput := r.Referer()
dialer := gorilla.Dialer{}
dialer.DialContext(context.TODO(), untrustedInput, r.Header) //SSRF
})
// gorilla websocket Dialer.DialContext good
http.HandleFunc("/ex9", func(w http.ResponseWriter, r *http.Request) {
untrustedInput := r.Referer()
if untrustedInput == "localhost" {
dialer := gorilla.Dialer{}
dialer.DialContext(context.TODO(), untrustedInput, r.Header) //OK
}
})
log.Println(http.ListenAndServe(":80", nil))
}

View File

@@ -0,0 +1,15 @@
module github.com/mercadolibre/fury_opensource-codeql/go/test/SSRF
go 1.13
require (
github.com/gin-gonic/gin v1.6.3
github.com/go-chi/chi v4.1.2+incompatible
github.com/gorilla/mux v1.8.0
github.com/gorilla/websocket v1.4.2
github.com/kr/text v0.2.0 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/stretchr/testify v1.6.0 // indirect
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
)

View File

@@ -0,0 +1,100 @@
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strconv"
"strings"
"github.com/gin-gonic/gin"
"github.com/go-chi/chi"
"github.com/go-playground/validator"
"github.com/gorilla/mux"
)
func HandlerGin(c *gin.Context) {
var body struct {
integer int
float float32
boolean bool
word string
safe string `binding:"alphanum"`
}
err := c.ShouldBindJSON(&body)
http.Get(fmt.Sprintf("http://example.com/%d", body.integer)) // OK
http.Get(fmt.Sprintf("http://example.com/%v", body.float)) // OK
http.Get(fmt.Sprintf("http://example.com/%v", body.boolean)) // OK
http.Get(fmt.Sprintf("http://example.com/%s", body.word)) // SSRF
http.Get(fmt.Sprintf("http://example.com/%s", body.safe)) // SSRF
if err == nil {
http.Get(fmt.Sprintf("http://example.com/%s", body.word)) // SSRF
http.Get(fmt.Sprintf("http://example.com/%s", body.safe)) // OK
}
taintedParam := c.Param("id")
validate := validator.New()
err = validate.Var(taintedParam, "alpha")
if err == nil {
http.Get("http://example.com/" + taintedParam) // OK
}
http.Get("http://example.com/" + taintedParam) //SSRF
taintedQuery := c.Query("id")
http.Get("http://example.com/" + taintedQuery) //SSRF
}
func HandlerHttp(req *http.Request) {
//HTTP
var body struct {
integer int
float float32
boolean bool
word string
safe string `validate:"alphanum"`
}
reqBody, _ := ioutil.ReadAll(req.Body)
json.Unmarshal(reqBody, &body)
http.Get(fmt.Sprintf("http://example.com/%d", body.integer)) // OK
http.Get(fmt.Sprintf("http://example.com/%v", body.float)) // OK
http.Get(fmt.Sprintf("http://example.com/%v", body.boolean)) // OK
http.Get(fmt.Sprintf("http://example.com/%s", body.word)) // SSRF
http.Get(fmt.Sprintf("http://example.com/%s", body.safe)) // SSRF
validate := validator.New()
err := validate.Struct(body)
if err == nil {
http.Get(fmt.Sprintf("http://example.com/%s", body.word)) // SSRF
http.Get(fmt.Sprintf("http://example.com/%s", body.safe)) // OK
}
taintedQuery := req.URL.Query().Get("param1")
http.Get("http://example.com/" + taintedQuery) // SSRF
taintedParam := strings.TrimPrefix(req.URL.Path, "/example-path/")
http.Get("http://example.com/" + taintedParam) // SSRF
}
func HandlerMux(r *http.Request) {
vars := mux.Vars(r)
taintedParam := vars["id"]
http.Get("http://example.com/" + taintedParam) // SSRF
numericID, _ := strconv.Atoi(taintedParam)
http.Get(fmt.Sprintf("http://example.com/%d", numericID)) // OK
}
func HandlerChi(r *http.Request) {
taintedParam := chi.URLParam(r, "articleID")
http.Get("http://example.com/" + taintedParam) // SSRF
b, _ := strconv.ParseBool(taintedParam)
http.Get(fmt.Sprintf("http://example.com/%t", b)) // OK
}

View File

@@ -0,0 +1,436 @@
// Code generated by depstubber. DO NOT EDIT.
// This is a simple stub for github.com/gin-gonic/gin, strictly for use in testing.
// See the LICENSE file for information about the licensing of the original library.
// Source: github.com/gin-gonic/gin (exports: Context; functions: )
// Package gin is a stub of github.com/gin-gonic/gin, generated by depstubber.
package gin
import (
bufio "bufio"
io "io"
multipart "mime/multipart"
net "net"
http "net/http"
time "time"
)
type Context struct {
Request *http.Request
Writer ResponseWriter
Params Params
Keys map[string]interface{}
Errors interface{}
Accepted []string
}
func (_ *Context) Abort() {}
func (_ *Context) AbortWithError(_ int, _ error) *Error {
return nil
}
func (_ *Context) AbortWithStatus(_ int) {}
func (_ *Context) AbortWithStatusJSON(_ int, _ interface{}) {}
func (_ *Context) AsciiJSON(_ int, _ interface{}) {}
func (_ *Context) Bind(_ interface{}) error {
return nil
}
func (_ *Context) BindHeader(_ interface{}) error {
return nil
}
func (_ *Context) BindJSON(_ interface{}) error {
return nil
}
func (_ *Context) BindQuery(_ interface{}) error {
return nil
}
func (_ *Context) BindUri(_ interface{}) error {
return nil
}
func (_ *Context) BindWith(_ interface{}, _ interface{}) error {
return nil
}
func (_ *Context) BindXML(_ interface{}) error {
return nil
}
func (_ *Context) BindYAML(_ interface{}) error {
return nil
}
func (_ *Context) ClientIP() string {
return ""
}
func (_ *Context) ContentType() string {
return ""
}
func (_ *Context) Cookie(_ string) (string, error) {
return "", nil
}
func (_ *Context) Copy() *Context {
return nil
}
func (_ *Context) Data(_ int, _ string, _ []byte) {}
func (_ *Context) DataFromReader(_ int, _ int64, _ string, _ io.Reader, _ map[string]string) {}
func (_ *Context) Deadline() (time.Time, bool) {
return time.Time{}, false
}
func (_ *Context) DefaultPostForm(_ string, _ string) string {
return ""
}
func (_ *Context) DefaultQuery(_ string, _ string) string {
return ""
}
func (_ *Context) Done() <-chan struct{} {
return nil
}
func (_ *Context) Err() error {
return nil
}
func (_ *Context) Error(_ error) *Error {
return nil
}
func (_ *Context) File(_ string) {}
func (_ *Context) FileAttachment(_ string, _ string) {}
func (_ *Context) FileFromFS(_ string, _ http.FileSystem) {}
func (_ *Context) FormFile(_ string) (*multipart.FileHeader, error) {
return nil, nil
}
func (_ *Context) FullPath() string {
return ""
}
func (_ *Context) Get(_ string) (interface{}, bool) {
return nil, false
}
func (_ *Context) GetBool(_ string) bool {
return false
}
func (_ *Context) GetDuration(_ string) time.Duration {
return 0
}
func (_ *Context) GetFloat64(_ string) float64 {
return 0
}
func (_ *Context) GetHeader(_ string) string {
return ""
}
func (_ *Context) GetInt(_ string) int {
return 0
}
func (_ *Context) GetInt64(_ string) int64 {
return 0
}
func (_ *Context) GetPostForm(_ string) (string, bool) {
return "", false
}
func (_ *Context) GetPostFormArray(_ string) ([]string, bool) {
return nil, false
}
func (_ *Context) GetPostFormMap(_ string) (map[string]string, bool) {
return nil, false
}
func (_ *Context) GetQuery(_ string) (string, bool) {
return "", false
}
func (_ *Context) GetQueryArray(_ string) ([]string, bool) {
return nil, false
}
func (_ *Context) GetQueryMap(_ string) (map[string]string, bool) {
return nil, false
}
func (_ *Context) GetRawData() ([]byte, error) {
return nil, nil
}
func (_ *Context) GetString(_ string) string {
return ""
}
func (_ *Context) GetStringMap(_ string) map[string]interface{} {
return nil
}
func (_ *Context) GetStringMapString(_ string) map[string]string {
return nil
}
func (_ *Context) GetStringMapStringSlice(_ string) map[string][]string {
return nil
}
func (_ *Context) GetStringSlice(_ string) []string {
return nil
}
func (_ *Context) GetTime(_ string) time.Time {
return time.Time{}
}
func (_ *Context) HTML(_ int, _ string, _ interface{}) {}
func (_ *Context) Handler() HandlerFunc {
return nil
}
func (_ *Context) HandlerName() string {
return ""
}
func (_ *Context) HandlerNames() []string {
return nil
}
func (_ *Context) Header(_ string, _ string) {}
func (_ *Context) IndentedJSON(_ int, _ interface{}) {}
func (_ *Context) IsAborted() bool {
return false
}
func (_ *Context) IsWebsocket() bool {
return false
}
func (_ *Context) JSON(_ int, _ interface{}) {}
func (_ *Context) JSONP(_ int, _ interface{}) {}
func (_ *Context) MultipartForm() (*multipart.Form, error) {
return nil, nil
}
func (_ *Context) MustBindWith(_ interface{}, _ interface{}) error {
return nil
}
func (_ *Context) MustGet(_ string) interface{} {
return nil
}
func (_ *Context) Negotiate(_ int, _ Negotiate) {}
func (_ *Context) NegotiateFormat(_ ...string) string {
return ""
}
func (_ *Context) Next() {}
func (_ *Context) Param(_ string) string {
return ""
}
func (_ *Context) PostForm(_ string) string {
return ""
}
func (_ *Context) PostFormArray(_ string) []string {
return nil
}
func (_ *Context) PostFormMap(_ string) map[string]string {
return nil
}
func (_ *Context) ProtoBuf(_ int, _ interface{}) {}
func (_ *Context) PureJSON(_ int, _ interface{}) {}
func (_ *Context) Query(_ string) string {
return ""
}
func (_ *Context) QueryArray(_ string) []string {
return nil
}
func (_ *Context) QueryMap(_ string) map[string]string {
return nil
}
func (_ *Context) Redirect(_ int, _ string) {}
func (_ *Context) Render(_ int, _ interface{}) {}
func (_ *Context) SSEvent(_ string, _ interface{}) {}
func (_ *Context) SaveUploadedFile(_ *multipart.FileHeader, _ string) error {
return nil
}
func (_ *Context) SecureJSON(_ int, _ interface{}) {}
func (_ *Context) Set(_ string, _ interface{}) {}
func (_ *Context) SetAccepted(_ ...string) {}
func (_ *Context) SetCookie(_ string, _ string, _ int, _ string, _ string, _ bool, _ bool) {}
func (_ *Context) SetSameSite(_ http.SameSite) {}
func (_ *Context) ShouldBind(_ interface{}) error {
return nil
}
func (_ *Context) ShouldBindBodyWith(_ interface{}, _ interface{}) error {
return nil
}
func (_ *Context) ShouldBindHeader(_ interface{}) error {
return nil
}
func (_ *Context) ShouldBindJSON(_ interface{}) error {
return nil
}
func (_ *Context) ShouldBindQuery(_ interface{}) error {
return nil
}
func (_ *Context) ShouldBindUri(_ interface{}) error {
return nil
}
func (_ *Context) ShouldBindWith(_ interface{}, _ interface{}) error {
return nil
}
func (_ *Context) ShouldBindXML(_ interface{}) error {
return nil
}
func (_ *Context) ShouldBindYAML(_ interface{}) error {
return nil
}
func (_ *Context) Status(_ int) {}
func (_ *Context) Stream(_ func(io.Writer) bool) bool {
return false
}
func (_ *Context) String(_ int, _ string, _ ...interface{}) {}
func (_ *Context) Value(_ interface{}) interface{} {
return nil
}
func (_ *Context) XML(_ int, _ interface{}) {}
func (_ *Context) YAML(_ int, _ interface{}) {}
type Error struct {
Err error
Type ErrorType
Meta interface{}
}
func (_ Error) Error() string {
return ""
}
func (_ *Error) IsType(_ ErrorType) bool {
return false
}
func (_ *Error) JSON() interface{} {
return nil
}
func (_ *Error) MarshalJSON() ([]byte, error) {
return nil, nil
}
func (_ *Error) SetMeta(_ interface{}) *Error {
return nil
}
func (_ *Error) SetType(_ ErrorType) *Error {
return nil
}
type ErrorType uint64
type HandlerFunc func(*Context)
type Negotiate struct {
Offered []string
HTMLName string
HTMLData interface{}
JSONData interface{}
XMLData interface{}
YAMLData interface{}
Data interface{}
}
type Param struct {
Key string
Value string
}
type Params []Param
func (_ Params) ByName(_ string) string {
return ""
}
func (_ Params) Get(_ string) (string, bool) {
return "", false
}
type ResponseWriter interface {
CloseNotify() <-chan bool
Flush()
Header() http.Header
Hijack() (net.Conn, *bufio.ReadWriter, error)
Pusher() http.Pusher
Size() int
Status() int
Write(_ []byte) (int, error)
WriteHeader(_ int)
WriteHeaderNow()
WriteString(_ string) (int, error)
Written() bool
}

View File

@@ -0,0 +1,16 @@
// 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: ; functions: URLParam)
// Package chi is a stub of github.com/go-chi/chi, generated by depstubber.
package chi
import (
http "net/http"
)
func URLParam(_ *http.Request, _ string) string {
return ""
}

View File

@@ -0,0 +1,160 @@
// Code generated by depstubber. DO NOT EDIT.
// This is a simple stub for github.com/go-playground/validator, strictly for use in testing.
// See the LICENSE file for information about the licensing of the original library.
// Source: github.com/go-playground/validator (exports: Validate; functions: New)
// Package validator is a stub of github.com/go-playground/validator, generated by depstubber.
package validator
import (
context "context"
reflect "reflect"
)
type CustomTypeFunc func(reflect.Value) interface{}
type FieldError interface {
ActualTag() string
Field() string
Kind() reflect.Kind
Namespace() string
Param() string
StructField() string
StructNamespace() string
Tag() string
Translate(_ interface{}) string
Type() reflect.Type
Value() interface{}
}
type FieldLevel interface {
ExtractType(_ reflect.Value) (reflect.Value, reflect.Kind, bool)
Field() reflect.Value
FieldName() string
GetStructFieldOK() (reflect.Value, reflect.Kind, bool)
GetStructFieldOK2() (reflect.Value, reflect.Kind, bool, bool)
GetStructFieldOKAdvanced(_ reflect.Value, _ string) (reflect.Value, reflect.Kind, bool)
GetStructFieldOKAdvanced2(_ reflect.Value, _ string) (reflect.Value, reflect.Kind, bool, bool)
GetTag() string
Param() string
Parent() reflect.Value
StructFieldName() string
Top() reflect.Value
}
type FilterFunc func([]byte) bool
type Func func(FieldLevel) bool
type FuncCtx func(context.Context, FieldLevel) bool
func New() *Validate {
return nil
}
type RegisterTranslationsFunc func(interface{}) error
type StructLevel interface {
Current() reflect.Value
ExtractType(_ reflect.Value) (reflect.Value, reflect.Kind, bool)
Parent() reflect.Value
ReportError(_ interface{}, _ string, _ string, _ string, _ string)
ReportValidationErrors(_ string, _ string, _ ValidationErrors)
Top() reflect.Value
Validator() *Validate
}
type StructLevelFunc func(StructLevel)
type StructLevelFuncCtx func(context.Context, StructLevel)
type TagNameFunc func(reflect.StructField) string
type TranslationFunc func(interface{}, FieldError) string
type Validate struct{}
func (_ *Validate) RegisterAlias(_ string, _ string) {}
func (_ *Validate) RegisterCustomTypeFunc(_ CustomTypeFunc, _ ...interface{}) {}
func (_ *Validate) RegisterStructValidation(_ StructLevelFunc, _ ...interface{}) {}
func (_ *Validate) RegisterStructValidationCtx(_ StructLevelFuncCtx, _ ...interface{}) {}
func (_ *Validate) RegisterTagNameFunc(_ TagNameFunc) {}
func (_ *Validate) RegisterTranslation(_ string, _ interface{}, _ RegisterTranslationsFunc, _ TranslationFunc) error {
return nil
}
func (_ *Validate) RegisterValidation(_ string, _ Func, _ ...bool) error {
return nil
}
func (_ *Validate) RegisterValidationCtx(_ string, _ FuncCtx, _ ...bool) error {
return nil
}
func (_ *Validate) SetTagName(_ string) {}
func (_ *Validate) Struct(_ interface{}) error {
return nil
}
func (_ *Validate) StructCtx(_ context.Context, _ interface{}) error {
return nil
}
func (_ *Validate) StructExcept(_ interface{}, _ ...string) error {
return nil
}
func (_ *Validate) StructExceptCtx(_ context.Context, _ interface{}, _ ...string) error {
return nil
}
func (_ *Validate) StructFiltered(_ interface{}, _ FilterFunc) error {
return nil
}
func (_ *Validate) StructFilteredCtx(_ context.Context, _ interface{}, _ FilterFunc) error {
return nil
}
func (_ *Validate) StructPartial(_ interface{}, _ ...string) error {
return nil
}
func (_ *Validate) StructPartialCtx(_ context.Context, _ interface{}, _ ...string) error {
return nil
}
func (_ *Validate) Var(_ interface{}, _ string) error {
return nil
}
func (_ *Validate) VarCtx(_ context.Context, _ interface{}, _ string) error {
return nil
}
func (_ *Validate) VarWithValue(_ interface{}, _ interface{}, _ string) error {
return nil
}
func (_ *Validate) VarWithValueCtx(_ context.Context, _ interface{}, _ interface{}, _ string) error {
return nil
}
type ValidationErrors []FieldError
func (_ ValidationErrors) Error() string {
return ""
}
func (_ ValidationErrors) Translate(_ interface{}) ValidationErrorsTranslations {
return nil
}
type ValidationErrorsTranslations map[string]string

View File

@@ -0,0 +1,252 @@
// Code generated by depstubber. DO NOT EDIT.
// This is a simple stub for github.com/gorilla/mux, strictly for use in testing.
// See the LICENSE file for information about the licensing of the original library.
// Source: github.com/gorilla/mux (exports: ; functions: Vars,NewRouter)
// Package mux is a stub of github.com/gorilla/mux, generated by depstubber.
package mux
import (
http "net/http"
url "net/url"
)
type BuildVarsFunc func(map[string]string) map[string]string
type MatcherFunc func(*http.Request, *RouteMatch) bool
func (_ MatcherFunc) Match(_ *http.Request, _ *RouteMatch) bool {
return false
}
type MiddlewareFunc func(http.Handler) http.Handler
func (_ MiddlewareFunc) Middleware(_ http.Handler) http.Handler {
return nil
}
func NewRouter() *Router {
return nil
}
type Route struct{}
func (_ *Route) BuildOnly() *Route {
return nil
}
func (_ *Route) BuildVarsFunc(_ BuildVarsFunc) *Route {
return nil
}
func (_ *Route) GetError() error {
return nil
}
func (_ *Route) GetHandler() http.Handler {
return nil
}
func (_ *Route) GetHostTemplate() (string, error) {
return "", nil
}
func (_ *Route) GetMethods() ([]string, error) {
return nil, nil
}
func (_ *Route) GetName() string {
return ""
}
func (_ *Route) GetPathRegexp() (string, error) {
return "", nil
}
func (_ *Route) GetPathTemplate() (string, error) {
return "", nil
}
func (_ *Route) GetQueriesRegexp() ([]string, error) {
return nil, nil
}
func (_ *Route) GetQueriesTemplates() ([]string, error) {
return nil, nil
}
func (_ *Route) Handler(_ http.Handler) *Route {
return nil
}
func (_ *Route) HandlerFunc(_ func(http.ResponseWriter, *http.Request)) *Route {
return nil
}
func (_ *Route) Headers(_ ...string) *Route {
return nil
}
func (_ *Route) HeadersRegexp(_ ...string) *Route {
return nil
}
func (_ *Route) Host(_ string) *Route {
return nil
}
func (_ *Route) Match(_ *http.Request, _ *RouteMatch) bool {
return false
}
func (_ *Route) MatcherFunc(_ MatcherFunc) *Route {
return nil
}
func (_ *Route) Methods(_ ...string) *Route {
return nil
}
func (_ *Route) Name(_ string) *Route {
return nil
}
func (_ *Route) Path(_ string) *Route {
return nil
}
func (_ *Route) PathPrefix(_ string) *Route {
return nil
}
func (_ *Route) Queries(_ ...string) *Route {
return nil
}
func (_ *Route) Schemes(_ ...string) *Route {
return nil
}
func (_ *Route) SkipClean() bool {
return false
}
func (_ *Route) Subrouter() *Router {
return nil
}
func (_ *Route) URL(_ ...string) (*url.URL, error) {
return nil, nil
}
func (_ *Route) URLHost(_ ...string) (*url.URL, error) {
return nil, nil
}
func (_ *Route) URLPath(_ ...string) (*url.URL, error) {
return nil, nil
}
type RouteMatch struct {
Route *Route
Handler http.Handler
Vars map[string]string
MatchErr error
}
type Router struct {
NotFoundHandler http.Handler
MethodNotAllowedHandler http.Handler
KeepContext bool
}
func (_ *Router) BuildVarsFunc(_ BuildVarsFunc) *Route {
return nil
}
func (_ *Router) Get(_ string) *Route {
return nil
}
func (_ *Router) GetRoute(_ string) *Route {
return nil
}
func (_ *Router) Handle(_ string, _ http.Handler) *Route {
return nil
}
func (_ *Router) HandleFunc(_ string, _ func(http.ResponseWriter, *http.Request)) *Route {
return nil
}
func (_ *Router) Headers(_ ...string) *Route {
return nil
}
func (_ *Router) Host(_ string) *Route {
return nil
}
func (_ *Router) Match(_ *http.Request, _ *RouteMatch) bool {
return false
}
func (_ *Router) MatcherFunc(_ MatcherFunc) *Route {
return nil
}
func (_ *Router) Methods(_ ...string) *Route {
return nil
}
func (_ *Router) Name(_ string) *Route {
return nil
}
func (_ *Router) NewRoute() *Route {
return nil
}
func (_ *Router) Path(_ string) *Route {
return nil
}
func (_ *Router) PathPrefix(_ string) *Route {
return nil
}
func (_ *Router) Queries(_ ...string) *Route {
return nil
}
func (_ *Router) Schemes(_ ...string) *Route {
return nil
}
func (_ *Router) ServeHTTP(_ http.ResponseWriter, _ *http.Request) {}
func (_ *Router) SkipClean(_ bool) *Router {
return nil
}
func (_ *Router) StrictSlash(_ bool) *Router {
return nil
}
func (_ *Router) Use(_ ...MiddlewareFunc) {}
func (_ *Router) UseEncodedPath() *Router {
return nil
}
func (_ *Router) Walk(_ WalkFunc) error {
return nil
}
func Vars(_ *http.Request) map[string]string {
return nil
}
type WalkFunc func(*Route, *Router, []*Route) error

View File

@@ -0,0 +1,135 @@
// Code generated by depstubber. DO NOT EDIT.
// This is a simple stub for github.com/gorilla/websocket, strictly for use in testing.
// See the LICENSE file for information about the licensing of the original library.
// Source: github.com/gorilla/websocket (exports: Dialer; functions: )
// Package websocket is a stub of github.com/gorilla/websocket, generated by depstubber.
package websocket
import (
context "context"
tls "crypto/tls"
io "io"
net "net"
http "net/http"
url "net/url"
time "time"
)
type BufferPool interface {
Get() interface{}
Put(_ interface{})
}
type Conn struct{}
func (_ *Conn) Close() error {
return nil
}
func (_ *Conn) CloseHandler() func(int, string) error {
return nil
}
func (_ *Conn) EnableWriteCompression(_ bool) {}
func (_ *Conn) LocalAddr() net.Addr {
return nil
}
func (_ *Conn) NextReader() (int, io.Reader, error) {
return 0, nil, nil
}
func (_ *Conn) NextWriter(_ int) (io.WriteCloser, error) {
return nil, nil
}
func (_ *Conn) PingHandler() func(string) error {
return nil
}
func (_ *Conn) PongHandler() func(string) error {
return nil
}
func (_ *Conn) ReadJSON(_ interface{}) error {
return nil
}
func (_ *Conn) ReadMessage() (int, []byte, error) {
return 0, nil, nil
}
func (_ *Conn) RemoteAddr() net.Addr {
return nil
}
func (_ *Conn) SetCloseHandler(_ func(int, string) error) {}
func (_ *Conn) SetCompressionLevel(_ int) error {
return nil
}
func (_ *Conn) SetPingHandler(_ func(string) error) {}
func (_ *Conn) SetPongHandler(_ func(string) error) {}
func (_ *Conn) SetReadDeadline(_ time.Time) error {
return nil
}
func (_ *Conn) SetReadLimit(_ int64) {}
func (_ *Conn) SetWriteDeadline(_ time.Time) error {
return nil
}
func (_ *Conn) Subprotocol() string {
return ""
}
func (_ *Conn) UnderlyingConn() net.Conn {
return nil
}
func (_ *Conn) WriteControl(_ int, _ []byte, _ time.Time) error {
return nil
}
func (_ *Conn) WriteJSON(_ interface{}) error {
return nil
}
func (_ *Conn) WriteMessage(_ int, _ []byte) error {
return nil
}
func (_ *Conn) WritePreparedMessage(_ *PreparedMessage) error {
return nil
}
type Dialer struct {
NetDial func(string, string) (net.Conn, error)
NetDialContext func(context.Context, string, string) (net.Conn, error)
Proxy func(*http.Request) (*url.URL, error)
TLSClientConfig *tls.Config
HandshakeTimeout time.Duration
ReadBufferSize int
WriteBufferSize int
WriteBufferPool BufferPool
Subprotocols []string
EnableCompression bool
Jar http.CookieJar
}
func (_ *Dialer) Dial(_ string, _ http.Header) (*Conn, *http.Response, error) {
return nil, nil, nil
}
func (_ *Dialer) DialContext(_ context.Context, _ string, _ http.Header) (*Conn, *http.Response, error) {
return nil, nil, nil
}
type PreparedMessage struct{}

View File

@@ -0,0 +1,120 @@
// Code generated by depstubber. DO NOT EDIT.
// This is a simple stub for golang.org/x/net/websocket, strictly for use in testing.
// See the LICENSE file for information about the licensing of the original library.
// Source: golang.org/x/net/websocket (exports: ; functions: Dial,NewConfig,DialConfig)
// Package websocket is a stub of golang.org/x/net/websocket, generated by depstubber.
package websocket
import (
tls "crypto/tls"
io "io"
net "net"
http "net/http"
url "net/url"
time "time"
)
type Config struct {
Location *url.URL
Origin *url.URL
Protocol []string
Version int
TlsConfig *tls.Config
Header http.Header
Dialer *net.Dialer
}
type Conn struct {
PayloadType byte
MaxPayloadBytes int
}
func (_ Conn) HandleFrame(_ interface{}) (interface{}, error) {
return nil, nil
}
func (_ Conn) HeaderReader() io.Reader {
return nil
}
func (_ Conn) Len() int {
return 0
}
func (_ Conn) NewFrameReader() (interface{}, error) {
return nil, nil
}
func (_ Conn) NewFrameWriter(_ byte) (interface{}, error) {
return nil, nil
}
func (_ Conn) TrailerReader() io.Reader {
return nil
}
func (_ Conn) WriteClose(_ int) error {
return nil
}
func (_ *Conn) Close() error {
return nil
}
func (_ *Conn) Config() *Config {
return nil
}
func (_ *Conn) IsClientConn() bool {
return false
}
func (_ *Conn) IsServerConn() bool {
return false
}
func (_ *Conn) LocalAddr() net.Addr {
return nil
}
func (_ *Conn) Read(_ []byte) (int, error) {
return 0, nil
}
func (_ *Conn) RemoteAddr() net.Addr {
return nil
}
func (_ *Conn) Request() *http.Request {
return nil
}
func (_ *Conn) SetDeadline(_ time.Time) error {
return nil
}
func (_ *Conn) SetReadDeadline(_ time.Time) error {
return nil
}
func (_ *Conn) SetWriteDeadline(_ time.Time) error {
return nil
}
func (_ *Conn) Write(_ []byte) (int, error) {
return 0, nil
}
func Dial(_ string, _ string, _ string) (*Conn, error) {
return nil, nil
}
func DialConfig(_ *Config) (*Conn, error) {
return nil, nil
}
func NewConfig(_ string, _ string) (*Config, error) {
return nil, nil
}

View File

@@ -0,0 +1,10 @@
# github.com/gorilla/websocket v1.4.2
# golang.org/x/net v0.0.0-20200421231249-e086a090c8fd
# github.com/gin-gonic/gin v1.6.3
# github.com/go-chi/chi v4.1.2+incompatible
# github.com/gorilla/mux v1.8.0
# github.com/kr/text v0.2.0
# github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e
# github.com/stretchr/testify v1.6.0
# golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
# gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f