package logging import ( "bytes" "encoding/base64" "errors" "git.ipao.vip/rogee/go-sip/internal/media" "io" "log/slog" "net/http" "net/http/httptest" "net/url" "strings" "testing" ) func TestAssociatedSensitiveControlsHideContentWithoutSwallowingBusiness(t *testing.T) { body := `

business-before

private-transcript

business-after

` safe, err := RedactText([]byte(body)) if err != nil { t.Fatal(err) } for _, secret := range []string{"private-password", "private-full-prompt", "private-full-dialogue", "private-transcript"} { if strings.Contains(safe, secret) { t.Fatalf("leak %s: %s", secret, safe) } } for _, keep := range []string{"business-before", "business-after"} { if !strings.Contains(safe, keep) { t.Fatal("non-sensitive markup lost", safe) } } } func TestContentFieldClassificationAndEveryLoggingPathAgree(t *testing.T) { fields := []string{"conversation", "history", "messages", "system_message", "final_transcript", "message_content", "utterance", "sentence", "sentences", "words", "opening_remark", "opening_remarks", "greeting", "chat_history", "conversation_history", "dialogue"} for _, field := range fields { t.Run(field, func(t *testing.T) { if !sensitive(field) || !conversationField(field) { t.Fatalf("content field classifications disagree: %s sensitive=%v continuation=%v", field, sensitive(field), conversationField(field)) } textValue := "upstream failed\n" + field + ":\n\nuser: private-user-turn\n\nassistant: private-assistant-turn\n\nstatus=retry_pending\nattempts=2" jsonValue := `{"` + field + `":[{"role":"user","message":"private-user-turn"},{"role":"assistant","message":"private-assistant-turn"}],"task_id":4,"status":"retry_pending"}` for _, debug := range []bool{false, true} { out := installTestLogger(t, debug) slog.Error("failed", "error", errors.New(textValue), "detail", textValue) slog.Info(textValue, "stage", "failure_observed") slog.Error("failed", "error", errors.New("upstream: "+jsonValue), "detail", jsonValue) TextBody(t.Context(), "HTTP text observed", []byte(textValue), "transport", "http") Body(t.Context(), "HTTP JSON observed", []byte(jsonValue), "transport", "http") text := out.String() for _, secret := range []string{"private-user-turn", "private-assistant-turn"} { if strings.Contains(text, secret) { t.Fatalf("%s debug %v leak: %s", field, debug, text) } } for _, keep := range []string{"upstream failed", "retry_pending", "task_id", "attempts=2"} { if !strings.Contains(text, keep) { t.Fatal("business diagnostics lost", text) } } } }) } } func TestInlineWAVDataURIsOnlyExposeTypeAndLength(t *testing.T) { pcm := make([]byte, 1600) for i := range pcm { pcm[i] = byte(i%239 + 1) } wav, _, err := media.EncodeMonoWAV(pcm, 2048) if err != nil { t.Fatal(err) } encoded := base64.StdEncoding.EncodeToString(wav) escaped := url.PathEscape(string(wav)) for _, uri := range []string{"data:audio/wav;base64," + encoded, "DATA:AUDIO/WAV;BASE64," + encoded, "data:audio/wav;codec=pcm;base64," + encoded, "data:audio/wav," + escaped} { for _, debug := range []bool{false, true} { out := installTestLogger(t, debug) for _, kind := range []string{"text/html", "text/plain", "application/json", "application/x-www-form-urlencoded"} { body := uri switch kind { case "text/html": body = `4ready` case "application/json": body = `{"resource":"` + uri + `","task_id":4,"status":"ready"}` case "application/x-www-form-urlencoded": body = "resource=" + url.QueryEscape(uri) + "&task_id=4&status=ready" } ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { got, err := io.ReadAll(r.Body) if err != nil || string(got) != body { t.Error("wire request changed", err) } w.Header().Set("Content-Type", kind) _, _ = io.WriteString(w, body) })) req, err := http.NewRequest(http.MethodPost, ts.URL, strings.NewReader(body)) if err != nil { t.Fatal(err) } req.Header.Set("Content-Type", kind) response, err := HTTPClient(ts.Client(), "test.inline_audio", "upstream").Do(req) if err != nil { ts.Close() t.Fatal(err) } got, err := io.ReadAll(response.Body) _ = response.Body.Close() ts.Close() if err != nil || string(got) != body { t.Fatal("wire response changed", err) } slog.Error("audio diagnostic", "error", errors.New(body), "detail", body) slog.Error(body) } text := out.String() for _, secret := range []string{encoded, escaped, url.QueryEscape(encoded), url.QueryEscape(escaped)} { if strings.Contains(text, secret) { t.Fatalf("audio leaked, debug=%v", debug) } } for _, keep := range []string{"audio/wav", "1644", "task_id", "ready"} { if !strings.Contains(text, keep) { t.Fatal("metadata or diagnostic lost", text) } } } } } func TestURLSchemeCaseCannotExposeSignedPartsOnAnyLoggingPath(t *testing.T) { for _, scheme := range []string{"http", "HTTP", "HtTp", "https", "HTTPS", "hTtPs", "ws", "WS", "wS", "wss", "WSS", "wSs"} { signed := scheme + "://storage.example/audio.wav?sv=2025-01-05&se=2026-10-10&sig=private-signature-value" for _, debug := range []bool{false, true} { out := installTestLogger(t, debug) for _, media := range []string{"text/plain", "application/json"} { body := "upload failed: " + signed if media == "application/json" { body = `{"resource":"` + signed + `","task_id":4,"status":"ready"}` } ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { got, err := io.ReadAll(r.Body) if err != nil || string(got) != body { t.Error("wire request changed", err) } w.Header().Set("Content-Type", media) _, _ = io.WriteString(w, body) })) req, err := http.NewRequest(http.MethodPost, ts.URL, strings.NewReader(body)) if err != nil { t.Fatal(err) } req.Header.Set("Content-Type", media) response, err := HTTPClient(ts.Client(), "test.url", "upstream").Do(req) if err != nil { ts.Close() t.Fatal(err) } got, err := io.ReadAll(response.Body) _ = response.Body.Close() ts.Close() if err != nil || string(got) != body { t.Fatal("wire response changed", err) } slog.Error("upload failed", "error", errors.New(body), "detail", body) slog.Error(body) } text := out.String() if strings.Contains(text, "private-signature-value") || strings.Contains(text, "sv=2025") || strings.Contains(text, "se=2026") { t.Fatalf("scheme %s debug %v leak: %s", scheme, debug, text) } for _, keep := range []string{"upload failed", "audio.wav", "task_id", "ready"} { if !strings.Contains(text, keep) { t.Fatal("safe diagnostic lost", text) } } plain := scheme + "://storage.example/public.wav" if got := cleanString(plain); got != plain { t.Fatal("unsigned URL changed", got) } } } } func TestPercentEncodedSingleAndMixedFormsKeepWireBytesAndHideSecrets(t *testing.T) { for _, body := range []string{ "%74oken=private-access-token", "%6dessages=private-user-turn+private-assistant-turn", "task_id=4&%74oken=private-access-token&status=ready", "task_id=4&%74oken=private-access-token&broken%zz=x&status=ready", "flag&%6dessages=private-user-turn+private-assistant-turn&status=ready", "%74oken=private-access-token&field=x%zz&status=ready", "broken%zz=private-access-token&status=ready", } { for _, debug := range []bool{false, true} { out := installTestLogger(t, debug) ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { got, err := io.ReadAll(r.Body) if err != nil || string(got) != body { t.Error("wire request changed", err) } w.Header().Set("Content-Type", "application/x-www-form-urlencoded") _, _ = io.WriteString(w, body) })) req, err := http.NewRequest(http.MethodPost, ts.URL, strings.NewReader(body)) if err != nil { t.Fatal(err) } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") response, err := HTTPClient(ts.Client(), "test.form", "upstream").Do(req) if err != nil { ts.Close() t.Fatal(err) } got, err := io.ReadAll(response.Body) _ = response.Body.Close() ts.Close() if err != nil || string(got) != body { t.Fatal("wire response changed", err) } slog.Error("form diagnostic", "error", errors.New(body), "detail", body) text := out.String() for _, secret := range []string{"private-access-token", "private-user-turn", "private-assistant-turn"} { if strings.Contains(text, secret) { t.Fatalf("form %q debug %v leak: %s", body, debug, text) } } for _, keep := range []string{"task_id=4", "status=ready"} { if strings.Contains(body, keep) && !strings.Contains(text, keep) { t.Fatal("diagnostic lost", text) } } if strings.Contains(body, "%zz") && !strings.Contains(text, "INVALID_FORM") { t.Fatal("malformed encoding must be explicit", text) } } } } func TestQuotedContentLabelsEstablishOwnershipOnEveryLoggingPath(t *testing.T) { for _, label := range []string{`"messages"`, `'conversation'`, "`history`", `“prompt”`, `‘messages’`, `\"messages\"`, `"m\u0065ssages"`, `"m\qssages"`} { value := "upstream failed: " + label + ":\nrole: user\nmessage: private-user-turn\nrole: assistant\nmessage: private-assistant-turn\nstatus=retry_pending\ntask_id=4\nattempts=2" for _, debug := range []bool{false, true} { out := installTestLogger(t, debug) slog.Error("request failed", "error", errors.New(value), "detail", value) slog.Info(value, "stage", "failure_observed") TextBody(t.Context(), "HTTP text observed", []byte(value), "transport", "http") text := out.String() for _, secret := range []string{"private-user-turn", "private-assistant-turn"} { if strings.Contains(text, secret) { t.Fatalf("label %q debug %v leak: %s", label, debug, text) } } for _, keep := range []string{"upstream failed", "status=retry_pending", "task_id=4", "attempts=2"} { if !strings.Contains(text, keep) { t.Fatal("diagnostic lost", text) } } } } } func TestUnknownDialogueFieldsCannotEndContentOwnership(t *testing.T) { for _, dialogue := range []string{ "role: user\nmessage: private-user-turn\nrole: assistant\nmessage: private-assistant-turn", "human: private-user-turn\nassistant: private-assistant-turn", "participant_42: private-user-turn\nparticipant_99: private-assistant-turn", "arbitrary_attribute: private-user-turn\n arbitrary_nested_attribute: private-assistant-turn", " status: private-user-turn\n message: private-assistant-turn", "role: user\nstatus=ready\nmessage: private-user-turn\nattempts=2\nhuman: private-assistant-turn", "status: private-user-turn\nmessage: private-assistant-turn", } { for _, label := range []string{"messages", "conversation", "history", "prompt"} { value := "upstream failed: " + label + ":\n" + dialogue + "\nstatus=retry_pending\ntask_id=4\nattempts=2" for _, debug := range []bool{false, true} { out := installTestLogger(t, debug) slog.Error("request failed", "error", errors.New(value), "detail", value) slog.Info(value, "stage", "failure_observed") TextBody(t.Context(), "HTTP text observed", []byte(value), "transport", "http") text := out.String() for _, secret := range []string{"private-user-turn", "private-assistant-turn"} { if strings.Contains(text, secret) { t.Fatalf("dialogue %q label %s debug %v leak: %s", dialogue, label, debug, text) } } for _, keep := range []string{"upstream failed", "status=retry_pending", "task_id=4", "attempts=2"} { if !strings.Contains(text, keep) { t.Fatal("business diagnostic lost", text) } } } } } } func TestPrefixedContentLabelsKeepReasonAndOwnFollowingDialogue(t *testing.T) { for _, prefix := range []string{"upstream failed: ", "wrapped error: upstream failed: ", "upstream failed; ", "上游失败:upstream failed: "} { for _, field := range []string{"conversation", "history", "messages", "system_message", "prompt", "final_transcript"} { value := prefix + field + ":\n\nuser: private-user-turn\n\nassistant: private-assistant-turn\nstatus=retry_pending\nattempts=2" for _, debug := range []bool{false, true} { out := installTestLogger(t, debug) slog.Error("request failed", "error", errors.New(value), "detail", value) slog.Info(value, "stage", "failure_observed") TextBody(t.Context(), "HTTP text observed", []byte(value), "transport", "http") text := out.String() for _, secret := range []string{"private-user-turn", "private-assistant-turn"} { if strings.Contains(text, secret) { t.Fatalf("prefix %q field %s debug %v leak: %s", prefix, field, debug, text) } } for _, keep := range []string{"upstream failed", "retry_pending", "attempts=2"} { if !strings.Contains(text, keep) { t.Fatal("business reason lost", text) } } } } } } func TestConversationOwnershipPersistsAcrossBlankLines(t *testing.T) { for _, gap := range []string{"\n", "\n\n", "\n \n\t\n", "\r\n\r\n"} { for _, label := range []string{"messages", "prompt", "transcript", "content"} { value := "upstream failed\n" + label + ":" + gap + "user: private-user-turn" + gap + "assistant: private-assistant-turn" + gap + "status=retry_pending\nattempts=2" for _, debug := range []bool{false, true} { out := installTestLogger(t, debug) slog.Error("operation failed", "error", errors.New(value), "detail", value) slog.Info(value, "stage", "failure_observed") TextBody(t.Context(), "HTTP text observed", []byte(value), "transport", "http") text := out.String() for _, secret := range []string{"private-user-turn", "private-assistant-turn"} { if strings.Contains(text, secret) { t.Fatalf("label %s gap %q debug %v leak: %s", label, gap, debug, text) } } for _, keep := range []string{"upstream failed", "retry_pending", "attempts=2"} { if !strings.Contains(text, keep) { t.Fatal("business diagnostics lost", text) } } } } } } func TestFreeTextConversationIsHiddenInBothModesAndAllScalarPaths(t *testing.T) { value := "upstream failed\nmessages:\nuser: private-user-turn\nassistant: private-assistant-turn\n\nstatus=retry_pending\nattempts=2" for _, debug := range []bool{false, true} { out := installTestLogger(t, debug) slog.Error("operation failed", "error", errors.New(value), "detail", value) slog.Info(value, "stage", "failure_observed") text := out.String() for _, secret := range []string{"private-user-turn", "private-assistant-turn"} { if strings.Contains(text, secret) { t.Fatalf("leak in mode %v: %s", debug, text) } } for _, keep := range []string{"upstream failed", "retry_pending", "attempts=2"} { if !strings.Contains(text, keep) { t.Fatal("failure metadata lost", text) } } } } func TestFreeTextAuthorizationAndUnlabelledPrivatePEMAreHidden(t *testing.T) { for _, debug := range []bool{false, true} { out := installTestLogger(t, debug) cause := "upstream denied\nAuthorization: Basic dXNlcjpwYXNzd29yZA==\nProxy-Authorization: Digest username=private-user, response=private-response\n-----BEGIN RSA PRIVATE KEY-----\nprivate-pem-material\n-----END RSA PRIVATE KEY-----\nretry attempt failed" slog.Error("workflow failed", "error", errors.New(cause)) for _, secret := range []string{"dXNlcjpwYXNzd29yZA==", "private-user", "private-response", "private-pem-material"} { if strings.Contains(out.String(), secret) { t.Fatalf("leak %s: %s", secret, out.String()) } } for _, keep := range []string{"upstream denied", "retry attempt failed"} { if !strings.Contains(out.String(), keep) { t.Fatal("failure reason lost", out.String()) } } } } func TestJSONErrorAuthorizationDoesNotHideBusinessFields(t *testing.T) { out := installTestLogger(t, false) slog.Error("request failed", "error", errors.New(`denied: {"Authorization":"Basic dXNlcjpwYXNzd29yZA==","task_id":"4","reason":"quota full"}`)) text := out.String() if strings.Contains(text, "dXNlcjpwYXNzd29yZA==") || !strings.Contains(text, "quota full") || !strings.Contains(text, "task_id") { t.Fatal(text) } } func TestReadableMarkupFormAndMultilineConversationAreSanitized(t *testing.T) { cases := []struct { body string keep string secrets []string }{ {`readyprivate-markup-keyprivate-markup-prompt27`, "27", []string{"private-markup-key", "private-markup-prompt", "private-input-password"}}, {"status=ready&api_key=private-form-key&remaining=27", "remaining=27", []string{"private-form-key"}}, {"status=ready\nprompt:\n private-multiline-prompt\n still-private\n\nremaining=27", "remaining=27", []string{"private-multiline-prompt", "still-private"}}, } for _, tc := range cases { safe, err := RedactText([]byte(tc.body)) if err != nil { t.Fatal(err) } if !strings.Contains(safe, tc.keep) { t.Fatal("business content lost", safe) } for _, s := range tc.secrets { if strings.Contains(safe, s) { t.Fatalf("leaked %s: %s", s, safe) } } } } func TestReadablePlainHTTPBodiesAreObservedSafelyWithoutChangingBytes(t *testing.T) { body := strings.Repeat("business-data ", 12000) + "\nstatus=ready\ntask_id=4\nremaining=27\nmessages:\n\nuser: private-http-user-turn\n\nassistant: private-http-assistant-turn\n\nstatus=ready\nAuthorization: Basic dXNlcjpwYXNzd29yZA==\napi_key=private-key\n-----BEGIN PRIVATE KEY-----\nprivate-pem-material\n-----END PRIVATE KEY-----\nretry_after=60\n" for _, debug := range []bool{false, true} { out := installTestLogger(t, debug) server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { b, _ := io.ReadAll(r.Body) if string(b) != body { t.Error("request changed") } w.Header().Set("Content-Type", "text/plain; charset=utf-8") io.WriteString(w, body) })) req, _ := http.NewRequest(http.MethodPost, server.URL, strings.NewReader(body)) req.Header.Set("Content-Type", "text/plain") res, err := HTTPClient(server.Client(), "dispatcher", "saas").Do(req) if err != nil { t.Fatal(err) } got, err := io.ReadAll(res.Body) res.Body.Close() server.Close() if err != nil || !bytes.Equal(got, []byte(body)) { t.Fatal("response changed", err) } text := out.String() for _, secret := range []string{"dXNlcjpwYXNzd29yZA==", "private-key", "private-pem-material", "private-http-user-turn", "private-http-assistant-turn"} { if strings.Contains(text, secret) { t.Fatalf("leak %s", secret) } } for _, keep := range []string{"remaining=27", "retry_after=60", "business-data"} { if strings.Contains(text, keep) != debug { t.Fatalf("body absent/truncated or exposed in normal mode: %v %s", debug, keep) } } if debug && !strings.Contains(text, `"body_encoding":"utf-8"`) { t.Fatal("text not identified", text) } } }