package securityaudit import ( "bytes" "encoding/json" "log/slog" "testing" "time" "github.com/stretchr/testify/require" ) func TestPromptAuditLogAllowlistAndErrorsDoNotLeakCanarySecrets(t *testing.T) { const canary = "PROMPT_AUDIT_CANARY_SECRET_DO_NOT_PERSIST" var output bytes.Buffer previous := slog.Default() slog.SetDefault(slog.New(slog.NewJSONHandler(&output, nil))) t.Cleanup(func() { slog.SetDefault(previous) }) LogWarn(EventConfigReloadDegraded, map[string]any{ "status": "degraded", "error_code": "config_reload_failed", "error_kind": "Authorization: Bearer " + canary, "token": canary, "body": canary, "base_url": "https://guard.example.test/path?api_key=" + canary, "raw_prompt": "prompt " + canary, }) require.NotContains(t, output.String(), canary) require.NotContains(t, output.String(), "api_key=") require.Contains(t, output.String(), EventConfigReloadDegraded) beforeUnknown := output.Len() LogWarn("prompt_audit.typo_event", map[string]any{"status": "failed"}) require.Equal(t, beforeUnknown, output.Len(), "events outside the stable dictionary must not be emitted") require.Len(t, knownLogEvents, 28) _, err := NormalizeBaseURL("https://guard.example.test/path?token=" + canary) require.Error(t, err) require.NotContains(t, err.Error(), canary) } func TestPromptGuardFailureLogUsesCompleteAllowlistedContextAndNoSideEffects(t *testing.T) { var output bytes.Buffer previous := slog.Default() slog.SetDefault(slog.New(slog.NewJSONHandler(&output, nil))) t.Cleanup(func() { slog.SetDefault(previous) }) groupID := int64(9) snapshot := PromptSnapshot{ RequestID: "req-1", UserID: 2, APIKeyID: 3, GroupID: &groupID, Provider: "openai", Protocol: "openai_chat", Endpoint: "/v1/chat/completions", Model: "gpt-test", Stage: "http", } logGuardFailure(snapshot, ActiveConfig{ConfigVersion: 7}, DecisionUnavailable, ErrorCodeUnavailable, "guard-1", 25*time.Millisecond) var entry map[string]any require.NoError(t, json.Unmarshal(output.Bytes(), &entry)) for key := range snapshotLogFields(snapshot) { require.Contains(t, entry, key) } require.EqualValues(t, 7, entry["config_version"]) require.Equal(t, ErrorCodeUnavailable, entry["error_code"]) require.Equal(t, false, entry["upstream_dispatched"]) require.Equal(t, false, entry["billing_preconsumed"]) require.EqualValues(t, 25, entry["latency_ms"]) }