diff --git a/internal/web/uploads_badinput_handler_test.go b/internal/web/uploads_badinput_handler_test.go index cb338f7..6cb594f 100644 --- a/internal/web/uploads_badinput_handler_test.go +++ b/internal/web/uploads_badinput_handler_test.go @@ -1,7 +1,9 @@ package web_test import ( + "bytes" "context" + "io" "log/slog" "net/http" "testing" @@ -60,3 +62,47 @@ func TestUploadPost_AMalformedMultipartHeaderIsA400AndItsTextIsNotLogged(t *test assert.Empty(t, sub.jobs) assert.NotContains(t, buf.String(), "zebrakaka", "a file name is private content") } + +func TestUploadPost_ABodyCutOffInsideTheFilePartIsA400NotA500(t *testing.T) { + app, sub := newUploadApp(t, audioOK, 200<<20) + req := uploadReq(t, "t", "a.m4a", bytes.Repeat([]byte("a"), 4096)) + raw, err := io.ReadAll(req.Body) + require.NoError(t, err) + req.Body = io.NopCloser(bytes.NewReader(raw[:len(raw)-2000])) // the sender hung up mid-file + buf := &lockedBuf{} + app.Log = slog.New(slog.NewTextHandler(buf, nil)) + + rec := do(t, app, req) + + assert.Equal(t, http.StatusBadRequest, rec.Code, "a connection that drops mid-upload is the sender's to retry, not a server fault") + assert.Empty(t, sub.jobs) + assert.NotContains(t, buf.String(), "level=ERROR") +} + +func TestUploadPost_BidirectionalControlCharactersAreStrippedFromATitle(t *testing.T) { + // U+202E reverses the text that follows it, so a title could render reversed on its + // owner's own page (second adversarial review). + app, sub := newUploadApp(t, audioOK, 200<<20) + + rec := do(t, app, uploadReq(t, "Sprint ‮review ⁦x⁩ ‏ok", "a.m4a", []byte("x"))) + + require.Equal(t, http.StatusSeeOther, rec.Code) + var title string + require.NoError(t, rawPool(t).QueryRow(context.Background(), `SELECT title FROM uploaded_transcripts WHERE id = $1`, sub.jobs[0].UploadID).Scan(&title)) + for _, r := range []string{"‮", "⁦", "⁩", "‏"} { + assert.NotContains(t, title, r) + } + assert.Contains(t, title, "Sprint") + assert.Contains(t, title, "review") +} + +func TestUploadPost_SwedishLettersAndEmojiSurviveTheTitleCleaning(t *testing.T) { + app, sub := newUploadApp(t, audioOK, 200<<20) + + rec := do(t, app, uploadReq(t, "Möte åäö 🎙️", "a.m4a", []byte("x"))) + + require.Equal(t, http.StatusSeeOther, rec.Code) + var title string + require.NoError(t, rawPool(t).QueryRow(context.Background(), `SELECT title FROM uploaded_transcripts WHERE id = $1`, sub.jobs[0].UploadID).Scan(&title)) + assert.Equal(t, "Möte åäö 🎙️", title) +}