diff --git a/utils/str/content_disposition.go b/utils/str/content_disposition.go index 22a28fdbd..34ae8a5c6 100644 --- a/utils/str/content_disposition.go +++ b/utils/str/content_disposition.go @@ -12,7 +12,11 @@ import ( "github.com/deluan/sanitize" ) -const maxFilenameBytes = 255 +const ( + maxFilenameBytes = 255 + maxExtensionBytes = 16 + fallbackFilename = "download" +) // ContentDispositionAttachment builds an RFC 6266 attachment header value for a user-controlled filename. // Non-ASCII names also get a filename* parameter, which clients prefer over the ASCII fallback. @@ -36,8 +40,11 @@ func splitFilename(filename string) (stem, ext string) { return r }, SanitizeFilename(strings.ToValidUTF8(filename, "_"))) ext = path.Ext(name) + if len(ext) > maxExtensionBytes { + ext = "" + } stem = strings.TrimSuffix(name, ext) - if limit := max(maxFilenameBytes-len(ext), 0); len(stem) > limit { + if limit := maxFilenameBytes - len(ext); len(stem) > limit { for limit > 0 && !utf8.RuneStart(stem[limit]) { limit-- } @@ -47,8 +54,8 @@ func splitFilename(filename string) (stem, ext string) { } func joinFilename(stem, ext string) string { - stem = cmp.Or(strings.TrimSpace(stem), "download") - return strings.TrimRight(stem+ext, " .") + stem = cmp.Or(strings.TrimSpace(stem), fallbackFilename) + return cmp.Or(strings.TrimRight(stem+ext, " ."), fallbackFilename) } func toASCII(s string) string { diff --git a/utils/str/content_disposition_test.go b/utils/str/content_disposition_test.go index 2a768a1ca..944392a4e 100644 --- a/utils/str/content_disposition_test.go +++ b/utils/str/content_disposition_test.go @@ -61,6 +61,16 @@ var _ = Describe("ContentDispositionAttachment", func() { Expect(asciiName("Loose End. ")).To(Equal("Loose End")) }) + It("falls back to a placeholder when only dots are left", func() { + Expect(str.ContentDispositionAttachment("...")).To(Equal(`attachment; filename="download"`)) + }) + + It("caps a long name whose last dot is not an extension", func() { + name := asciiName("a." + strings.Repeat("x", 300)) + Expect(len(name)).To(BeNumerically("<=", 255)) + Expect(name).To(HavePrefix("a.xxx")) + }) + It("replaces path separators and reserved characters", func() { Expect(asciiName("AC/DC: Live, 1979?.zip")).To(Equal("AC_DC_ Live_ 1979_.zip")) })