mirror of
https://github.com/navidrome/navidrome.git
synced 2026-10-18 07:30:29 +00:00
fix(server): handle dot-only names and long fake extensions
A name made only of dots trimmed down to an empty filename. It now falls back to download, like an empty stem does. path.Ext treats anything after the last dot as the extension, so a long suffix with no real extension was kept whole and replaced the stem with download, going past the 255-byte cap. Suffixes longer than 16 bytes are now treated as part of the stem and truncated with it. Neither case is reachable from the current call sites, which always append a short extension.
This commit is contained in:
parent
86890feb77
commit
e4813fe053
@ -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 {
|
||||
|
||||
@ -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"))
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user