mirror of
https://github.com/github/codeql.git
synced 2026-04-28 10:15:14 +02:00
add tests
This commit is contained in:
@@ -18,6 +18,28 @@ module Fasthttp {
|
||||
string fasthttpPackage() { result = "github.com/valyala/fasthttp" }
|
||||
|
||||
module Functions {
|
||||
class FileSystemAccess extends FileSystemAccess::Range, DataFlow::CallNode {
|
||||
FileSystemAccess() {
|
||||
exists(DataFlow::Function f |
|
||||
f.hasQualifiedName("github.com/valyala/fasthttp",
|
||||
[
|
||||
"ServeFile", "ServeFileUncompressed", "ServeFileBytes", "ServeFileBytesUncompressed",
|
||||
"SaveMultipartFile"
|
||||
]) and
|
||||
this = f.getACall()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
this.getTarget().getName() =
|
||||
[
|
||||
"ServeFile", "ServeFileUncompressed", "ServeFileBytes", "ServeFileBytesUncompressed",
|
||||
"SaveMultipartFile"
|
||||
] and
|
||||
result = this.getArgument(1)
|
||||
}
|
||||
}
|
||||
|
||||
private class Redirect extends Http::Redirect::Range, DataFlow::CallNode {
|
||||
Redirect() {
|
||||
exists(DataFlow::Function f |
|
||||
@@ -194,6 +216,20 @@ module Fasthttp {
|
||||
}
|
||||
|
||||
module Response {
|
||||
class FileSystemAccess extends FileSystemAccess::Range, DataFlow::CallNode {
|
||||
FileSystemAccess() {
|
||||
exists(DataFlow::Method mcn |
|
||||
mcn.hasQualifiedName("github.com/valyala/fasthttp.Response", "SendFile") and
|
||||
this = mcn.getACall()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
this.getTarget().getName() = "SendFile" and
|
||||
result = this.getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
class HttpResponseBodySink extends SharedXss::Sink {
|
||||
HttpResponseBodySink() {
|
||||
exists(DataFlow::Method m |
|
||||
@@ -209,6 +245,21 @@ module Fasthttp {
|
||||
}
|
||||
|
||||
module RequestCtx {
|
||||
class FileSystemAccess extends FileSystemAccess::Range, DataFlow::CallNode {
|
||||
FileSystemAccess() {
|
||||
exists(DataFlow::Method mcn |
|
||||
mcn.hasQualifiedName("github.com/valyala/fasthttp.RequestCtx",
|
||||
["SendFileBytes", "SendFile"]) and
|
||||
this = mcn.getACall()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
this.getTarget().getName() = ["SendFile", "SendFileBytes"] and
|
||||
result = this.getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
private class Redirect extends Http::Redirect::Range, DataFlow::CallNode {
|
||||
Redirect() {
|
||||
exists(DataFlow::Function f |
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"github.com/valyala/fasthttp"
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
func fasthttpClient() {
|
||||
// #SSRF
|
||||
response, err := fasthttp.DialDualStack("127.0.0.1:8909")
|
||||
response, err = fasthttp.Dial("google.com:80")
|
||||
response, err = fasthttp.DialTimeout("google.com:80", 5)
|
||||
response, err = fasthttp.DialDualStackTimeout("google.com:80", 5)
|
||||
log.Println(err)
|
||||
resByte := make([]byte, 1000)
|
||||
_, err = response.Read(resByte)
|
||||
log.Println(resByte)
|
||||
|
||||
// #SSRF
|
||||
fasthttp.Get(resByte, "http://127.0.0.1:8909")
|
||||
fasthttp.GetDeadline(resByte, "http://127.0.0.1:8909", time.Time{})
|
||||
fasthttp.GetTimeout(resByte, "http://127.0.0.1:8909", 5)
|
||||
fasthttp.Post(resByte, "http://127.0.0.1:8909", nil)
|
||||
log.Println(string(resByte))
|
||||
|
||||
// #SSRF
|
||||
client := fasthttp.Client{}
|
||||
client.Get(resByte, "http://127.0.0.1:8909")
|
||||
client.GetDeadline(resByte, "http://127.0.0.1:8909", time.Time{})
|
||||
client.GetTimeout(resByte, "http://127.0.0.1:8909", 5)
|
||||
client.Post(resByte, "http://127.0.0.1:8909", nil)
|
||||
|
||||
res := &fasthttp.Response{}
|
||||
req := &fasthttp.Request{}
|
||||
uri := fasthttp.URI{}
|
||||
// additional steps
|
||||
uri.SetHost("UserControlled.com:80")
|
||||
uri.SetHostBytes([]byte("UserControlled.com:80"))
|
||||
req.SetHost("UserControlled.com:80")
|
||||
req.SetHostBytes([]byte("UserControlled.com:80"))
|
||||
req.SetRequestURI("https://UserControlled.com")
|
||||
req.SetRequestURIBytes([]byte("https://UserControlled.com"))
|
||||
req.SetURI(&uri)
|
||||
fasthttp.Do(req, res)
|
||||
fasthttp.DoDeadline(req, res, time.Time{})
|
||||
fasthttp.DoTimeout(req, res, 5)
|
||||
client.Do(req, res)
|
||||
client.DoDeadline(req, res, time.Time{})
|
||||
client.DoTimeout(req, res, 5)
|
||||
|
||||
// #SSRF
|
||||
tcpDialer := fasthttp.TCPDialer{}
|
||||
tcpDialer.Dial("127.0.0.1:8909")
|
||||
tcpDialer.DialTimeout("127.0.0.1:8909", 5)
|
||||
tcpDialer.DialDualStack("127.0.0.1:8909")
|
||||
tcpDialer.DialDualStackTimeout("127.0.0.1:8909", 5)
|
||||
}
|
||||
|
||||
func fasthttpServer() {
|
||||
ln, err := net.Listen("tcp4", "127.0.0.1:8080")
|
||||
if err != nil {
|
||||
log.Fatalf("error in net.Listen: %v", err)
|
||||
}
|
||||
requestHandler := func(ctx *fasthttp.RequestCtx) {
|
||||
filePath := ctx.QueryArgs().Peek("filePath")
|
||||
// File System Access
|
||||
_ = ctx.Response.SendFile(string(filePath))
|
||||
ctx.SendFile(string(filePath))
|
||||
ctx.SendFileBytes(filePath)
|
||||
fileHeader, _ := ctx.FormFile("file")
|
||||
_ = fasthttp.SaveMultipartFile(fileHeader, string(filePath))
|
||||
fasthttp.ServeFile(ctx, string(filePath))
|
||||
fasthttp.ServeFileUncompressed(ctx, string(filePath))
|
||||
fasthttp.ServeFileBytes(ctx, filePath)
|
||||
fasthttp.ServeFileBytesUncompressed(ctx, filePath)
|
||||
|
||||
dstWriter := &bufio.Writer{}
|
||||
dstReader := &bufio.Reader{}
|
||||
// user controlled methods as source
|
||||
requestHeader := &fasthttp.RequestHeader{}
|
||||
ctx.Request.Header.CopyTo(requestHeader)
|
||||
requestHeader.Write(dstWriter)
|
||||
requestHeader.Header()
|
||||
requestHeader.TrailerHeader()
|
||||
requestHeader.String()
|
||||
requestHeader.RequestURI()
|
||||
requestHeader.Host()
|
||||
requestHeader.UserAgent()
|
||||
requestHeader.ContentEncoding()
|
||||
requestHeader.ContentType()
|
||||
requestHeader.Cookie("ACookie")
|
||||
requestHeader.CookieBytes([]byte("ACookie"))
|
||||
requestHeader.MultipartFormBoundary()
|
||||
requestHeader.Peek("AHeaderName")
|
||||
requestHeader.PeekAll("AHeaderName")
|
||||
requestHeader.PeekBytes([]byte("AHeaderName"))
|
||||
requestHeader.PeekKeys()
|
||||
requestHeader.PeekTrailerKeys()
|
||||
requestHeader.Referer()
|
||||
requestHeader.RawHeaders()
|
||||
// multipart.Form is already implemented
|
||||
//ctx.MultipartForm()
|
||||
ctx.URI().Path()
|
||||
ctx.URI().PathOriginal()
|
||||
newURI := &fasthttp.URI{}
|
||||
ctx.URI().CopyTo(newURI)
|
||||
ctx.URI().FullURI()
|
||||
ctx.URI().LastPathSegment()
|
||||
ctx.URI().QueryString()
|
||||
ctx.URI().String()
|
||||
ctx.URI().WriteTo(dstWriter)
|
||||
|
||||
newArgs := &fasthttp.Args{}
|
||||
//or ctx.PostArgs()
|
||||
ctx.URI().QueryArgs().CopyTo(newArgs)
|
||||
ctx.URI().QueryArgs().Peek("arg1")
|
||||
ctx.URI().QueryArgs().PeekBytes([]byte("arg1"))
|
||||
ctx.URI().QueryArgs().PeekMulti("arg1")
|
||||
ctx.URI().QueryArgs().PeekMultiBytes([]byte("arg1"))
|
||||
ctx.URI().QueryArgs().QueryString()
|
||||
ctx.URI().QueryArgs().String()
|
||||
ctx.URI().QueryArgs().WriteTo(dstWriter)
|
||||
// not sure what is the best way to write query for following
|
||||
//ctx.URI().QueryArgs().VisitAll(type func(,))
|
||||
|
||||
ctx.Path()
|
||||
// multipart.Form is already implemented
|
||||
// ctx.FormFile("FileName")
|
||||
// ctx.FormValue("ValueName")
|
||||
ctx.Referer()
|
||||
ctx.PostBody()
|
||||
ctx.RequestBodyStream()
|
||||
ctx.RequestURI()
|
||||
ctx.UserAgent()
|
||||
ctx.Host()
|
||||
|
||||
ctx.Request.Host()
|
||||
ctx.Request.Body()
|
||||
ctx.Request.RequestURI()
|
||||
ctx.Request.BodyGunzip()
|
||||
ctx.Request.BodyInflate()
|
||||
ctx.Request.BodyUnbrotli()
|
||||
ctx.Request.BodyStream()
|
||||
ctx.Request.BodyWriteTo(dstWriter)
|
||||
ctx.Request.WriteTo(dstWriter)
|
||||
ctx.Request.BodyUncompressed()
|
||||
ctx.Request.ReadBody(dstReader, 100, 1000)
|
||||
ctx.Request.ReadLimitBody(dstReader, 100)
|
||||
ctx.Request.ContinueReadBodyStream(dstReader, 100, true)
|
||||
ctx.Request.ContinueReadBody(dstReader, 100)
|
||||
// not sure what is the best way to write query for following
|
||||
//ctx.Request.Header.VisitAllCookie()
|
||||
|
||||
// Xss Sinks
|
||||
ctx.Response.AppendBody([]byte("user Controlled"))
|
||||
ctx.Response.AppendBodyString("user Controlled")
|
||||
rspWriter := ctx.Response.BodyWriter()
|
||||
rspWriter.Write([]byte("XSS"))
|
||||
ctx.Response.SetBody([]byte("user Controlled"))
|
||||
ctx.Response.SetBodyString("user Controlled")
|
||||
ctx.Response.SetBodyRaw([]byte("user Controlled"))
|
||||
ctx.Response.SetBodyStream(dstReader, 100)
|
||||
dstByte := []byte("init")
|
||||
// sanitizers
|
||||
fasthttp.AppendQuotedArg(dstByte, []byte("xsss"))
|
||||
fasthttp.AppendHTMLEscape(dstByte, "xss")
|
||||
fasthttp.AppendHTMLEscapeBytes(dstByte, []byte("xss"))
|
||||
|
||||
// TODO: unstrusted Remote IP from Header?
|
||||
// TODO: open redirect Sinks
|
||||
req := &fasthttp.Request{}
|
||||
res := &fasthttp.Response{}
|
||||
// there are additional steps from other scope
|
||||
req.SetRequestURI("https://userControlled.com")
|
||||
fasthttp.DoRedirects(req, res, 2)
|
||||
ctx.Redirect("https://userControlled.com", 301)
|
||||
ctx.RedirectBytes([]byte("https://userControlled.com"), 301)
|
||||
}
|
||||
if err := fasthttp.Serve(ln, requestHandler); err != nil {
|
||||
log.Fatalf("error in Serve: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import go
|
||||
import semmle.go.frameworks.Fasthttp
|
||||
|
||||
from
|
||||
Fasthttp::Request::UntrustedFlowSource u1, Fasthttp::RequestCtx::UntrustedFlowSource u2,
|
||||
Fasthttp::URI::UntrustedFlowSource u3, Fasthttp::RequestHeader::UntrustedFlowSource u4
|
||||
select u1, u2, u3, u4
|
||||
@@ -0,0 +1,10 @@
|
||||
module fastHttpModel
|
||||
|
||||
go 1.20
|
||||
|
||||
require github.com/valyala/fasthttp v1.49.0
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/klauspost/compress v1.16.3 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect)
|
||||
Reference in New Issue
Block a user