audit_test.go
1 package memory 2 3 import ( 4 "strings" 5 "testing" 6 ) 7 8 // TestAudit_NeverContainsKey enforces the privacy invariant from spec §7: 9 // 10 // (a) no string field across the whole event payload contains the resolved 11 // API key bytes (substring assertion); 12 // (b) any field describing key/endpoint state is a bool, not a string. 13 // 14 // This is the schema-shape gate that prevents a future bug where someone 15 // interpolates the api_key into an "error context" string by accident. 16 func TestAudit_NeverContainsKey(t *testing.T) { 17 captured := []map[string]any{} 18 a := AuditFunc(func(_ string, fields map[string]any) { 19 captured = append(captured, fields) 20 }) 21 key := "secret-API-KEY-do-not-leak-1234567890" 22 fp := Fingerprint(key) 23 24 // Realistic event payloads matching the §7 audit-event list. 25 a.Log("memory_tlm_missing", map[string]any{"tlm_path_set": false}) 26 a.Log("memory_cloud_misconfigured", map[string]any{"endpoint_resolved": false, "api_key_present": false}) 27 a.Log("memory_tenant_switch", map[string]any{"fingerprint": fp}) 28 a.Log("memory_sidecar_degraded", map[string]any{}) 29 a.Log("memory_reload_failed", map[string]any{"reason": "timeout"}) 30 a.Log("memory_response_decode_failed", map[string]any{"sub_code": "x"}) 31 a.Log("memory_bundle_install_failed", map[string]any{"reason": "sha256_mismatch", "path_sample": "data.bin"}) 32 a.Log("memory_bundle_unsafe_path", map[string]any{"path_sample": "../../../etc/passwd", "reason": "contains parent traversal"}) 33 34 for _, p := range captured { 35 for k, v := range p { 36 if s, ok := v.(string); ok && strings.Contains(s, key) { 37 t.Fatalf("api key leaked in field %q: %q", k, s) 38 } 39 // Boolean-only convention for key/endpoint state. 40 switch k { 41 case "api_key_present", "endpoint_resolved", "tlm_path_set": 42 if _, ok := v.(bool); !ok { 43 t.Fatalf("field %q must be bool, got %T", k, v) 44 } 45 } 46 } 47 } 48 }