prompt_logging_test.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. package securityaudit
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "log/slog"
  6. "testing"
  7. "time"
  8. "github.com/stretchr/testify/require"
  9. )
  10. func TestPromptAuditLogAllowlistAndErrorsDoNotLeakCanarySecrets(t *testing.T) {
  11. const canary = "PROMPT_AUDIT_CANARY_SECRET_DO_NOT_PERSIST"
  12. var output bytes.Buffer
  13. previous := slog.Default()
  14. slog.SetDefault(slog.New(slog.NewJSONHandler(&output, nil)))
  15. t.Cleanup(func() { slog.SetDefault(previous) })
  16. LogWarn(EventConfigReloadDegraded, map[string]any{
  17. "status": "degraded",
  18. "error_code": "config_reload_failed",
  19. "error_kind": "Authorization: Bearer " + canary,
  20. "token": canary,
  21. "body": canary,
  22. "base_url": "https://guard.example.test/path?api_key=" + canary,
  23. "raw_prompt": "prompt " + canary,
  24. })
  25. require.NotContains(t, output.String(), canary)
  26. require.NotContains(t, output.String(), "api_key=")
  27. require.Contains(t, output.String(), EventConfigReloadDegraded)
  28. beforeUnknown := output.Len()
  29. LogWarn("prompt_audit.typo_event", map[string]any{"status": "failed"})
  30. require.Equal(t, beforeUnknown, output.Len(), "events outside the stable dictionary must not be emitted")
  31. require.Len(t, knownLogEvents, 28)
  32. _, err := NormalizeBaseURL("https://guard.example.test/path?token=" + canary)
  33. require.Error(t, err)
  34. require.NotContains(t, err.Error(), canary)
  35. }
  36. func TestPromptGuardFailureLogUsesCompleteAllowlistedContextAndNoSideEffects(t *testing.T) {
  37. var output bytes.Buffer
  38. previous := slog.Default()
  39. slog.SetDefault(slog.New(slog.NewJSONHandler(&output, nil)))
  40. t.Cleanup(func() { slog.SetDefault(previous) })
  41. groupID := int64(9)
  42. snapshot := PromptSnapshot{
  43. RequestID: "req-1", UserID: 2, APIKeyID: 3, GroupID: &groupID,
  44. Provider: "openai", Protocol: "openai_chat", Endpoint: "/v1/chat/completions",
  45. Model: "gpt-test", Stage: "http",
  46. }
  47. logGuardFailure(snapshot, ActiveConfig{ConfigVersion: 7}, DecisionUnavailable, ErrorCodeUnavailable, "guard-1", 25*time.Millisecond)
  48. var entry map[string]any
  49. require.NoError(t, json.Unmarshal(output.Bytes(), &entry))
  50. for key := range snapshotLogFields(snapshot) {
  51. require.Contains(t, entry, key)
  52. }
  53. require.EqualValues(t, 7, entry["config_version"])
  54. require.Equal(t, ErrorCodeUnavailable, entry["error_code"])
  55. require.Equal(t, false, entry["upstream_dispatched"])
  56. require.Equal(t, false, entry["billing_preconsumed"])
  57. require.EqualValues(t, 25, entry["latency_ms"])
  58. }