package httpapi import ( "encoding/json" "net/http" "net/http/httptest" "strings" "testing" "github.com/easyai/easyai-ai-gateway/apps/api/internal/auth" "github.com/easyai/easyai-ai-gateway/apps/api/internal/config" ssfreceiver "github.com/easyai/easyai-ai-gateway/apps/api/internal/securityevents" ) func TestSecurityEventConnectionTraceAndAuditProjection(t *testing.T) { request := httptest.NewRequest(http.MethodPut, "/connection", nil) request.Header.Set("Authorization", "Bearer must-not-escape") recorder := httptest.NewRecorder() traceID := ensureSecurityEventTraceID(recorder, request) if traceID == "" || recorder.Header().Get("X-Trace-Id") != traceID { t.Fatalf("trace header=%q trace=%q", recorder.Header().Get("X-Trace-Id"), traceID) } actor := &auth.User{ID: "admin-id", Username: "admin", Source: "local", Roles: []string{"manager"}} connection := ssfreceiver.ConnectionView{ConnectionID: "connection-id", LifecycleStatus: "error", HealthMode: "introspection_fallback"} input := securityEventConnectionAuditInput(request, actor, "connect", "failure", "management_token_failed", traceID, connection) payload, err := json.Marshal(input) if err != nil { t.Fatal(err) } if input.Action != "identity.security_event_connection.connect" || input.TargetID != "connection-id" || input.Metadata["traceId"] != traceID { t.Fatalf("audit input=%#v", input) } for _, forbidden := range []string{"must-not-escape", "authorization_header", "push_bearer", "credential_ref"} { if strings.Contains(strings.ToLower(string(payload)), forbidden) { t.Fatalf("audit payload exposed forbidden field %q: %s", forbidden, payload) } } } func TestSecurityEventConnectionWriteHeaders(t *testing.T) { request := httptest.NewRequest(http.MethodPost, "/connection/verify", nil) recorder := httptest.NewRecorder() if _, ok := requiredConnectionIdempotencyKey(recorder, request); ok || recorder.Code != http.StatusBadRequest { t.Fatalf("missing Idempotency-Key status=%d", recorder.Code) } request = httptest.NewRequest(http.MethodPost, "/connection/verify", nil) request.Header.Set("If-Match", `W/"7"`) recorder = httptest.NewRecorder() if !matchConnectionVersion(recorder, request, 7) { t.Fatalf("valid weak ETag was rejected: status=%d", recorder.Code) } request.Header.Set("If-Match", `"6"`) recorder = httptest.NewRecorder() if matchConnectionVersion(recorder, request, 7) || recorder.Code != http.StatusPreconditionFailed { t.Fatalf("stale ETag status=%d", recorder.Code) } if normalizedConnectionETag(`W/"7"`) != "7" || securityEventOperationHash("verify", "7") == securityEventOperationHash("verify", "8") { t.Fatal("security event idempotency request fingerprint is not stable") } } func TestSecurityEventPrerequisitesNeverExposeSecrets(t *testing.T) { server := &Server{cfg: config.Config{ OIDCEnabled: true, OIDCIssuer: "https://auth.example/issuer/shared", OIDCTenantID: "stable-tenant", OIDCIntrospectionClientID: "gateway-machine", OIDCIntrospectionClientSecret: "must-not-escape", PublicBaseURL: "https://gateway.example", }} payload, err := json.Marshal(server.securityEventPrerequisites()) if err != nil { t.Fatal(err) } if strings.Contains(string(payload), "must-not-escape") || strings.Contains(strings.ToLower(string(payload)), "secret") { t.Fatalf("secret escaped in prerequisites: %s", payload) } }