From c70609e6be38e7f2081f500cd56b1627f467b5d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20Daba=C5=A1inskas?= Date: Mon, 11 Aug 2025 10:49:47 +0100 Subject: [PATCH] [SREP-1559] check that when used, proxy url starts with http and do proxy hostname dns lookup to confirm connectivity --- pkg/cli/config/config.go | 12 +++++ pkg/cli/config/config_test.go | 84 +++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/pkg/cli/config/config.go b/pkg/cli/config/config.go index 2fd0a15b..76fdc48a 100644 --- a/pkg/cli/config/config.go +++ b/pkg/cli/config/config.go @@ -3,6 +3,7 @@ package config import ( "context" "fmt" + "net" "net/http" "net/url" "os" @@ -222,6 +223,8 @@ func GetBackplaneConfiguration() (bpConfig BackplaneConfiguration, err error) { return bpConfig, nil } +var lookupHost = net.LookupHost + var testProxy = func(ctx context.Context, testURL string, proxyURL url.URL) error { // Try call the test URL via the proxy client := &http.Client{ @@ -410,6 +413,15 @@ func (config *BackplaneConfiguration) testHTTPRequestToBackplaneAPI() (bool, err if err != nil { return false, err } + scheme := proxyURL.Scheme + if scheme != "http" && scheme != "https" { + return false, fmt.Errorf("proxy URL scheme must be http or https, got: %s", scheme) + } + hostname := proxyURL.Hostname() + _, err = lookupHost(hostname) + if err != nil { + return false, fmt.Errorf("DNS lookup failed for proxy hostname '%s': %v (check network connectivity or VPN if required)", hostname, err) + } http.DefaultTransport = &http.Transport{Proxy: http.ProxyURL(proxyURL)} } diff --git a/pkg/cli/config/config_test.go b/pkg/cli/config/config_test.go index 91f6c49f..bd029e13 100644 --- a/pkg/cli/config/config_test.go +++ b/pkg/cli/config/config_test.go @@ -7,6 +7,7 @@ import ( "net/http/httptest" "net/url" "os" + "strings" "testing" "github.com/spf13/viper" @@ -85,6 +86,89 @@ func TestGetBackplaneConnection(t *testing.T) { t.Failed() } }) + + t.Run("should pass for valid http proxy URL", func(t *testing.T) { + proxyURL := "http://www.example.com" + config := BackplaneConfiguration{URL: "https://api.example.com", ProxyURL: &proxyURL} + _, err := config.testHTTPRequestToBackplaneAPI() + + // Should not get scheme validation error (DNS lookup error is expected) + if err != nil && strings.Contains(err.Error(), "proxy URL scheme must be http or https") { + t.Errorf("unexpected scheme validation error: %v", err) + } + }) + + t.Run("should pass for valid https proxy URL", func(t *testing.T) { + proxyURL := "https://www.example.com" + config := BackplaneConfiguration{URL: "https://api.example.com", ProxyURL: &proxyURL} + _, err := config.testHTTPRequestToBackplaneAPI() + + // Should not get scheme validation error (DNS lookup error is expected) + if err != nil && strings.Contains(err.Error(), "proxy URL scheme must be http or https") { + t.Errorf("unexpected scheme validation error: %v", err) + } + }) + + t.Run("should fail for proxy URL without scheme", func(t *testing.T) { + proxyURL := "www.example.com" + config := BackplaneConfiguration{URL: "https://api.example.com", ProxyURL: &proxyURL} + _, err := config.testHTTPRequestToBackplaneAPI() + + if err == nil { + t.Errorf("expected error but got none") + } else if !strings.Contains(err.Error(), "proxy URL scheme must be http or https, got:") { + t.Errorf("expected scheme validation error, got: %s", err.Error()) + } + }) + + t.Run("should fail for ftp proxy URL", func(t *testing.T) { + proxyURL := "ftp://www.example.com" + config := BackplaneConfiguration{URL: "https://api.example.com", ProxyURL: &proxyURL} + _, err := config.testHTTPRequestToBackplaneAPI() + + if err == nil { + t.Errorf("expected error but got none") + } else if !strings.Contains(err.Error(), "proxy URL scheme must be http or https, got: ftp") { + t.Errorf("expected scheme validation error for ftp, got: %s", err.Error()) + } + }) + + t.Run("should fail on DNS lookup error", func(t *testing.T) { + // Mock DNS lookup to return an error + originalLookupHost := lookupHost + lookupHost = func(hostname string) ([]string, error) { + return nil, fmt.Errorf("DNS resolution failed") + } + defer func() { lookupHost = originalLookupHost }() + + proxyURL := "https://proxy.example.com" + config := BackplaneConfiguration{URL: "https://api.example.com", ProxyURL: &proxyURL} + _, err := config.testHTTPRequestToBackplaneAPI() + + if err == nil { + t.Errorf("expected DNS lookup error but got none") + } else if !strings.Contains(err.Error(), "DNS lookup failed for proxy hostname") { + t.Errorf("expected DNS lookup error, got: %s", err.Error()) + } + }) + + t.Run("should pass on successful DNS lookup", func(t *testing.T) { + // Mock DNS lookup to succeed + originalLookupHost := lookupHost + lookupHost = func(hostname string) ([]string, error) { + return []string{"192.168.1.1"}, nil + } + defer func() { lookupHost = originalLookupHost }() + + proxyURL := "https://proxy.example.com" + config := BackplaneConfiguration{URL: "https://api.example.com", ProxyURL: &proxyURL} + _, err := config.testHTTPRequestToBackplaneAPI() + + // Should not get DNS lookup error (HTTP request error is expected) + if err != nil && strings.Contains(err.Error(), "DNS lookup failed for proxy hostname") { + t.Errorf("unexpected DNS lookup error: %v", err) + } + }) } func TestBackplaneConfiguration_getFirstWorkingProxyURL(t *testing.T) {