fix: limit filename length (#825)

Drew Smirnoff and Daniel Purnomo created

Co-authored-by: Daniel Purnomo <danielpurnomo100@gmail.com>

Change summary

main.go      | 11 +++++++++++
main_test.go |  5 +++++
2 files changed, 16 insertions(+)

Detailed changes

main.go 🔗

@@ -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}
 		}

main_test.go 🔗

@@ -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 {