11package main
22
33import (
4+ "bufio"
45 "crypto/tls"
56 "encoding/json"
67 "fmt"
7- "io"
88 "net"
99 "net/http"
1010 "net/url"
@@ -211,36 +211,61 @@ func splitServerValue(v string) (host, port string) {
211211}
212212
213213func httpProbe (c * models.Connection , displayURL string ) testResult {
214- u , err := validatedHTTPProbeURL (c .URL )
214+ target , err := validatedHTTPProbeTarget (c .URL )
215215 if err != nil {
216216 return testResult {OK : false , Message : err .Error (), URL : displayURL }
217217 }
218- client := & http.Client {
219- Timeout : 8 * time .Second ,
220- Transport : & http.Transport {
221- TLSClientConfig : & tls.Config {InsecureSkipVerify : c .InsecureTLS }, //nolint:gosec // operator opt-in via insecure_tls
222- },
223- }
224- req , err := http .NewRequest (http .MethodGet , u .String (), http .NoBody )
218+ conn , err := (& net.Dialer {Timeout : 8 * time .Second }).Dial ("tcp" , target .hostPort )
225219 if err != nil {
220+ return testResult {OK : false , Message : fmt .Sprintf ("HTTP connect failed: %v" , redactError (err , c .URL , displayURL )), URL : displayURL }
221+ }
222+ defer func () { _ = conn .Close () }()
223+
224+ if target .url .Scheme == "https" {
225+ tlsConn := tls .Client (conn , & tls.Config {
226+ ServerName : target .url .Hostname (),
227+ InsecureSkipVerify : c .InsecureTLS , //nolint:gosec // operator opt-in via insecure_tls
228+ })
229+ if err := tlsConn .Handshake (); err != nil {
230+ return testResult {OK : false , Message : fmt .Sprintf ("TLS handshake failed: %v" , redactError (err , c .URL , displayURL )), URL : displayURL }
231+ }
232+ conn = tlsConn
233+ }
234+
235+ deadline := time .Now ().Add (8 * time .Second )
236+ _ = conn .SetDeadline (deadline )
237+ req := & http.Request {
238+ Method : http .MethodGet ,
239+ URL : target .url ,
240+ Host : target .hostHeader ,
241+ Header : http.Header {"Connection" : []string {"close" }},
242+ Proto : "HTTP/1.1" ,
243+ ProtoMajor : 1 ,
244+ ProtoMinor : 1 ,
245+ Body : http .NoBody ,
246+ }
247+ if err := req .Write (conn ); err != nil {
226248 return testResult {OK : false , Message : fmt .Sprintf ("HTTP request failed: %v" , redactError (err , c .URL , displayURL )), URL : displayURL }
227249 }
228- // codeql[go/request-forgery]: connection testing intentionally probes the
229- // operator-supplied URL after validating it is an absolute HTTP(S) URL.
230- resp , err := client .Do (req )
250+ resp , err := http .ReadResponse (bufio .NewReader (conn ), req )
231251 if err != nil {
232252 return testResult {OK : false , Message : fmt .Sprintf ("HTTP request failed: %v" , redactError (err , c .URL , displayURL )), URL : displayURL }
233253 }
234254 defer func () { _ = resp .Body .Close () }()
235- _ , _ = io .Copy (io .Discard , resp .Body )
236255 return testResult {
237256 OK : resp .StatusCode < 500 ,
238257 Message : fmt .Sprintf ("HTTP %s" , resp .Status ),
239258 URL : displayURL ,
240259 }
241260}
242261
243- func validatedHTTPProbeURL (rawURL string ) (* url.URL , error ) {
262+ type httpProbeTarget struct {
263+ url * url.URL
264+ hostPort string
265+ hostHeader string
266+ }
267+
268+ func validatedHTTPProbeTarget (rawURL string ) (* httpProbeTarget , error ) {
244269 u , err := url .Parse (rawURL )
245270 if err != nil {
246271 return nil , fmt .Errorf ("invalid HTTP URL: %w" , err )
@@ -251,14 +276,28 @@ func validatedHTTPProbeURL(rawURL string) (*url.URL, error) {
251276 if u .Hostname () == "" {
252277 return nil , fmt .Errorf ("HTTP probe requires a URL host" )
253278 }
254- return u , nil
279+ port := u .Port ()
280+ if port == "" {
281+ port = defaultPort (u .Scheme )
282+ }
283+ if port == "" {
284+ return nil , fmt .Errorf ("HTTP probe requires a URL port" )
285+ }
286+
287+ probeURL := * u
288+ probeURL .User = nil
289+ return & httpProbeTarget {
290+ url : & probeURL ,
291+ hostPort : net .JoinHostPort (u .Hostname (), port ),
292+ hostHeader : u .Host ,
293+ }, nil
255294}
256295
257296func redactConnectionURL (rawURL string ) string {
258297 if rawURL == "" {
259298 return ""
260299 }
261- if u , err := url .Parse (rawURL ); err == nil && u .Scheme != "" && u . Host != "" {
300+ if u , err := url .Parse (rawURL ); err == nil && u .Scheme != "" {
262301 redacted := * u
263302 redacted .User = nil
264303 q := redacted .Query ()
0 commit comments