diff --git a/coderd/chats_test.go b/coderd/chats_test.go index f056d5fbf4..6a38b592c3 100644 --- a/coderd/chats_test.go +++ b/coderd/chats_test.go @@ -6,6 +6,7 @@ import ( "database/sql" "encoding/json" "fmt" + "mime" "net/http" "net/http/httptest" "regexp" @@ -3986,6 +3987,30 @@ func TestGetChatFile(t *testing.T) { require.NotContains(t, cd, strings.Repeat("a", 256)) }) + t.Run("UnicodeFilename", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitLong) + client := newChatClient(t) + firstUser := coderdtest.CreateFirstUser(t, client) + + // Upload with a non-ASCII filename using RFC 5987 encoding, + // which is what the frontend sends for Unicode filenames. + data := append([]byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}, make([]byte, 64)...) + uploaded, err := client.UploadChatFile(ctx, firstUser.OrganizationID, "image/png", "スクリーンショット.png", bytes.NewReader(data)) + require.NoError(t, err) + + res, err := client.Request(ctx, http.MethodGet, + fmt.Sprintf("/api/experimental/chats/files/%s", uploaded.ID), nil) + require.NoError(t, err) + defer res.Body.Close() + require.Equal(t, http.StatusOK, res.StatusCode) + cd := res.Header.Get("Content-Disposition") + require.Contains(t, cd, "inline") + _, params, err := mime.ParseMediaType(cd) + require.NoError(t, err) + require.Equal(t, "スクリーンショット.png", params["filename"]) + }) + t.Run("NotFound", func(t *testing.T) { t.Parallel() ctx := testutil.Context(t, testutil.WaitLong) diff --git a/site/src/api/api.ts b/site/src/api/api.ts index feba6c2224..65a346d1ad 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -2312,7 +2312,11 @@ class ApiMethods { { headers: { "Content-Type": file.type || "application/octet-stream", - "Content-Disposition": `attachment; filename="${file.name}"`, + // Use RFC 5987 encoding for the filename to support + // non-ASCII characters. Placing the raw name directly in + // the header causes XMLHttpRequest to throw because HTTP + // headers only allow ISO-8859-1 code points. + "Content-Disposition": `attachment; filename="file"; filename*=UTF-8''${encodeURIComponent(file.name)}`, }, }, );