@@ -2584,6 +2584,16 @@ func sanitizeFilename(name string) string {
if name == "" || name == "." || strings.HasPrefix(name, ".") {
name = "attachment"
}
+ // Sanitize filename: enforce length limit to prevent filesystem errors
+ // with extremely long names from untrusted email headers.
+ const maxFilenameLen = 255
+ if len(name) > maxFilenameLen {
+ ext := filepath.Ext(name)
+ if len(ext) > maxFilenameLen {
+ ext = ext[:maxFilenameLen]
+ }
+ name = name[:maxFilenameLen-len(ext)] + ext
+ }
return name
}
@@ -2602,6 +2612,7 @@ func downloadAttachmentCmd(account *config.Account, uid uint32, msg tui.Download
default:
data, err = fetcher.FetchAttachment(account, uid, msg.PartID, msg.Encoding)
}
+
if err != nil {
return tui.AttachmentDownloadedMsg{Err: err}
}
@@ -1,6 +1,7 @@
package main
import (
+ "strings"
"testing"
)
@@ -26,6 +27,10 @@ func TestSanitizeFilename(t *testing.T) {
{"null bytes removed", "file\x00name.txt", "file\x00name.txt"},
{"unicode filename", "日本語.txt", "日本語.txt"},
{"long traversal chain", "a/b/c/../../../d/e/f.txt", "f.txt"},
+ {"exact 255 chars", strings.Repeat("a", 255), strings.Repeat("a", 255)},
+ {"256 chars", strings.Repeat("a", 256), strings.Repeat("a", 255)},
+ {"long with extension", strings.Repeat("b", 260) + ".txt", strings.Repeat("b", 251) + ".txt"},
+ {"long extension only", "a." + strings.Repeat("c", 260), "." + strings.Repeat("c", 254)},
}
for _, tt := range tests {