From 414bab1f3068ac2d9316d16fb4bfdae6eee09548 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 25 Sep 2025 15:57:14 +0100 Subject: [PATCH] Add OpenUrlRedirect tests for Url.Host field --- .../OpenUrlRedirect/OpenUrlRedirect.expected | 60 +++++++++++++++++++ .../CWE-601/OpenUrlRedirect/stdlib.go | 16 +++++ 2 files changed, 76 insertions(+) diff --git a/go/ql/test/query-tests/Security/CWE-601/OpenUrlRedirect/OpenUrlRedirect.expected b/go/ql/test/query-tests/Security/CWE-601/OpenUrlRedirect/OpenUrlRedirect.expected index b091d0343d8..7986b0a7e8c 100644 --- a/go/ql/test/query-tests/Security/CWE-601/OpenUrlRedirect/OpenUrlRedirect.expected +++ b/go/ql/test/query-tests/Security/CWE-601/OpenUrlRedirect/OpenUrlRedirect.expected @@ -10,6 +10,10 @@ | stdlib.go:188:23:188:28 | target | stdlib.go:186:13:186:33 | call to FormValue | stdlib.go:188:23:188:28 | target | This path to an untrusted URL redirection depends on a $@. | stdlib.go:186:13:186:33 | call to FormValue | user-provided value | | stdlib.go:196:23:196:33 | selection of Path | stdlib.go:194:36:194:56 | call to FormValue | stdlib.go:196:23:196:33 | selection of Path | This path to an untrusted URL redirection depends on a $@. | stdlib.go:194:36:194:56 | call to FormValue | user-provided value | | stdlib.go:198:23:198:42 | call to EscapedPath | stdlib.go:194:36:194:56 | call to FormValue | stdlib.go:198:23:198:42 | call to EscapedPath | This path to an untrusted URL redirection depends on a $@. | stdlib.go:194:36:194:56 | call to FormValue | user-provided value | +| stdlib.go:201:23:201:33 | selection of Path | stdlib.go:194:36:194:56 | call to FormValue | stdlib.go:201:23:201:33 | selection of Path | This path to an untrusted URL redirection depends on a $@. | stdlib.go:194:36:194:56 | call to FormValue | user-provided value | +| stdlib.go:203:23:203:37 | call to String | stdlib.go:194:36:194:56 | call to FormValue | stdlib.go:203:23:203:37 | call to String | This path to an untrusted URL redirection depends on a $@. | stdlib.go:194:36:194:56 | call to FormValue | user-provided value | +| stdlib.go:212:23:212:28 | selection of Path | stdlib.go:210:12:210:30 | call to FormValue | stdlib.go:212:23:212:28 | selection of Path | This path to an untrusted URL redirection depends on a $@. | stdlib.go:210:12:210:30 | call to FormValue | user-provided value | +| stdlib.go:214:23:214:32 | call to String | stdlib.go:210:12:210:30 | call to FormValue | stdlib.go:214:23:214:32 | call to String | This path to an untrusted URL redirection depends on a $@. | stdlib.go:210:12:210:30 | call to FormValue | user-provided value | edges | OpenUrlRedirect.go:10:23:10:28 | selection of Form | OpenUrlRedirect.go:10:23:10:42 | call to Get | provenance | Src:MaD:2 Config Sink:MaD:1 | | stdlib.go:13:13:13:18 | selection of Form | stdlib.go:13:13:13:32 | call to Get | provenance | Src:MaD:2 Config | @@ -55,9 +59,41 @@ edges | stdlib.go:196:23:196:28 | target | stdlib.go:196:23:196:28 | implicit dereference | provenance | Config | | stdlib.go:196:23:196:28 | target | stdlib.go:196:23:196:33 | selection of Path | provenance | Config Sink:MaD:1 | | stdlib.go:196:23:196:28 | target | stdlib.go:198:23:198:28 | target | provenance | | +| stdlib.go:196:23:196:28 | target | stdlib.go:199:3:199:8 | target | provenance | | | stdlib.go:196:23:196:28 | target [postupdate] | stdlib.go:196:23:196:28 | implicit dereference | provenance | Config | | stdlib.go:196:23:196:28 | target [postupdate] | stdlib.go:198:23:198:28 | target | provenance | | +| stdlib.go:196:23:196:28 | target [postupdate] | stdlib.go:199:3:199:8 | target | provenance | | | stdlib.go:198:23:198:28 | target | stdlib.go:198:23:198:42 | call to EscapedPath | provenance | Config Sink:MaD:1 | +| stdlib.go:199:3:199:8 | implicit dereference | stdlib.go:199:3:199:8 | target [postupdate] | provenance | Config | +| stdlib.go:199:3:199:8 | target | stdlib.go:199:3:199:8 | implicit dereference | provenance | Config | +| stdlib.go:199:3:199:8 | target | stdlib.go:201:23:201:28 | target | provenance | | +| stdlib.go:199:3:199:8 | target [postupdate] | stdlib.go:199:3:199:8 | implicit dereference | provenance | Config | +| stdlib.go:199:3:199:8 | target [postupdate] | stdlib.go:201:23:201:28 | target | provenance | | +| stdlib.go:201:23:201:28 | implicit dereference | stdlib.go:201:23:201:28 | target [postupdate] | provenance | Config | +| stdlib.go:201:23:201:28 | implicit dereference | stdlib.go:201:23:201:33 | selection of Path | provenance | Config Sink:MaD:1 | +| stdlib.go:201:23:201:28 | target | stdlib.go:201:23:201:28 | implicit dereference | provenance | Config | +| stdlib.go:201:23:201:28 | target | stdlib.go:201:23:201:33 | selection of Path | provenance | Config Sink:MaD:1 | +| stdlib.go:201:23:201:28 | target | stdlib.go:203:23:203:28 | target | provenance | | +| stdlib.go:201:23:201:28 | target [postupdate] | stdlib.go:201:23:201:28 | implicit dereference | provenance | Config | +| stdlib.go:201:23:201:28 | target [postupdate] | stdlib.go:203:23:203:28 | target | provenance | | +| stdlib.go:203:23:203:28 | target | stdlib.go:203:23:203:37 | call to String | provenance | Config Sink:MaD:1 | +| stdlib.go:210:3:210:3 | implicit dereference | stdlib.go:210:3:210:3 | u [postupdate] | provenance | Config | +| stdlib.go:210:3:210:3 | implicit dereference [postupdate] | stdlib.go:210:3:210:3 | u [postupdate] | provenance | Config | +| stdlib.go:210:3:210:3 | implicit dereference [postupdate] | stdlib.go:210:3:210:3 | u [postupdate] [pointer] | provenance | | +| stdlib.go:210:3:210:3 | u [postupdate] | stdlib.go:210:3:210:3 | implicit dereference | provenance | Config | +| stdlib.go:210:3:210:3 | u [postupdate] | stdlib.go:212:23:212:23 | u | provenance | | +| stdlib.go:210:3:210:3 | u [postupdate] [pointer] | stdlib.go:212:23:212:23 | u [pointer] | provenance | | +| stdlib.go:210:12:210:30 | call to FormValue | stdlib.go:210:3:210:3 | implicit dereference [postupdate] | provenance | Src:MaD:3 Config | +| stdlib.go:210:12:210:30 | call to FormValue | stdlib.go:210:3:210:3 | u [postupdate] | provenance | Src:MaD:3 Config | +| stdlib.go:212:23:212:23 | implicit dereference | stdlib.go:212:23:212:23 | u [postupdate] | provenance | Config | +| stdlib.go:212:23:212:23 | implicit dereference | stdlib.go:212:23:212:28 | selection of Path | provenance | Config Sink:MaD:1 | +| stdlib.go:212:23:212:23 | u | stdlib.go:212:23:212:23 | implicit dereference | provenance | Config | +| stdlib.go:212:23:212:23 | u | stdlib.go:212:23:212:28 | selection of Path | provenance | Config Sink:MaD:1 | +| stdlib.go:212:23:212:23 | u | stdlib.go:214:23:214:23 | u | provenance | | +| stdlib.go:212:23:212:23 | u [pointer] | stdlib.go:212:23:212:23 | implicit dereference | provenance | | +| stdlib.go:212:23:212:23 | u [postupdate] | stdlib.go:212:23:212:23 | implicit dereference | provenance | Config | +| stdlib.go:212:23:212:23 | u [postupdate] | stdlib.go:214:23:214:23 | u | provenance | | +| stdlib.go:214:23:214:23 | u | stdlib.go:214:23:214:32 | call to String | provenance | Config Sink:MaD:1 | models | 1 | Sink: net/http; ; false; Redirect; ; ; Argument[2]; url-redirection[0]; manual | | 2 | Source: net/http; Request; true; Form; ; ; ; remote; manual | @@ -119,4 +155,28 @@ nodes | stdlib.go:196:23:196:33 | selection of Path | semmle.label | selection of Path | | stdlib.go:198:23:198:28 | target | semmle.label | target | | stdlib.go:198:23:198:42 | call to EscapedPath | semmle.label | call to EscapedPath | +| stdlib.go:199:3:199:8 | implicit dereference | semmle.label | implicit dereference | +| stdlib.go:199:3:199:8 | target | semmle.label | target | +| stdlib.go:199:3:199:8 | target [postupdate] | semmle.label | target [postupdate] | +| stdlib.go:201:23:201:28 | implicit dereference | semmle.label | implicit dereference | +| stdlib.go:201:23:201:28 | target | semmle.label | target | +| stdlib.go:201:23:201:28 | target [postupdate] | semmle.label | target [postupdate] | +| stdlib.go:201:23:201:33 | selection of Path | semmle.label | selection of Path | +| stdlib.go:203:23:203:28 | target | semmle.label | target | +| stdlib.go:203:23:203:37 | call to String | semmle.label | call to String | +| stdlib.go:210:3:210:3 | implicit dereference | semmle.label | implicit dereference | +| stdlib.go:210:3:210:3 | implicit dereference [postupdate] | semmle.label | implicit dereference [postupdate] | +| stdlib.go:210:3:210:3 | u [postupdate] | semmle.label | u [postupdate] | +| stdlib.go:210:3:210:3 | u [postupdate] [pointer] | semmle.label | u [postupdate] [pointer] | +| stdlib.go:210:12:210:30 | call to FormValue | semmle.label | call to FormValue | +| stdlib.go:212:23:212:23 | implicit dereference | semmle.label | implicit dereference | +| stdlib.go:212:23:212:23 | u | semmle.label | u | +| stdlib.go:212:23:212:23 | u [pointer] | semmle.label | u [pointer] | +| stdlib.go:212:23:212:23 | u [postupdate] | semmle.label | u [postupdate] | +| stdlib.go:212:23:212:28 | selection of Path | semmle.label | selection of Path | +| stdlib.go:214:23:214:23 | u | semmle.label | u | +| stdlib.go:214:23:214:32 | call to String | semmle.label | call to String | subpaths +testFailures +| stdlib.go:201:23:201:33 | selection of Path | Fixed missing result: Alert | +| stdlib.go:203:23:203:37 | call to String | Unexpected result: Alert | diff --git a/go/ql/test/query-tests/Security/CWE-601/OpenUrlRedirect/stdlib.go b/go/ql/test/query-tests/Security/CWE-601/OpenUrlRedirect/stdlib.go index 80eb8c970b1..c32661f6688 100644 --- a/go/ql/test/query-tests/Security/CWE-601/OpenUrlRedirect/stdlib.go +++ b/go/ql/test/query-tests/Security/CWE-601/OpenUrlRedirect/stdlib.go @@ -196,9 +196,25 @@ func serveStdlib() { http.Redirect(w, r, target.Path, 301) // $ Alert // BAD: EscapedPath() does not help with that http.Redirect(w, r, target.EscapedPath(), 301) // $ Alert + target.Host = "example.com" + // BAD: Host field was overwritten but Path field remains untrusted + http.Redirect(w, r, target.Path, 301) // $ MISSING: Alert + // GOOD: untrusted Host field was overwritten + http.Redirect(w, r, target.String(), 301) }) http.HandleFunc("/ex11", func(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + + u, _ := url.Parse("http://bing.com/search?q=dotnet") + u.Host = r.FormValue("host") // $ Source + // GOOD: Path field is trusted + http.Redirect(w, r, u.Path, 301) // $ SPURIOUS: Alert + // BAD: Host field is untrusted + http.Redirect(w, r, u.String(), 301) // $ Alert + }) + + http.HandleFunc("/ex12", func(w http.ResponseWriter, r *http.Request) { // GOOD: all these fields and methods are disregarded for OpenRedirect attacks: buf := make([]byte, 100) r.Body.Read(buf)