Skip to content

Commit 3bc24c3

Browse files
committed
add inline tests for open redirect,xss, fix some issues in fasthttp.qll
1 parent 2921992 commit 3bc24c3

File tree

7 files changed

+98
-77
lines changed

7 files changed

+98
-77
lines changed

go/ql/lib/semmle/go/frameworks/Fasthttp.qll

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,42 @@ module Fasthttp {
343343
}
344344
}
345345

346+
/**
347+
* Provide modeling for fasthttp.Response Type
348+
*/
349+
module Response {
350+
/**
351+
* A Method That send files from its input and it does not check input path against path traversal attacks, so it is a dangerous method
352+
*/
353+
class FileSystemAccess extends FileSystemAccess::Range, DataFlow::CallNode {
354+
FileSystemAccess() {
355+
exists(Method mcn |
356+
mcn.hasQualifiedName(packagePath(), "Response", "SendFile") and
357+
this = mcn.getACall()
358+
)
359+
}
360+
361+
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
362+
}
363+
364+
/**
365+
* The methods that can write to HTTP Response Body.
366+
* These methods can be dangerous if they are user controllable.
367+
*/
368+
class HttpResponseBodySink extends SharedXss::Sink {
369+
HttpResponseBodySink() {
370+
exists(Method m |
371+
m.hasQualifiedName(packagePath(), "Response",
372+
[
373+
"AppendBody", "AppendBodyString", "SetBody", "SetBodyString", "SetBodyRaw",
374+
"SetBodyStream"
375+
]) and
376+
this = m.getACall().getArgument(0)
377+
)
378+
}
379+
}
380+
}
381+
346382
/**
347383
* Provide modeling for fasthttp.Request Type
348384
*/
@@ -371,42 +407,6 @@ module Fasthttp {
371407
}
372408
}
373409

374-
/**
375-
* Provide modeling for fasthttp.Response Type
376-
*/
377-
module Response {
378-
/**
379-
* A Method That send files from its input and it does not check input path against path traversal attacks, so it is a dangerous method
380-
*/
381-
class FileSystemAccess extends FileSystemAccess::Range, DataFlow::CallNode {
382-
FileSystemAccess() {
383-
exists(Method mcn |
384-
mcn.hasQualifiedName(packagePath(), "Response", "SendFile") and
385-
this = mcn.getACall()
386-
)
387-
}
388-
389-
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
390-
}
391-
392-
/**
393-
* The methods that can write to HTTP Response Body.
394-
* These methods can be dangerous if they are user controllable.
395-
*/
396-
class HttpResponseBodySink extends SharedXss::Sink {
397-
HttpResponseBodySink() {
398-
exists(Method m |
399-
m.hasQualifiedName(packagePath(), "Response",
400-
[
401-
"AppendBody", "AppendBodyString", "SetBody", "SetBodyString", "SetBodyRaw",
402-
"SetBodyStream"
403-
]) and
404-
this = m.getACall().getArgument(0)
405-
)
406-
}
407-
}
408-
}
409-
410410
/**
411411
* The methods as Remote user controllable source which can be many part of request
412412
*/
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
| fasthttp.go:218:23:218:50 | "https://userControlled.com" |
2-
| fasthttp.go:219:28:219:63 | type conversion |
1+
testFailures
2+
failures
Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,19 @@
11
import go
22
import semmle.go.security.OpenUrlRedirectCustomizations
3+
import TestUtilities.InlineExpectationsTest
34

4-
select any(OpenUrlRedirect::Sink s)
5+
module FasthttpTest implements TestSig {
6+
string getARelevantTag() { result = "OpenRedirect" }
7+
8+
predicate hasActualResult(Location location, string element, string tag, string value) {
9+
exists(OpenUrlRedirect::Sink s |
10+
s.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
11+
location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
12+
element = s.toString() and
13+
value = s.toString() and
14+
tag = "OpenRedirect"
15+
)
16+
}
17+
}
18+
19+
import MakeTest<FasthttpTest>

