diff --git a/internal/web/uploads_logging_handler_test.go b/internal/web/uploads_logging_handler_test.go new file mode 100644 index 0000000..e29a33e --- /dev/null +++ b/internal/web/uploads_logging_handler_test.go @@ -0,0 +1,72 @@ +package web_test + +import ( + "bytes" + "context" + "errors" + "io" + "log/slog" + "net/http" + "net/url" + "sync" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// Error paths of the upload routes log a kind, never the error's text, which can carry +// a file name, a path, a database value or a provider's output (adversarial review F5). + +type lockedBuf struct { + mu sync.Mutex + b bytes.Buffer +} + +func (l *lockedBuf) Write(p []byte) (int, error) { + l.mu.Lock() + defer l.mu.Unlock() + return l.b.Write(p) +} + +func (l *lockedBuf) String() string { + l.mu.Lock() + defer l.mu.Unlock() + return l.b.String() +} + +type failingUploader struct{ err error } + +func (f failingUploader) Accept(context.Context, string, string, io.Reader) (string, error) { + return "", f.err +} + +func TestUploadPost_AnUnexpectedFailureLogsAKindAndNotItsText(t *testing.T) { + app, _ := newUploadApp(t, audioOK, 200<<20) + buf := &lockedBuf{} + app.Log = slog.New(slog.NewTextHandler(buf, nil)) + app.Uploads = failingUploader{err: errors.New(`ffprobe: Error opening input file /spool/upload-555 for "zebrakaka-meeting.m4a"`)} + + rec := do(t, app, uploadReq(t, "t", "zebrakaka-meeting.m4a", []byte("x"))) + + assert.Equal(t, http.StatusInternalServerError, rec.Code) + out := buf.String() + assert.NotContains(t, out, "zebrakaka", "a file name is private content") + assert.NotContains(t, out, "/spool/", "a spool path carries the upload's identity") + assert.Contains(t, out, "kind=error") +} + +func TestUploadChatPost_AModelFailureLogsAKindAndNotItsText(t *testing.T) { + app, fc := newUploadChatApp(t) + buf := &lockedBuf{} + app.Log = slog.New(slog.NewTextHandler(buf, nil)) + fc.err = errors.New("LLM returned 400: the prompt was zebrakaka secret words") + id := seedUpload(t, userID, "m", "done", meetingText) + + rec := do(t, app, chatPost("/uploads/"+id+"/chat", url.Values{"question": {"q"}})) + + require.Equal(t, http.StatusOK, rec.Code) + out := buf.String() + assert.NotContains(t, out, "zebrakaka") + assert.Contains(t, out, "kind=error") +}