This commit is contained in:
amammad
2023-09-02 22:27:08 +10:00
committed by amammad
parent c6bc1a3f3a
commit cddd27c5f8
2 changed files with 287 additions and 0 deletions

View File

@@ -0,0 +1,235 @@
/** Provides models of commonly used functions and types in the fasthttp packages. */
import go
private import semmle.go.security.RequestForgeryCustomizations
private import semmle.go.security.SafeUrlFlowCustomizations
import semmle.go.security.Xss
/** Provides models of commonly used functions and types in the fasthttp packages. */
module Fasthttp {
bindingset[this]
abstract class AdditionalStep extends string {
/**
* Holds if `pred` to `succ` is an additional taint-propagating step for this query.
*/
abstract predicate hasTaintStep(DataFlow::Node pred, DataFlow::Node succ);
}
string fasthttpPackage() { result = "github.com/valyala/fasthttp" }
module Functions {
private class Redirect extends Http::Redirect::Range, DataFlow::CallNode {
Redirect() {
exists(DataFlow::Function f |
f.hasQualifiedName("github.com/valyala/fasthttp", "DoRedirects") and this = f.getACall()
)
}
override DataFlow::Node getUrl() { result = this.getArgument(0) }
override Http::ResponseWriter getResponseWriter() { result.getANode() = this.getArgument(1) }
}
private class HtmlQuoteSanitizer extends SharedXss::Sanitizer {
HtmlQuoteSanitizer() {
exists(DataFlow::CallNode c |
c.getTarget()
.hasQualifiedName("github.com/valyala/fasthttp",
["AppendHTMLEscape", "AppendHTMLEscapeBytes", "AppendQuotedArg"])
|
this = c.getArgument(1)
)
}
}
class SSRFSink extends RequestForgery::Sink {
SSRFSink() {
exists(DataFlow::Function f |
f.hasQualifiedName("github.com/valyala/fasthttp",
[
"DialDualStack", "Dial", "DialTimeout", "DialDualStackTimeout", "Get", "GetDeadline",
"GetTimeout", "Post", "Do", "DoDeadline", "DoTimeout", "Write", "Write", "Write",
"Write", "Write"
]) and
this = f.getACall().getArgument(0)
)
}
override DataFlow::Node getARequest() { result = this }
override string getKind() { result = "URL" }
}
}
module RequestHeader {
class UntrustedFlowSource extends UntrustedFlowSource::Range instanceof DataFlow::Node {
UntrustedFlowSource() {
exists(DataFlow::Method m |
m.hasQualifiedName("github.com/valyala/fasthttp.RequestHeader",
[
"Header", "TrailerHeader", "RequestURI", "Host", "UserAgent", "ContentEncoding",
"ContentType", "Cookie", "CookieBytes", "MultipartFormBoundary", "Peek", "PeekAll",
"PeekBytes", "PeekKeys", "PeekTrailerKeys", "Referer", "RawHeaders"
]) and
this = m.getACall()
or
m.hasQualifiedName("github.com/valyala/fasthttp.RequestHeader", ["Write"]) and
this = m.getACall().getArgument(0)
)
}
}
}
module URI {
class URIAdditionalStep extends AdditionalStep {
URIAdditionalStep() { this = "URI additioanl steps" }
override predicate hasTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::MethodCallNode m, DataFlow::Variable frn |
m.getTarget()
.hasQualifiedName("github.com/valyala/fasthttp.URI", ["SetHost", "SetHostBytes"]) and
pred = m.getArgument(0) and
frn.getARead() = m.getReceiver() and
succ = frn.getARead()
)
}
}
class UntrustedFlowSource extends UntrustedFlowSource::Range instanceof DataFlow::Node {
UntrustedFlowSource() {
exists(DataFlow::Method m |
m.hasQualifiedName("github.com/valyala/fasthttp.URI",
["Path", "PathOriginal", "LastPathSegment", "FullURI", "QueryString", "String"]) and
this = m.getACall()
or
m.hasQualifiedName("github.com/valyala/fasthttp.URI", "WriteTo") and
this = m.getACall().getArgument(0)
)
}
}
}
module Args {
class UntrustedFlowSource extends UntrustedFlowSource::Range instanceof DataFlow::Node {
UntrustedFlowSource() {
exists(DataFlow::Method m |
m.hasQualifiedName("github.com/valyala/fasthttp.Args",
["Peek", "PeekBytes", "PeekMulti", "PeekMultiBytes", "QueryString", "String"]) and
this = m.getACall()
or
m.hasQualifiedName("github.com/valyala/fasthttp.Args", "WriteTo") and
this = m.getACall().getArgument(0)
)
}
}
}
module TCPDialer {
class SSRFSink extends RequestForgery::Sink {
SSRFSink() {
exists(DataFlow::Method m |
m.hasQualifiedName("github.com/valyala/fasthttp.TCPDialer",
["Dial", "DialTimeout", "DialDualStack", "DialDualStackTimeout"]) and
this = m.getACall().getArgument(0)
)
}
override DataFlow::Node getARequest() { result = this }
override string getKind() { result = "Host" }
}
}
module Client {
class SSRFSink extends RequestForgery::Sink {
SSRFSink() {
exists(DataFlow::Method m |
m.hasQualifiedName("github.com/valyala/fasthttp.Client",
["Get", "GetDeadline", "GetTimeout", "Post", "Do", "DoDeadline", "DoTimeout"]) and
this = m.getACall().getArgument(0)
)
}
override DataFlow::Node getARequest() { result = this }
override string getKind() { result = "URL" }
}
}
module Request {
class RequestAdditionalStep extends AdditionalStep {
RequestAdditionalStep() { this = "Request additioanl steps" }
override predicate hasTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::MethodCallNode m, DataFlow::Variable frn |
m.getTarget()
.hasQualifiedName("github.com/valyala/fasthttp.Request",
["SetRequestURI", "SetRequestURIBytes", "SetURI", "SetHost", "SetHostBytes"]) and
pred = m.getArgument(0) and
frn.getARead() = m.getReceiver() and
succ = frn.getARead()
)
}
}
class UntrustedFlowSource extends UntrustedFlowSource::Range instanceof DataFlow::Node {
UntrustedFlowSource() {
exists(DataFlow::Method m |
m.hasQualifiedName("github.com/valyala/fasthttp.Request",
[
"Host", "RequestURI", "Body", "BodyGunzip", "BodyInflate", "BodyUnbrotli",
"BodyStream", "BodyUncompressed"
]) and
this = m.getACall()
or
m.hasQualifiedName("github.com/valyala/fasthttp.Request",
[
"BodyWriteTo", "WriteTo", "ReadBody", "ReadLimitBody", "ContinueReadBodyStream",
"ContinueReadBody"
]) and
this = m.getACall().getArgument(0)
)
}
}
}
module Response {
class HttpResponseBodySink extends SharedXss::Sink {
HttpResponseBodySink() {
exists(DataFlow::Method m |
m.hasQualifiedName("github.com/valyala/fasthttp.Response",
[
"AppendBody", "AppendBodyString", "SetBody", "SetBodyString", "SetBodyRaw",
"SetBodyStream"
]) and
this = m.getACall().getArgument(0)
)
}
}
}
module RequestCtx {
private class Redirect extends Http::Redirect::Range, DataFlow::CallNode {
Redirect() {
exists(DataFlow::Function f |
f.hasQualifiedName("github.com/valyala/fasthttp", ["Redirect", "RedirectBytes"]) and
this = f.getACall()
)
}
override DataFlow::Node getUrl() { result = this.getArgument(0) }
override Http::ResponseWriter getResponseWriter() { none() }
}
class UntrustedFlowSource extends UntrustedFlowSource::Range instanceof DataFlow::Node {
UntrustedFlowSource() {
exists(DataFlow::Method m |
m.hasQualifiedName("github.com/valyala/fasthttp.RequestCtx",
["Path", "Referer", "PostBody", "RequestBodyStream", "RequestURI", "UserAgent", "Host"]) and
this = m.getACall()
)
}
}
}
}

