package audit import ( "context" "strings" "testing" "gitea.stevedudenhoeffer.com/steve/majordomo/llm" ) // TestOnToolRedactsSecretTools: a secret-bearing tool's args/result must NOT be // persisted verbatim in the audit log. func TestOnToolRedactsSecretTools(t *testing.T) { ctx := context.Background() mem := NewMemory() mem.StartRun(ctx, SkillRun{ID: "r1"}) w := NewWriter(mem, "r1") secret := `{"url":"https://x","headers":{"Authorization":"Bearer SUPERSECRET"}}` w.OnTool(llm.ToolCall{Name: "http_get", ID: "1", Arguments: []byte(secret)}, "TOPSECRETBODY") // a non-secret tool is logged verbatim w.OnTool(llm.ToolCall{Name: "think", ID: "2", Arguments: []byte(`{"thought":"hi"}`)}, "ok") logs, _ := mem.ListLogsByRun(ctx, "r1") var dump strings.Builder for _, l := range logs { for k, v := range l.Payload { dump.WriteString(k) dump.WriteString("=") if s, ok := v.(string); ok { dump.WriteString(s) } dump.WriteString(" ") } } all := dump.String() if strings.Contains(all, "SUPERSECRET") || strings.Contains(all, "TOPSECRETBODY") { t.Fatalf("secret leaked into audit log: %s", all) } // the redaction marker is present, and the non-secret tool's args survive foundRedacted, foundThink := false, false for _, l := range logs { if l.EventType == "tool_call" { if r, _ := l.Payload["args_redacted"].(bool); r { foundRedacted = true } if a, _ := l.Payload["args"].(string); strings.Contains(a, "thought") { foundThink = true } } } if !foundRedacted { t.Error("secret tool_call should carry args_redacted=true") } if !foundThink { t.Error("non-secret tool args should be logged verbatim") } }