package gateway import ( "errors" "strings" "testing" "time" protocol "git.sechmachine.io.vn/sechmachine/VerseVDI-Protocol/gen/go/protocol" ) func TestValidateGatewayClipboardEnforcesServerOwnedPolicy(t *testing.T) { policy := protocol.ClipboardPolicy{ClientToProviderEnabled: true, ProviderToClientEnabled: true, MaxTextBytes: 5, MaxUpdatesPerMinute: 2} valid := protocol.GatewayClipboardText{Direction: "client_to_provider", Text: "hello", Encoding: "utf-8", LoopToken: "abcdefghijklmnop"} if err := ValidateGatewayClipboard(policy, valid); err != nil { t.Fatalf("ValidateGatewayClipboard() valid text = %v", err) } if err := ValidateGatewayClipboard(policy, protocol.GatewayClipboardText{Direction: "client_to_provider", Text: "hello", Encoding: "utf-8", LoopToken: "!!!!!!!!!!!!!!!!"}); err == nil { t.Fatal("ValidateGatewayClipboard() accepted malformed loop token") } if err := ValidateGatewayClipboard(policy, protocol.GatewayClipboardText{Direction: "client_to_provider", Text: strings.Repeat("x", 6), Encoding: "utf-8", LoopToken: "abcdefghijklmnop"}); err == nil { t.Fatal("ValidateGatewayClipboard() accepted oversized text") } disabled := policy disabled.ClientToProviderEnabled = false if err := ValidateGatewayClipboard(disabled, valid); err == nil { t.Fatal("ValidateGatewayClipboard() accepted disabled direction") } } func TestClipboardGateSuppressesReflectionsAndBoundsRate(t *testing.T) { now := time.Date(2026, time.January, 1, 0, 0, 0, 0, time.UTC) policy := protocol.ClipboardPolicy{ClientToProviderEnabled: true, ProviderToClientEnabled: true, MaxTextBytes: 64, MaxUpdatesPerMinute: 2} gate, err := newClipboardGate(policy, func() time.Time { return now }) if err != nil { t.Fatal(err) } client := protocol.GatewayClipboardText{Direction: "client_to_provider", Text: "client", Encoding: "utf-8", LoopToken: "abcdefghijklmnop"} if suppress, err := gate.fromClient(client); err != nil || suppress { t.Fatalf("fromClient() = suppress %t, err %v", suppress, err) } if _, suppress, err := gate.fromProvider("client"); err != nil || !suppress { t.Fatalf("fromProvider() reflection = suppress %t, err %v", suppress, err) } host, suppress, err := gate.fromProvider("host") if err != nil || suppress || host.Direction != "provider_to_client" { t.Fatalf("fromProvider() host = %#v, suppress %t, err %v", host, suppress, err) } if suppress, err := gate.fromClient(host); err != nil || !suppress { t.Fatalf("fromClient() host reflection = suppress %t, err %v", suppress, err) } if _, err := gate.fromClient(protocol.GatewayClipboardText{Direction: "client_to_provider", Text: "third", Encoding: "utf-8", LoopToken: "qrstuvwxyzABCDEF"}); !errors.Is(err, ErrClipboardRate) { t.Fatalf("fromClient() rate error = %v, want ErrClipboardRate", err) } now = now.Add(time.Minute) if suppress, err := gate.fromClient(protocol.GatewayClipboardText{Direction: "client_to_provider", Text: "after-window", Encoding: "utf-8", LoopToken: "0123456789abcdef"}); err != nil || suppress { t.Fatalf("fromClient() after window = suppress %t, err %v", suppress, err) } } func TestClipboardGatePermitsRetryAfterProviderWriteFailure(t *testing.T) { policy := protocol.ClipboardPolicy{ClientToProviderEnabled: true, MaxTextBytes: 64, MaxUpdatesPerMinute: 2} gate, err := newClipboardGate(policy, time.Now) if err != nil { t.Fatal(err) } value := protocol.GatewayClipboardText{Direction: "client_to_provider", Text: "retry", Encoding: "utf-8", LoopToken: "abcdefghijklmnop"} if suppress, err := gate.fromClient(value); err != nil || suppress { t.Fatalf("fromClient() = suppress %t, err %v", suppress, err) } gate.retractClient(value) if suppress, err := gate.fromClient(value); err != nil || suppress { t.Fatalf("fromClient() retry = suppress %t, err %v", suppress, err) } }