View File

@@ -0,0 +1,52 @@
/**
* @name Uncontrolled data used in network request
* @description Sending network requests with user-controlled data allows for request forgery attacks.
* @id go/ssrf
* @kind path-problem
* @problem.severity error
* @precision high
* @tags security
* experimental
* external/cwe/cwe-918
*/
import go
module Config implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(DataFlow::MethodCallNode m |
m.getTarget().hasQualifiedName("github.com/valyala/fasthttp.URI", ["SetHost", "SetHostBytes"]) and
source = m.getArgument(0)
)
}
predicate isSink(DataFlow::Node sink) { any() }
predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::MethodCallNode m, DataFlow::Variable frn |
m.getTarget().hasQualifiedName("github.com/valyala/fasthttp.URI", ["SetHost", "SetHostBytes"]) and
pred = m.getArgument(0) and
frn.getARead() = m.getReceiver() and
succ = frn.getARead()
)
or
exists(DataFlow::MethodCallNode m, DataFlow::Variable frn |
m.getTarget()
.hasQualifiedName("github.com/valyala/fasthttp.Request",
["SetRequestURI", "SetRequestURIBytes", "SetURI"]) and
pred = m.getArgument(0) and
frn.getARead() = m.getReceiver() and
succ = frn.getARead()
)
}
}
module Flow = TaintTracking::Global<Config>;
import Flow::PathGraph
from Flow::PathNode source, Flow::PathNode sink, DataFlow::Node request
where
Flow::flowPath(source, sink) and
request = sink.getNode()
select request, source, sink, "The URL of this request depends on a user-provided value."