107 lines
4.0 KiB
Go
107 lines
4.0 KiB
Go
package logging
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"io"
|
|
"log/slog"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
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
|
|
}{
|
|
{`<task><status>ready</status><api_key>private-markup-key</api_key><prompt>private-markup-prompt</prompt><input name="password" value="private-input-password"><count>27</count></task>`, "<count>27</count>", []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 := "status=ready\ntask_id=4\nremaining=27\nAuthorization: Basic dXNlcjpwYXNzd29yZA==\napi_key=private-key\n-----BEGIN PRIVATE KEY-----\nprivate-pem-material\n-----END PRIVATE KEY-----\nretry_after=60\n" + strings.Repeat("business-data ", 12000)
|
|
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"} {
|
|
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)
|
|
}
|
|
}
|
|
}
|