package gateway import ( "context" "net/http" "net/http/httptest" "sync/atomic" "testing" "time" protocol "git.sechmachine.io.vn/sechmachine/VerseVDI-Protocol/gen/go/protocol" ) func TestControlPlaneRejectsHTTPBeforeSending(t *testing.T) { var requests atomic.Int64 server := httptest.NewServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) { requests.Add(1) })) defer server.Close() err := NewControlPlaneClient(server.URL, server.Client()).Heartbeat( context.Background(), validControlPlaneHeartbeat("gateway-1"), ) if err == nil { t.Fatal("HTTP control-plane URL was accepted") } if got := requests.Load(); got != 0 { t.Fatalf("HTTP control-plane received %d requests, want 0", got) } } func TestControlPlaneRejectsHTTPSRedirectToHTTPWithoutDisclosure(t *testing.T) { var downgradeRequests atomic.Int64 downgrade := httptest.NewServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) { downgradeRequests.Add(1) })) defer downgrade.Close() serverTLS, clientTLS := testTLS(t) source := httptest.NewUnstartedServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) { if len(request.TLS.PeerCertificates) == 0 { t.Error("source did not authenticate the client certificate") } http.Redirect(response, request, downgrade.URL+"/capture", http.StatusTemporaryRedirect) })) source.TLS = serverTLS source.StartTLS() defer source.Close() client := &http.Client{Transport: &http.Transport{TLSClientConfig: clientTLS}} err := NewControlPlaneClient(source.URL, client).Heartbeat( context.Background(), validControlPlaneHeartbeat("gateway-secret"), ) if err == nil { t.Fatal("HTTPS-to-HTTP redirect was accepted") } if got := downgradeRequests.Load(); got != 0 { t.Fatalf("downgrade target received %d requests, want 0", got) } } func TestControlPlanePinnedMTLSRemainsFunctional(t *testing.T) { serverTLS, clientTLS := testTLS(t) var authenticated atomic.Bool server := httptest.NewUnstartedServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) { authenticated.Store(request.TLS != nil && len(request.TLS.PeerCertificates) > 0) response.WriteHeader(http.StatusNoContent) })) server.TLS = serverTLS server.StartTLS() defer server.Close() client := &http.Client{Transport: &http.Transport{TLSClientConfig: clientTLS}} if err := NewControlPlaneClient(server.URL, client).Heartbeat( context.Background(), validControlPlaneHeartbeat("gateway-1"), ); err != nil { t.Fatalf("pinned mTLS heartbeat: %v", err) } if !authenticated.Load() { t.Fatal("server did not authenticate the client certificate") } } func validControlPlaneHeartbeat(gatewayID string) protocol.GatewayHeartbeat { return protocol.GatewayHeartbeat{ Version: "1", GatewayID: gatewayID, Sequence: 1, ObservedAt: time.Unix(1, 0).UTC().Format(time.RFC3339Nano), State: "ready", Telemetry: protocol.GatewayTelemetry{MediaPackets: 1, ProviderState: "ready"}, } }