From d10b9e665c845b302eedb8f7a38aac3842251726 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 4 Jul 2025 16:55:09 +0100 Subject: [PATCH 1/6] Fix linter warnings in Request Forgery tests --- .../Security/CWE-918/RequestForgery.expected | 12 ++++++------ .../test/query-tests/Security/CWE-918/websocket.go | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/go/ql/test/query-tests/Security/CWE-918/RequestForgery.expected b/go/ql/test/query-tests/Security/CWE-918/RequestForgery.expected index 76097b7a5c0b..914d4c3c084e 100644 --- a/go/ql/test/query-tests/Security/CWE-918/RequestForgery.expected +++ b/go/ql/test/query-tests/Security/CWE-918/RequestForgery.expected @@ -14,8 +14,8 @@ | websocket.go:129:3:129:62 | call to DialContext | websocket.go:126:21:126:31 | call to Referer | websocket.go:129:38:129:51 | untrustedInput | The $@ of this request depends on a $@. | websocket.go:129:38:129:51 | untrustedInput | WebSocket URL | websocket.go:126:21:126:31 | call to Referer | user-provided value | | websocket.go:155:3:155:45 | call to Dial | websocket.go:154:21:154:31 | call to Referer | websocket.go:155:31:155:44 | untrustedInput | The $@ of this request depends on a $@. | websocket.go:155:31:155:44 | untrustedInput | WebSocket URL | websocket.go:154:21:154:31 | call to Referer | user-provided value | | websocket.go:162:3:162:45 | call to Dial | websocket.go:160:21:160:31 | call to Referer | websocket.go:162:31:162:44 | untrustedInput | The $@ of this request depends on a $@. | websocket.go:162:31:162:44 | untrustedInput | WebSocket URL | websocket.go:160:21:160:31 | call to Referer | user-provided value | -| websocket.go:197:3:197:32 | call to BuildProxy | websocket.go:195:21:195:31 | call to Referer | websocket.go:197:18:197:31 | untrustedInput | The $@ of this request depends on a $@. | websocket.go:197:18:197:31 | untrustedInput | WebSocket URL | websocket.go:195:21:195:31 | call to Referer | user-provided value | -| websocket.go:204:3:204:25 | call to New | websocket.go:202:21:202:31 | call to Referer | websocket.go:204:11:204:24 | untrustedInput | The $@ of this request depends on a $@. | websocket.go:204:11:204:24 | untrustedInput | WebSocket URL | websocket.go:202:21:202:31 | call to Referer | user-provided value | +| websocket.go:197:7:197:36 | call to BuildProxy | websocket.go:195:21:195:31 | call to Referer | websocket.go:197:22:197:35 | untrustedInput | The $@ of this request depends on a $@. | websocket.go:197:22:197:35 | untrustedInput | WebSocket URL | websocket.go:195:21:195:31 | call to Referer | user-provided value | +| websocket.go:204:7:204:29 | call to New | websocket.go:202:21:202:31 | call to Referer | websocket.go:204:15:204:28 | untrustedInput | The $@ of this request depends on a $@. | websocket.go:204:15:204:28 | untrustedInput | WebSocket URL | websocket.go:202:21:202:31 | call to Referer | user-provided value | edges | RequestForgery.go:8:12:8:34 | call to FormValue | RequestForgery.go:11:24:11:65 | ...+... | provenance | Src:MaD:1 | | tst.go:10:13:10:35 | call to FormValue | tst.go:14:11:14:17 | tainted | provenance | Src:MaD:1 | @@ -42,8 +42,8 @@ edges | websocket.go:126:21:126:31 | call to Referer | websocket.go:129:38:129:51 | untrustedInput | provenance | Src:MaD:2 | | websocket.go:154:21:154:31 | call to Referer | websocket.go:155:31:155:44 | untrustedInput | provenance | Src:MaD:2 | | websocket.go:160:21:160:31 | call to Referer | websocket.go:162:31:162:44 | untrustedInput | provenance | Src:MaD:2 | -| websocket.go:195:21:195:31 | call to Referer | websocket.go:197:18:197:31 | untrustedInput | provenance | Src:MaD:2 | -| websocket.go:202:21:202:31 | call to Referer | websocket.go:204:11:204:24 | untrustedInput | provenance | Src:MaD:2 | +| websocket.go:195:21:195:31 | call to Referer | websocket.go:197:22:197:35 | untrustedInput | provenance | Src:MaD:2 | +| websocket.go:202:21:202:31 | call to Referer | websocket.go:204:15:204:28 | untrustedInput | provenance | Src:MaD:2 | models | 1 | Source: net/http; Request; true; FormValue; ; ; ReturnValue; remote; manual | | 2 | Source: net/http; Request; true; Referer; ; ; ReturnValue; remote; manual | @@ -80,7 +80,7 @@ nodes | websocket.go:160:21:160:31 | call to Referer | semmle.label | call to Referer | | websocket.go:162:31:162:44 | untrustedInput | semmle.label | untrustedInput | | websocket.go:195:21:195:31 | call to Referer | semmle.label | call to Referer | -| websocket.go:197:18:197:31 | untrustedInput | semmle.label | untrustedInput | +| websocket.go:197:22:197:35 | untrustedInput | semmle.label | untrustedInput | | websocket.go:202:21:202:31 | call to Referer | semmle.label | call to Referer | -| websocket.go:204:11:204:24 | untrustedInput | semmle.label | untrustedInput | +| websocket.go:204:15:204:28 | untrustedInput | semmle.label | untrustedInput | subpaths diff --git a/go/ql/test/query-tests/Security/CWE-918/websocket.go b/go/ql/test/query-tests/Security/CWE-918/websocket.go index 328200770aec..fb3882000ffb 100644 --- a/go/ql/test/query-tests/Security/CWE-918/websocket.go +++ b/go/ql/test/query-tests/Security/CWE-918/websocket.go @@ -166,7 +166,7 @@ func test() { http.HandleFunc("/ex12", func(w http.ResponseWriter, r *http.Request) { untrustedInput := r.Referer() - if "localhost" == untrustedInput { + if untrustedInput == "localhost" { dialer := gobwas.Dialer{} dialer.Dial(context.TODO(), untrustedInput) } @@ -176,8 +176,8 @@ func test() { http.HandleFunc("/ex13", func(w http.ResponseWriter, r *http.Request) { untrustedInput := r.Referer() - if "localhost" == untrustedInput { - sac.New(untrustedInput) + if untrustedInput == "localhost" { + _ = sac.New(untrustedInput) } }) @@ -185,8 +185,8 @@ func test() { http.HandleFunc("/ex14", func(w http.ResponseWriter, r *http.Request) { untrustedInput := r.Referer() - if "localhost" == untrustedInput { - sac.BuildProxy(untrustedInput) + if untrustedInput == "localhost" { + _ = sac.BuildProxy(untrustedInput) } }) @@ -194,14 +194,14 @@ func test() { http.HandleFunc("/ex15", func(w http.ResponseWriter, r *http.Request) { untrustedInput := r.Referer() - sac.BuildProxy(untrustedInput) + _ = sac.BuildProxy(untrustedInput) }) // sac007 websocket New bad http.HandleFunc("/ex16", func(w http.ResponseWriter, r *http.Request) { untrustedInput := r.Referer() - sac.New(untrustedInput) + _ = sac.New(untrustedInput) }) log.Println(http.ListenAndServe(":80", nil)) From 0788a90d886ca66d8e3817bd691962fa85ee7d96 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 4 Jul 2025 16:56:05 +0100 Subject: [PATCH 2/6] Convert RequestForgery test to inline expectations --- .../Security/CWE-918/RequestForgery.go | 4 +- .../Security/CWE-918/RequestForgery.qlref | 4 +- .../test/query-tests/Security/CWE-918/tst.go | 20 +++++----- .../query-tests/Security/CWE-918/websocket.go | 38 +++++++++---------- 4 files changed, 34 insertions(+), 32 deletions(-) diff --git a/go/ql/test/query-tests/Security/CWE-918/RequestForgery.go b/go/ql/test/query-tests/Security/CWE-918/RequestForgery.go index c88e9dc5f742..ab8f907c58b5 100644 --- a/go/ql/test/query-tests/Security/CWE-918/RequestForgery.go +++ b/go/ql/test/query-tests/Security/CWE-918/RequestForgery.go @@ -5,10 +5,10 @@ import ( ) func handler(w http.ResponseWriter, req *http.Request) { - target := req.FormValue("target") + target := req.FormValue("target") // $ Source // BAD: `target` is controlled by the attacker - resp, err := http.Get("https://" + target + ".example.com/data/") + resp, err := http.Get("https://" + target + ".example.com/data/") // $ Alert if err != nil { // error handling } diff --git a/go/ql/test/query-tests/Security/CWE-918/RequestForgery.qlref b/go/ql/test/query-tests/Security/CWE-918/RequestForgery.qlref index 061679da228d..760862973f1d 100644 --- a/go/ql/test/query-tests/Security/CWE-918/RequestForgery.qlref +++ b/go/ql/test/query-tests/Security/CWE-918/RequestForgery.qlref @@ -1,2 +1,4 @@ query: Security/CWE-918/RequestForgery.ql -postprocess: utils/test/PrettyPrintModels.ql +postprocess: + - utils/test/PrettyPrintModels.ql + - utils/test/InlineExpectationsTestQuery.ql diff --git a/go/ql/test/query-tests/Security/CWE-918/tst.go b/go/ql/test/query-tests/Security/CWE-918/tst.go index 0e04429580c8..00a09fef0f75 100644 --- a/go/ql/test/query-tests/Security/CWE-918/tst.go +++ b/go/ql/test/query-tests/Security/CWE-918/tst.go @@ -7,26 +7,26 @@ import ( ) func handler2(w http.ResponseWriter, req *http.Request) { - tainted := req.FormValue("target") + tainted := req.FormValue("target") // $ Source http.Get("example.com") // OK - http.Get(tainted) // Not OK + http.Get(tainted) // $ Alert http.Head(tainted) // OK - http.Post(tainted, "text/basic", nil) // Not OK + http.Post(tainted, "text/basic", nil) // $ Alert client := &http.Client{} - rq, _ := http.NewRequest("GET", tainted, nil) - client.Do(rq) // Not OK + rq, _ := http.NewRequest("GET", tainted, nil) // $ Sink + client.Do(rq) // $ Alert - rq, _ = http.NewRequestWithContext(context.Background(), "GET", tainted, nil) - client.Do(rq) // Not OK + rq, _ = http.NewRequestWithContext(context.Background(), "GET", tainted, nil) // $ Sink + client.Do(rq) // $ Alert - http.Get("http://" + tainted) // Not OK + http.Get("http://" + tainted) // $ Alert - http.Get("http://example.com" + tainted) // Not OK + http.Get("http://example.com" + tainted) // $ Alert http.Get("http://example.com/" + tainted) // OK @@ -34,7 +34,7 @@ func handler2(w http.ResponseWriter, req *http.Request) { u, _ := url.Parse("http://example.com/relative-path") u.Host = tainted - http.Get(u.String()) // Not OK + http.Get(u.String()) // $ Alert } func main() { diff --git a/go/ql/test/query-tests/Security/CWE-918/websocket.go b/go/ql/test/query-tests/Security/CWE-918/websocket.go index fb3882000ffb..15edef6b6cce 100644 --- a/go/ql/test/query-tests/Security/CWE-918/websocket.go +++ b/go/ql/test/query-tests/Security/CWE-918/websocket.go @@ -57,12 +57,12 @@ func test() { // x net websocket dial bad http.HandleFunc("/ex2", func(w http.ResponseWriter, r *http.Request) { - untrustedInput := r.Referer() + untrustedInput := r.Referer() // $ Source origin := "http://localhost/" // bad as input is directly passed to dial function - ws, _ := websocket.Dial(untrustedInput, "", origin) + ws, _ := websocket.Dial(untrustedInput, "", origin) // $ Alert var msg = make([]byte, 512) var n int n, _ = ws.Read(msg) @@ -71,12 +71,12 @@ func test() { // x net websocket dialConfig bad http.HandleFunc("/ex3", func(w http.ResponseWriter, r *http.Request) { - untrustedInput := r.Referer() + untrustedInput := r.Referer() // $ Source origin := "http://localhost/" // bad as input is directly used - config, _ := websocket.NewConfig(untrustedInput, origin) // good - ws2, _ := websocket.DialConfig(config) + config, _ := websocket.NewConfig(untrustedInput, origin) // $ Sink + ws2, _ := websocket.DialConfig(config) // $ Alert var msg = make([]byte, 512) var n int n, _ = ws2.Read(msg) @@ -85,10 +85,10 @@ func test() { // nhooyr websocket dial bad http.HandleFunc("/ex4", func(w http.ResponseWriter, r *http.Request) { - untrustedInput := r.Referer() + untrustedInput := r.Referer() // $ Source // bad as input is used directly - nhooyr.Dial(context.TODO(), untrustedInput, nil) + nhooyr.Dial(context.TODO(), untrustedInput, nil) // $ Alert w.WriteHeader(500) }) @@ -104,10 +104,10 @@ func test() { // gorilla websocket Dialer.Dial bad http.HandleFunc("/ex6", func(w http.ResponseWriter, r *http.Request) { - untrustedInput := r.Referer() + untrustedInput := r.Referer() // $ Source dialer := gorilla.Dialer{} - dialer.Dial(untrustedInput, r.Header) + dialer.Dial(untrustedInput, r.Header) // $ Alert }) // gorilla websocket Dialer.Dial good @@ -123,10 +123,10 @@ func test() { // gorilla websocket Dialer.DialContext bad http.HandleFunc("/ex8", func(w http.ResponseWriter, r *http.Request) { - untrustedInput := r.Referer() + untrustedInput := r.Referer() // $ Source dialer := gorilla.Dialer{} - dialer.DialContext(context.TODO(), untrustedInput, r.Header) + dialer.DialContext(context.TODO(), untrustedInput, r.Header) // $ Alert }) // gorilla websocket Dialer.DialContext good @@ -151,15 +151,15 @@ func test() { // gobwas websocket Dial bad http.HandleFunc("/ex11", func(w http.ResponseWriter, r *http.Request) { - untrustedInput := r.Referer() - gobwas.Dial(context.TODO(), untrustedInput) + untrustedInput := r.Referer() // $ Source + gobwas.Dial(context.TODO(), untrustedInput) // $ Alert }) // gobwas websocket Dialer.Dial bad http.HandleFunc("/ex12", func(w http.ResponseWriter, r *http.Request) { - untrustedInput := r.Referer() + untrustedInput := r.Referer() // $ Source dialer := gobwas.Dialer{} - dialer.Dial(context.TODO(), untrustedInput) + dialer.Dial(context.TODO(), untrustedInput) // $ Alert }) // gobwas websocket Dialer.Dial good @@ -192,16 +192,16 @@ func test() { // sac007 websocket BuildProxy bad http.HandleFunc("/ex15", func(w http.ResponseWriter, r *http.Request) { - untrustedInput := r.Referer() + untrustedInput := r.Referer() // $ Source - _ = sac.BuildProxy(untrustedInput) + _ = sac.BuildProxy(untrustedInput) // $ Alert }) // sac007 websocket New bad http.HandleFunc("/ex16", func(w http.ResponseWriter, r *http.Request) { - untrustedInput := r.Referer() + untrustedInput := r.Referer() // $ Source - _ = sac.New(untrustedInput) + _ = sac.New(untrustedInput) // $ Alert }) log.Println(http.ListenAndServe(":80", nil)) From d437a096f1063dd86f913cb4af08fe4cf02d9980 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 8 Jul 2025 13:20:04 +0100 Subject: [PATCH 3/6] Test more client request URL sinks --- .../Security/CWE-918/RequestForgery.expected | 74 +++++++++++-------- .../test/query-tests/Security/CWE-918/tst.go | 14 +++- 2 files changed, 53 insertions(+), 35 deletions(-) diff --git a/go/ql/test/query-tests/Security/CWE-918/RequestForgery.expected b/go/ql/test/query-tests/Security/CWE-918/RequestForgery.expected index 914d4c3c084e..53b348c479f8 100644 --- a/go/ql/test/query-tests/Security/CWE-918/RequestForgery.expected +++ b/go/ql/test/query-tests/Security/CWE-918/RequestForgery.expected @@ -2,11 +2,15 @@ | RequestForgery.go:11:15:11:66 | call to Get | RequestForgery.go:8:12:8:34 | call to FormValue | RequestForgery.go:11:24:11:65 | ...+... | The $@ of this request depends on a $@. | RequestForgery.go:11:24:11:65 | ...+... | URL | RequestForgery.go:8:12:8:34 | call to FormValue | user-provided value | | tst.go:14:2:14:18 | call to Get | tst.go:10:13:10:35 | call to FormValue | tst.go:14:11:14:17 | tainted | The $@ of this request depends on a $@. | tst.go:14:11:14:17 | tainted | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | | tst.go:18:2:18:38 | call to Post | tst.go:10:13:10:35 | call to FormValue | tst.go:18:12:18:18 | tainted | The $@ of this request depends on a $@. | tst.go:18:12:18:18 | tainted | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | -| tst.go:22:2:22:14 | call to Do | tst.go:10:13:10:35 | call to FormValue | tst.go:21:34:21:40 | tainted | The $@ of this request depends on a $@. | tst.go:21:34:21:40 | tainted | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | -| tst.go:25:2:25:14 | call to Do | tst.go:10:13:10:35 | call to FormValue | tst.go:24:66:24:72 | tainted | The $@ of this request depends on a $@. | tst.go:24:66:24:72 | tainted | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | -| tst.go:27:2:27:30 | call to Get | tst.go:10:13:10:35 | call to FormValue | tst.go:27:11:27:29 | ...+... | The $@ of this request depends on a $@. | tst.go:27:11:27:29 | ...+... | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | -| tst.go:29:2:29:41 | call to Get | tst.go:10:13:10:35 | call to FormValue | tst.go:29:11:29:40 | ...+... | The $@ of this request depends on a $@. | tst.go:29:11:29:40 | ...+... | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | -| tst.go:37:2:37:21 | call to Get | tst.go:10:13:10:35 | call to FormValue | tst.go:37:11:37:20 | call to String | The $@ of this request depends on a $@. | tst.go:37:11:37:20 | call to String | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | +| tst.go:20:2:20:28 | call to PostForm | tst.go:10:13:10:35 | call to FormValue | tst.go:20:16:20:22 | tainted | The $@ of this request depends on a $@. | tst.go:20:16:20:22 | tainted | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | +| tst.go:24:2:24:15 | call to Do | tst.go:10:13:10:35 | call to FormValue | tst.go:23:35:23:41 | tainted | The $@ of this request depends on a $@. | tst.go:23:35:23:41 | tainted | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | +| tst.go:27:2:27:15 | call to Do | tst.go:10:13:10:35 | call to FormValue | tst.go:26:68:26:74 | tainted | The $@ of this request depends on a $@. | tst.go:26:68:26:74 | tainted | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | +| tst.go:29:2:29:20 | call to Get | tst.go:10:13:10:35 | call to FormValue | tst.go:29:13:29:19 | tainted | The $@ of this request depends on a $@. | tst.go:29:13:29:19 | tainted | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | +| tst.go:30:2:30:40 | call to Post | tst.go:10:13:10:35 | call to FormValue | tst.go:30:14:30:20 | tainted | The $@ of this request depends on a $@. | tst.go:30:14:30:20 | tainted | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | +| tst.go:31:2:31:30 | call to PostForm | tst.go:10:13:10:35 | call to FormValue | tst.go:31:18:31:24 | tainted | The $@ of this request depends on a $@. | tst.go:31:18:31:24 | tainted | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | +| tst.go:33:2:33:30 | call to Get | tst.go:10:13:10:35 | call to FormValue | tst.go:33:11:33:29 | ...+... | The $@ of this request depends on a $@. | tst.go:33:11:33:29 | ...+... | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | +| tst.go:35:2:35:41 | call to Get | tst.go:10:13:10:35 | call to FormValue | tst.go:35:11:35:40 | ...+... | The $@ of this request depends on a $@. | tst.go:35:11:35:40 | ...+... | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | +| tst.go:43:2:43:21 | call to Get | tst.go:10:13:10:35 | call to FormValue | tst.go:43:11:43:20 | call to String | The $@ of this request depends on a $@. | tst.go:43:11:43:20 | call to String | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | | websocket.go:65:12:65:53 | call to Dial | websocket.go:60:21:60:31 | call to Referer | websocket.go:65:27:65:40 | untrustedInput | The $@ of this request depends on a $@. | websocket.go:65:27:65:40 | untrustedInput | WebSocket URL | websocket.go:60:21:60:31 | call to Referer | user-provided value | | websocket.go:79:13:79:40 | call to DialConfig | websocket.go:74:21:74:31 | call to Referer | websocket.go:78:36:78:49 | untrustedInput | The $@ of this request depends on a $@. | websocket.go:78:36:78:49 | untrustedInput | WebSocket URL | websocket.go:74:21:74:31 | call to Referer | user-provided value | | websocket.go:91:3:91:50 | call to Dial | websocket.go:88:21:88:31 | call to Referer | websocket.go:91:31:91:44 | untrustedInput | The $@ of this request depends on a $@. | websocket.go:91:31:91:44 | untrustedInput | WebSocket URL | websocket.go:88:21:88:31 | call to Referer | user-provided value | @@ -20,21 +24,25 @@ edges | RequestForgery.go:8:12:8:34 | call to FormValue | RequestForgery.go:11:24:11:65 | ...+... | provenance | Src:MaD:1 | | tst.go:10:13:10:35 | call to FormValue | tst.go:14:11:14:17 | tainted | provenance | Src:MaD:1 | | tst.go:10:13:10:35 | call to FormValue | tst.go:18:12:18:18 | tainted | provenance | Src:MaD:1 | -| tst.go:10:13:10:35 | call to FormValue | tst.go:21:34:21:40 | tainted | provenance | Src:MaD:1 | -| tst.go:10:13:10:35 | call to FormValue | tst.go:24:66:24:72 | tainted | provenance | Src:MaD:1 | -| tst.go:10:13:10:35 | call to FormValue | tst.go:27:11:27:29 | ...+... | provenance | Src:MaD:1 | -| tst.go:10:13:10:35 | call to FormValue | tst.go:29:11:29:40 | ...+... | provenance | Src:MaD:1 | -| tst.go:10:13:10:35 | call to FormValue | tst.go:36:11:36:17 | tainted | provenance | Src:MaD:1 | -| tst.go:35:2:35:2 | definition of u [pointer] | tst.go:36:2:36:2 | u [pointer] | provenance | | -| tst.go:36:2:36:2 | implicit dereference | tst.go:35:2:35:2 | definition of u [pointer] | provenance | | -| tst.go:36:2:36:2 | implicit dereference | tst.go:36:2:36:2 | u | provenance | | -| tst.go:36:2:36:2 | implicit dereference | tst.go:37:11:37:11 | u | provenance | | -| tst.go:36:2:36:2 | u | tst.go:36:2:36:2 | implicit dereference | provenance | | -| tst.go:36:2:36:2 | u | tst.go:37:11:37:11 | u | provenance | | -| tst.go:36:2:36:2 | u [pointer] | tst.go:36:2:36:2 | implicit dereference | provenance | | -| tst.go:36:11:36:17 | tainted | tst.go:36:2:36:2 | u | provenance | Config | -| tst.go:36:11:36:17 | tainted | tst.go:37:11:37:11 | u | provenance | Config | -| tst.go:37:11:37:11 | u | tst.go:37:11:37:20 | call to String | provenance | MaD:3 | +| tst.go:10:13:10:35 | call to FormValue | tst.go:20:16:20:22 | tainted | provenance | Src:MaD:1 | +| tst.go:10:13:10:35 | call to FormValue | tst.go:23:35:23:41 | tainted | provenance | Src:MaD:1 | +| tst.go:10:13:10:35 | call to FormValue | tst.go:26:68:26:74 | tainted | provenance | Src:MaD:1 | +| tst.go:10:13:10:35 | call to FormValue | tst.go:29:13:29:19 | tainted | provenance | Src:MaD:1 | +| tst.go:10:13:10:35 | call to FormValue | tst.go:30:14:30:20 | tainted | provenance | Src:MaD:1 | +| tst.go:10:13:10:35 | call to FormValue | tst.go:31:18:31:24 | tainted | provenance | Src:MaD:1 | +| tst.go:10:13:10:35 | call to FormValue | tst.go:33:11:33:29 | ...+... | provenance | Src:MaD:1 | +| tst.go:10:13:10:35 | call to FormValue | tst.go:35:11:35:40 | ...+... | provenance | Src:MaD:1 | +| tst.go:10:13:10:35 | call to FormValue | tst.go:42:11:42:17 | tainted | provenance | Src:MaD:1 | +| tst.go:41:2:41:2 | definition of u [pointer] | tst.go:42:2:42:2 | u [pointer] | provenance | | +| tst.go:42:2:42:2 | implicit dereference | tst.go:41:2:41:2 | definition of u [pointer] | provenance | | +| tst.go:42:2:42:2 | implicit dereference | tst.go:42:2:42:2 | u | provenance | | +| tst.go:42:2:42:2 | implicit dereference | tst.go:43:11:43:11 | u | provenance | | +| tst.go:42:2:42:2 | u | tst.go:42:2:42:2 | implicit dereference | provenance | | +| tst.go:42:2:42:2 | u | tst.go:43:11:43:11 | u | provenance | | +| tst.go:42:2:42:2 | u [pointer] | tst.go:42:2:42:2 | implicit dereference | provenance | | +| tst.go:42:11:42:17 | tainted | tst.go:42:2:42:2 | u | provenance | Config | +| tst.go:42:11:42:17 | tainted | tst.go:43:11:43:11 | u | provenance | Config | +| tst.go:43:11:43:11 | u | tst.go:43:11:43:20 | call to String | provenance | MaD:3 | | websocket.go:60:21:60:31 | call to Referer | websocket.go:65:27:65:40 | untrustedInput | provenance | Src:MaD:2 | | websocket.go:74:21:74:31 | call to Referer | websocket.go:78:36:78:49 | untrustedInput | provenance | Src:MaD:2 | | websocket.go:88:21:88:31 | call to Referer | websocket.go:91:31:91:44 | untrustedInput | provenance | Src:MaD:2 | @@ -54,17 +62,21 @@ nodes | tst.go:10:13:10:35 | call to FormValue | semmle.label | call to FormValue | | tst.go:14:11:14:17 | tainted | semmle.label | tainted | | tst.go:18:12:18:18 | tainted | semmle.label | tainted | -| tst.go:21:34:21:40 | tainted | semmle.label | tainted | -| tst.go:24:66:24:72 | tainted | semmle.label | tainted | -| tst.go:27:11:27:29 | ...+... | semmle.label | ...+... | -| tst.go:29:11:29:40 | ...+... | semmle.label | ...+... | -| tst.go:35:2:35:2 | definition of u [pointer] | semmle.label | definition of u [pointer] | -| tst.go:36:2:36:2 | implicit dereference | semmle.label | implicit dereference | -| tst.go:36:2:36:2 | u | semmle.label | u | -| tst.go:36:2:36:2 | u [pointer] | semmle.label | u [pointer] | -| tst.go:36:11:36:17 | tainted | semmle.label | tainted | -| tst.go:37:11:37:11 | u | semmle.label | u | -| tst.go:37:11:37:20 | call to String | semmle.label | call to String | +| tst.go:20:16:20:22 | tainted | semmle.label | tainted | +| tst.go:23:35:23:41 | tainted | semmle.label | tainted | +| tst.go:26:68:26:74 | tainted | semmle.label | tainted | +| tst.go:29:13:29:19 | tainted | semmle.label | tainted | +| tst.go:30:14:30:20 | tainted | semmle.label | tainted | +| tst.go:31:18:31:24 | tainted | semmle.label | tainted | +| tst.go:33:11:33:29 | ...+... | semmle.label | ...+... | +| tst.go:35:11:35:40 | ...+... | semmle.label | ...+... | +| tst.go:41:2:41:2 | definition of u [pointer] | semmle.label | definition of u [pointer] | +| tst.go:42:2:42:2 | implicit dereference | semmle.label | implicit dereference | +| tst.go:42:2:42:2 | u | semmle.label | u | +| tst.go:42:2:42:2 | u [pointer] | semmle.label | u [pointer] | +| tst.go:42:11:42:17 | tainted | semmle.label | tainted | +| tst.go:43:11:43:11 | u | semmle.label | u | +| tst.go:43:11:43:20 | call to String | semmle.label | call to String | | websocket.go:60:21:60:31 | call to Referer | semmle.label | call to Referer | | websocket.go:65:27:65:40 | untrustedInput | semmle.label | untrustedInput | | websocket.go:74:21:74:31 | call to Referer | semmle.label | call to Referer | diff --git a/go/ql/test/query-tests/Security/CWE-918/tst.go b/go/ql/test/query-tests/Security/CWE-918/tst.go index 00a09fef0f75..5ca6641fb06b 100644 --- a/go/ql/test/query-tests/Security/CWE-918/tst.go +++ b/go/ql/test/query-tests/Security/CWE-918/tst.go @@ -17,12 +17,18 @@ func handler2(w http.ResponseWriter, req *http.Request) { http.Post(tainted, "text/basic", nil) // $ Alert + http.PostForm(tainted, nil) // $ Alert + client := &http.Client{} - rq, _ := http.NewRequest("GET", tainted, nil) // $ Sink - client.Do(rq) // $ Alert + rq1, _ := http.NewRequest("GET", tainted, nil) // $ Sink + client.Do(rq1) // $ Alert + + rq2, _ := http.NewRequestWithContext(context.Background(), "GET", tainted, nil) // $ Sink + client.Do(rq2) // $ Alert - rq, _ = http.NewRequestWithContext(context.Background(), "GET", tainted, nil) // $ Sink - client.Do(rq) // $ Alert + client.Get(tainted) // $ Alert + client.Post(tainted, "text/basic", nil) // $ Alert + client.PostForm(tainted, nil) // $ Alert http.Get("http://" + tainted) // $ Alert From 71703aa497fecf4d0208050954c67df2eabc6636 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 8 Jul 2025 14:29:11 +0100 Subject: [PATCH 4/6] Improve formatting of some QL --- go/ql/lib/semmle/go/frameworks/stdlib/NetHttp.qll | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/go/ql/lib/semmle/go/frameworks/stdlib/NetHttp.qll b/go/ql/lib/semmle/go/frameworks/stdlib/NetHttp.qll index dded5092bcc3..97692186dccf 100644 --- a/go/ql/lib/semmle/go/frameworks/stdlib/NetHttp.qll +++ b/go/ql/lib/semmle/go/frameworks/stdlib/NetHttp.qll @@ -179,12 +179,11 @@ module NetHttp { private class RequestCall extends Http::ClientRequest::Range, DataFlow::CallNode { RequestCall() { exists(string functionName | - ( - this.getTarget().hasQualifiedName("net/http", functionName) - or - this.getTarget().(Method).hasQualifiedName("net/http", "Client", functionName) - ) and - (functionName = "Get" or functionName = "Post" or functionName = "PostForm") + this.getTarget().hasQualifiedName("net/http", functionName) + or + this.getTarget().(Method).hasQualifiedName("net/http", "Client", functionName) + | + functionName = ["Get", "Post", "PostForm"] ) } From 990043ce8686cc88ecf7a5c52a556ee76b03a771 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 8 Jul 2025 14:31:48 +0100 Subject: [PATCH 5/6] Add net/http.Head and net/http.Client.Head as client requests They were previously deliberately excluded. --- .../semmle/go/frameworks/stdlib/NetHttp.qll | 2 +- .../Security/CWE-918/RequestForgery.expected | 64 ++++++++++--------- .../test/query-tests/Security/CWE-918/tst.go | 3 +- 3 files changed, 38 insertions(+), 31 deletions(-) diff --git a/go/ql/lib/semmle/go/frameworks/stdlib/NetHttp.qll b/go/ql/lib/semmle/go/frameworks/stdlib/NetHttp.qll index 97692186dccf..9a917f05ff5c 100644 --- a/go/ql/lib/semmle/go/frameworks/stdlib/NetHttp.qll +++ b/go/ql/lib/semmle/go/frameworks/stdlib/NetHttp.qll @@ -183,7 +183,7 @@ module NetHttp { or this.getTarget().(Method).hasQualifiedName("net/http", "Client", functionName) | - functionName = ["Get", "Post", "PostForm"] + functionName = ["Get", "Head", "Post", "PostForm"] ) } diff --git a/go/ql/test/query-tests/Security/CWE-918/RequestForgery.expected b/go/ql/test/query-tests/Security/CWE-918/RequestForgery.expected index 53b348c479f8..696d8dd700b4 100644 --- a/go/ql/test/query-tests/Security/CWE-918/RequestForgery.expected +++ b/go/ql/test/query-tests/Security/CWE-918/RequestForgery.expected @@ -1,16 +1,18 @@ #select | RequestForgery.go:11:15:11:66 | call to Get | RequestForgery.go:8:12:8:34 | call to FormValue | RequestForgery.go:11:24:11:65 | ...+... | The $@ of this request depends on a $@. | RequestForgery.go:11:24:11:65 | ...+... | URL | RequestForgery.go:8:12:8:34 | call to FormValue | user-provided value | | tst.go:14:2:14:18 | call to Get | tst.go:10:13:10:35 | call to FormValue | tst.go:14:11:14:17 | tainted | The $@ of this request depends on a $@. | tst.go:14:11:14:17 | tainted | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | +| tst.go:16:2:16:19 | call to Head | tst.go:10:13:10:35 | call to FormValue | tst.go:16:12:16:18 | tainted | The $@ of this request depends on a $@. | tst.go:16:12:16:18 | tainted | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | | tst.go:18:2:18:38 | call to Post | tst.go:10:13:10:35 | call to FormValue | tst.go:18:12:18:18 | tainted | The $@ of this request depends on a $@. | tst.go:18:12:18:18 | tainted | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | | tst.go:20:2:20:28 | call to PostForm | tst.go:10:13:10:35 | call to FormValue | tst.go:20:16:20:22 | tainted | The $@ of this request depends on a $@. | tst.go:20:16:20:22 | tainted | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | | tst.go:24:2:24:15 | call to Do | tst.go:10:13:10:35 | call to FormValue | tst.go:23:35:23:41 | tainted | The $@ of this request depends on a $@. | tst.go:23:35:23:41 | tainted | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | | tst.go:27:2:27:15 | call to Do | tst.go:10:13:10:35 | call to FormValue | tst.go:26:68:26:74 | tainted | The $@ of this request depends on a $@. | tst.go:26:68:26:74 | tainted | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | | tst.go:29:2:29:20 | call to Get | tst.go:10:13:10:35 | call to FormValue | tst.go:29:13:29:19 | tainted | The $@ of this request depends on a $@. | tst.go:29:13:29:19 | tainted | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | -| tst.go:30:2:30:40 | call to Post | tst.go:10:13:10:35 | call to FormValue | tst.go:30:14:30:20 | tainted | The $@ of this request depends on a $@. | tst.go:30:14:30:20 | tainted | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | -| tst.go:31:2:31:30 | call to PostForm | tst.go:10:13:10:35 | call to FormValue | tst.go:31:18:31:24 | tainted | The $@ of this request depends on a $@. | tst.go:31:18:31:24 | tainted | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | -| tst.go:33:2:33:30 | call to Get | tst.go:10:13:10:35 | call to FormValue | tst.go:33:11:33:29 | ...+... | The $@ of this request depends on a $@. | tst.go:33:11:33:29 | ...+... | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | -| tst.go:35:2:35:41 | call to Get | tst.go:10:13:10:35 | call to FormValue | tst.go:35:11:35:40 | ...+... | The $@ of this request depends on a $@. | tst.go:35:11:35:40 | ...+... | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | -| tst.go:43:2:43:21 | call to Get | tst.go:10:13:10:35 | call to FormValue | tst.go:43:11:43:20 | call to String | The $@ of this request depends on a $@. | tst.go:43:11:43:20 | call to String | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | +| tst.go:30:2:30:21 | call to Head | tst.go:10:13:10:35 | call to FormValue | tst.go:30:14:30:20 | tainted | The $@ of this request depends on a $@. | tst.go:30:14:30:20 | tainted | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | +| tst.go:31:2:31:40 | call to Post | tst.go:10:13:10:35 | call to FormValue | tst.go:31:14:31:20 | tainted | The $@ of this request depends on a $@. | tst.go:31:14:31:20 | tainted | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | +| tst.go:32:2:32:30 | call to PostForm | tst.go:10:13:10:35 | call to FormValue | tst.go:32:18:32:24 | tainted | The $@ of this request depends on a $@. | tst.go:32:18:32:24 | tainted | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | +| tst.go:34:2:34:30 | call to Get | tst.go:10:13:10:35 | call to FormValue | tst.go:34:11:34:29 | ...+... | The $@ of this request depends on a $@. | tst.go:34:11:34:29 | ...+... | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | +| tst.go:36:2:36:41 | call to Get | tst.go:10:13:10:35 | call to FormValue | tst.go:36:11:36:40 | ...+... | The $@ of this request depends on a $@. | tst.go:36:11:36:40 | ...+... | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | +| tst.go:44:2:44:21 | call to Get | tst.go:10:13:10:35 | call to FormValue | tst.go:44:11:44:20 | call to String | The $@ of this request depends on a $@. | tst.go:44:11:44:20 | call to String | URL | tst.go:10:13:10:35 | call to FormValue | user-provided value | | websocket.go:65:12:65:53 | call to Dial | websocket.go:60:21:60:31 | call to Referer | websocket.go:65:27:65:40 | untrustedInput | The $@ of this request depends on a $@. | websocket.go:65:27:65:40 | untrustedInput | WebSocket URL | websocket.go:60:21:60:31 | call to Referer | user-provided value | | websocket.go:79:13:79:40 | call to DialConfig | websocket.go:74:21:74:31 | call to Referer | websocket.go:78:36:78:49 | untrustedInput | The $@ of this request depends on a $@. | websocket.go:78:36:78:49 | untrustedInput | WebSocket URL | websocket.go:74:21:74:31 | call to Referer | user-provided value | | websocket.go:91:3:91:50 | call to Dial | websocket.go:88:21:88:31 | call to Referer | websocket.go:91:31:91:44 | untrustedInput | The $@ of this request depends on a $@. | websocket.go:91:31:91:44 | untrustedInput | WebSocket URL | websocket.go:88:21:88:31 | call to Referer | user-provided value | @@ -23,26 +25,28 @@ edges | RequestForgery.go:8:12:8:34 | call to FormValue | RequestForgery.go:11:24:11:65 | ...+... | provenance | Src:MaD:1 | | tst.go:10:13:10:35 | call to FormValue | tst.go:14:11:14:17 | tainted | provenance | Src:MaD:1 | +| tst.go:10:13:10:35 | call to FormValue | tst.go:16:12:16:18 | tainted | provenance | Src:MaD:1 | | tst.go:10:13:10:35 | call to FormValue | tst.go:18:12:18:18 | tainted | provenance | Src:MaD:1 | | tst.go:10:13:10:35 | call to FormValue | tst.go:20:16:20:22 | tainted | provenance | Src:MaD:1 | | tst.go:10:13:10:35 | call to FormValue | tst.go:23:35:23:41 | tainted | provenance | Src:MaD:1 | | tst.go:10:13:10:35 | call to FormValue | tst.go:26:68:26:74 | tainted | provenance | Src:MaD:1 | | tst.go:10:13:10:35 | call to FormValue | tst.go:29:13:29:19 | tainted | provenance | Src:MaD:1 | | tst.go:10:13:10:35 | call to FormValue | tst.go:30:14:30:20 | tainted | provenance | Src:MaD:1 | -| tst.go:10:13:10:35 | call to FormValue | tst.go:31:18:31:24 | tainted | provenance | Src:MaD:1 | -| tst.go:10:13:10:35 | call to FormValue | tst.go:33:11:33:29 | ...+... | provenance | Src:MaD:1 | -| tst.go:10:13:10:35 | call to FormValue | tst.go:35:11:35:40 | ...+... | provenance | Src:MaD:1 | -| tst.go:10:13:10:35 | call to FormValue | tst.go:42:11:42:17 | tainted | provenance | Src:MaD:1 | -| tst.go:41:2:41:2 | definition of u [pointer] | tst.go:42:2:42:2 | u [pointer] | provenance | | -| tst.go:42:2:42:2 | implicit dereference | tst.go:41:2:41:2 | definition of u [pointer] | provenance | | -| tst.go:42:2:42:2 | implicit dereference | tst.go:42:2:42:2 | u | provenance | | -| tst.go:42:2:42:2 | implicit dereference | tst.go:43:11:43:11 | u | provenance | | -| tst.go:42:2:42:2 | u | tst.go:42:2:42:2 | implicit dereference | provenance | | -| tst.go:42:2:42:2 | u | tst.go:43:11:43:11 | u | provenance | | -| tst.go:42:2:42:2 | u [pointer] | tst.go:42:2:42:2 | implicit dereference | provenance | | -| tst.go:42:11:42:17 | tainted | tst.go:42:2:42:2 | u | provenance | Config | -| tst.go:42:11:42:17 | tainted | tst.go:43:11:43:11 | u | provenance | Config | -| tst.go:43:11:43:11 | u | tst.go:43:11:43:20 | call to String | provenance | MaD:3 | +| tst.go:10:13:10:35 | call to FormValue | tst.go:31:14:31:20 | tainted | provenance | Src:MaD:1 | +| tst.go:10:13:10:35 | call to FormValue | tst.go:32:18:32:24 | tainted | provenance | Src:MaD:1 | +| tst.go:10:13:10:35 | call to FormValue | tst.go:34:11:34:29 | ...+... | provenance | Src:MaD:1 | +| tst.go:10:13:10:35 | call to FormValue | tst.go:36:11:36:40 | ...+... | provenance | Src:MaD:1 | +| tst.go:10:13:10:35 | call to FormValue | tst.go:43:11:43:17 | tainted | provenance | Src:MaD:1 | +| tst.go:42:2:42:2 | definition of u [pointer] | tst.go:43:2:43:2 | u [pointer] | provenance | | +| tst.go:43:2:43:2 | implicit dereference | tst.go:42:2:42:2 | definition of u [pointer] | provenance | | +| tst.go:43:2:43:2 | implicit dereference | tst.go:43:2:43:2 | u | provenance | | +| tst.go:43:2:43:2 | implicit dereference | tst.go:44:11:44:11 | u | provenance | | +| tst.go:43:2:43:2 | u | tst.go:43:2:43:2 | implicit dereference | provenance | | +| tst.go:43:2:43:2 | u | tst.go:44:11:44:11 | u | provenance | | +| tst.go:43:2:43:2 | u [pointer] | tst.go:43:2:43:2 | implicit dereference | provenance | | +| tst.go:43:11:43:17 | tainted | tst.go:43:2:43:2 | u | provenance | Config | +| tst.go:43:11:43:17 | tainted | tst.go:44:11:44:11 | u | provenance | Config | +| tst.go:44:11:44:11 | u | tst.go:44:11:44:20 | call to String | provenance | MaD:3 | | websocket.go:60:21:60:31 | call to Referer | websocket.go:65:27:65:40 | untrustedInput | provenance | Src:MaD:2 | | websocket.go:74:21:74:31 | call to Referer | websocket.go:78:36:78:49 | untrustedInput | provenance | Src:MaD:2 | | websocket.go:88:21:88:31 | call to Referer | websocket.go:91:31:91:44 | untrustedInput | provenance | Src:MaD:2 | @@ -61,22 +65,24 @@ nodes | RequestForgery.go:11:24:11:65 | ...+... | semmle.label | ...+... | | tst.go:10:13:10:35 | call to FormValue | semmle.label | call to FormValue | | tst.go:14:11:14:17 | tainted | semmle.label | tainted | +| tst.go:16:12:16:18 | tainted | semmle.label | tainted | | tst.go:18:12:18:18 | tainted | semmle.label | tainted | | tst.go:20:16:20:22 | tainted | semmle.label | tainted | | tst.go:23:35:23:41 | tainted | semmle.label | tainted | | tst.go:26:68:26:74 | tainted | semmle.label | tainted | | tst.go:29:13:29:19 | tainted | semmle.label | tainted | | tst.go:30:14:30:20 | tainted | semmle.label | tainted | -| tst.go:31:18:31:24 | tainted | semmle.label | tainted | -| tst.go:33:11:33:29 | ...+... | semmle.label | ...+... | -| tst.go:35:11:35:40 | ...+... | semmle.label | ...+... | -| tst.go:41:2:41:2 | definition of u [pointer] | semmle.label | definition of u [pointer] | -| tst.go:42:2:42:2 | implicit dereference | semmle.label | implicit dereference | -| tst.go:42:2:42:2 | u | semmle.label | u | -| tst.go:42:2:42:2 | u [pointer] | semmle.label | u [pointer] | -| tst.go:42:11:42:17 | tainted | semmle.label | tainted | -| tst.go:43:11:43:11 | u | semmle.label | u | -| tst.go:43:11:43:20 | call to String | semmle.label | call to String | +| tst.go:31:14:31:20 | tainted | semmle.label | tainted | +| tst.go:32:18:32:24 | tainted | semmle.label | tainted | +| tst.go:34:11:34:29 | ...+... | semmle.label | ...+... | +| tst.go:36:11:36:40 | ...+... | semmle.label | ...+... | +| tst.go:42:2:42:2 | definition of u [pointer] | semmle.label | definition of u [pointer] | +| tst.go:43:2:43:2 | implicit dereference | semmle.label | implicit dereference | +| tst.go:43:2:43:2 | u | semmle.label | u | +| tst.go:43:2:43:2 | u [pointer] | semmle.label | u [pointer] | +| tst.go:43:11:43:17 | tainted | semmle.label | tainted | +| tst.go:44:11:44:11 | u | semmle.label | u | +| tst.go:44:11:44:20 | call to String | semmle.label | call to String | | websocket.go:60:21:60:31 | call to Referer | semmle.label | call to Referer | | websocket.go:65:27:65:40 | untrustedInput | semmle.label | untrustedInput | | websocket.go:74:21:74:31 | call to Referer | semmle.label | call to Referer | diff --git a/go/ql/test/query-tests/Security/CWE-918/tst.go b/go/ql/test/query-tests/Security/CWE-918/tst.go index 5ca6641fb06b..44a172ddad07 100644 --- a/go/ql/test/query-tests/Security/CWE-918/tst.go +++ b/go/ql/test/query-tests/Security/CWE-918/tst.go @@ -13,7 +13,7 @@ func handler2(w http.ResponseWriter, req *http.Request) { http.Get(tainted) // $ Alert - http.Head(tainted) // OK + http.Head(tainted) // $ Alert http.Post(tainted, "text/basic", nil) // $ Alert @@ -27,6 +27,7 @@ func handler2(w http.ResponseWriter, req *http.Request) { client.Do(rq2) // $ Alert client.Get(tainted) // $ Alert + client.Head(tainted) // $ Alert client.Post(tainted, "text/basic", nil) // $ Alert client.PostForm(tainted, nil) // $ Alert From a5333ae1a10c5a10663483b86f6d4aa0770a48ef Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 8 Jul 2025 16:51:22 +0100 Subject: [PATCH 6/6] Add change note --- go/ql/lib/change-notes/2025-07-08-request-forgery-sinks.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 go/ql/lib/change-notes/2025-07-08-request-forgery-sinks.md diff --git a/go/ql/lib/change-notes/2025-07-08-request-forgery-sinks.md b/go/ql/lib/change-notes/2025-07-08-request-forgery-sinks.md new file mode 100644 index 000000000000..6204749d9d62 --- /dev/null +++ b/go/ql/lib/change-notes/2025-07-08-request-forgery-sinks.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added models for the `Head` function and the `Client.Head` method, from the `net/http` package, to the `Http::ClientRequest` class. This means that they will be recognized as sinks for the query `go/request-forgery` and the experimental query `go/ssrf`.