go/ql/test/library-tests/semmle/go/frameworks/Fasthttp/UntrustedRemoteFlowSource.ql

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ module FasthttpTest implements TestSig {
55
string getARelevantTag() { result = "UntrustedFlowSource" }
66

77
predicate hasActualResult(Location location, string element, string tag, string value) {
8-
exists(UntrustedFlowSource q |
9-
q.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
8+
exists(UntrustedFlowSource source |
9+
source.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
1010
location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
11-
element = q.toString() and
12-
value = "\"" + q.toString() + "\"" and
11+
element = source.toString() and
12+
value = "\"" + source.toString() + "\"" and
1313
tag = "UntrustedFlowSource"
1414
)
1515
}
Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,2 @@
1-
| fasthttp.go:194:34:194:58 | type conversion |
2-
| fasthttp.go:195:40:195:56 | "user Controlled" |
3-
| fasthttp.go:198:31:198:55 | type conversion |
4-
| fasthttp.go:199:37:199:53 | "user Controlled" |
5-
| fasthttp.go:200:34:200:58 | type conversion |
6-
| fasthttp.go:201:37:201:45 | dstReader |
7-
| fasthttp.go:207:26:207:39 | type conversion |
8-
| fasthttp.go:208:32:208:37 | "body" |
9-
| fasthttp.go:213:34:213:90 | call to AppendQuotedArg |
10-
| fasthttp.go:214:34:214:83 | call to AppendHTMLEscape |
11-
| fasthttp.go:215:34:215:96 | call to AppendHTMLEscapeBytes |
1+
testFailures
2+
failures
Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
11
import go
2+
import TestUtilities.InlineExpectationsTest
23

3-
select any(SharedXss::Sink s)
4+
module FasthttpTest implements TestSig {
5+
string getARelevantTag() { result = "XssSink" }
6+
7+
predicate hasActualResult(Location location, string element, string tag, string value) {
8+
exists(SharedXss::Sink xssSink |
9+
xssSink
10+
.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
11+
location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
12+
element = xssSink.toString() and
13+
value = xssSink.toString() and
14+
tag = "XssSink"
15+
)
16+
}
17+
}
18+
19+
import MakeTest<FasthttpTest>

go/ql/test/library-tests/semmle/go/frameworks/Fasthttp/fasthttp.go

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ func fasthttpClient() {
2222
res := &fasthttp.Response{}
2323
req := &fasthttp.Request{}
2424
uri := fasthttp.AcquireURI()
25-
uri2 := fasthttp.AcquireURI()
2625
fasthttp.Get(resByte, "http://127.0.0.1:8909") // $ SSRF="http://127.0.0.1:8909"
2726
fasthttp.GetDeadline(resByte, "http://127.0.0.1:8909", time.Time{}) // $ SSRF="http://127.0.0.1:8909"
2827
fasthttp.GetTimeout(resByte, "http://127.0.0.1:8909", 5) // $ SSRF="http://127.0.0.1:8909"
@@ -174,35 +173,35 @@ func fasthttpServer() {
174173

175174
// Response methods
176175
// Xss Sinks Related method
177-
requestCtx.Response.AppendBody([]byte("user Controlled")) // $ XSS=[]byte("user Controlled")
178-
requestCtx.Response.AppendBodyString("user Controlled") // $ XSS="user Controlled"
179-
rspWriter := requestCtx.Response.BodyWriter()
180-
rspWriter.Write([]byte("XSS")) // $ XSS=[]byte("XSS")
181-
requestCtx.Response.SetBody([]byte("user Controlled")) // $ XSS=[]byte("XSS")
182-
requestCtx.Response.SetBodyString("user Controlled") // $ XSS=[]byte("XSS")
183-
requestCtx.Response.SetBodyRaw([]byte("user Controlled")) // $ XSS=[]byte("XSS")
184-
requestCtx.Response.SetBodyStream(dstReader, 100) // $ XSS=[]byte("XSS")
176+
userInput := "user Controlled input"
177+
userInputByte := []byte("user Controlled input")
178+
requestCtx.Response.AppendBody(userInputByte) // $ XssSink=userInputByte
179+
requestCtx.Response.AppendBodyString(userInput) // $ XssSink=userInput
180+
rspWriter := requestCtx.Response.BodyWriter() // IDK how to handle this that returns a `io.Writer`
181+
rspWriter.Write(userInputByte)
182+
requestCtx.Response.SetBody(userInputByte) // $ XssSink=userInputByte
183+
requestCtx.Response.SetBodyString(userInput) // $ XssSink=userInput
184+
requestCtx.Response.SetBodyRaw(userInputByte) // $ XssSink=userInputByte
185+
requestCtx.Response.SetBodyStream(dstReader, 100) // $ XssSink=dstReader
185186
// mostly related to header writers
186-
requestCtx.Response.Header.Set("Content-Type", "")
187-
requestCtx.Response.Header.Add("Content-Type", "")
188-
requestCtx.Response.Header.SetContentTypeBytes([]byte(""))
189-
requestCtx.Response.Header.SetContentType("")
190-
requestCtx.Success("", []byte("body")) // $ XSS=[]byte("body")
191-
requestCtx.SuccessString("", "body") // $ XSS="body"
192-
requestCtx.SetContentType("")
193-
requestCtx.SetContentTypeBytes([]byte(""))
187+
requestCtx.Success("", userInputByte) // $ XssSink=userInputByte
188+
requestCtx.SuccessString("", userInput) // $ XssSink=userInput
194189

195190
// sanitizers
196-
requestCtx.Response.AppendBody(fasthttp.AppendQuotedArg([]byte(""), []byte("<>\"':()&"))) // $ Sanitizer=AppendBody
191+
userInputByte = []byte("<>\"':()&")
192+
userInput = "<>\"':()&"
193+
fasthttp.AppendQuotedArg([]byte(""), userInputByte) // $ Sanitizer=userInputByte
197194
// %3C%3E%22%27%3A%28%29%26
198-
requestCtx.Response.AppendBody(fasthttp.AppendHTMLEscape([]byte(""), "<>\"':()&")) // $ Sanitizer=AppendBody
195+
fasthttp.AppendHTMLEscape([]byte(""), userInput) // $ Sanitizer=userInput
199196
// &lt;&gt;&#34;&#39;:()&amp;
200-
requestCtx.Response.AppendBody(fasthttp.AppendHTMLEscapeBytes([]byte(""), []byte("<>\"':()&"))) // $ Sanitizer=AppendBody
197+
fasthttp.AppendHTMLEscapeBytes([]byte(""), userInputByte) // $ Sanitizer=userInputByte
201198
// &lt;&gt;&#34;&#39;:()&amp;
202199

203200
// open redirect Sinks
204-
requestCtx.Redirect("https://userControlled.com", 301) // $ OpenRedirect="https://userControlled.com"
205-
requestCtx.RedirectBytes([]byte("https://userControlled.com"), 301) // $ OpenRedirect=[]byte("https://userControlled.com")
201+
userInput = "https://userControlled.com"
202+
requestCtx.Redirect(userInput, 301) // $ OpenRedirect=userInput
203+
userInputByte = []byte("https://userControlled.com")
204+
requestCtx.RedirectBytes(userInputByte, 301) // $ OpenRedirect=userInputByte
206205
}
207206
fasthttp.Serve(ln, requestHandler)
208207
}

0 commit comments

Comments
 (0)