1package main
2
3import (
4 "strings"
5 "testing"
6)
7
8func TestSanitizeFilename(t *testing.T) {
9 tests := []struct {
10 name string
11 input string
12 expected string
13 }{
14 {"normal filename", "document.pdf", "document.pdf"},
15 {"filename with spaces", "my report.docx", "my report.docx"},
16 {"unix path traversal", "../../../etc/passwd", "passwd"},
17 {"windows path traversal", "..\\..\\..\\windows\\system32\\config", "config"},
18 {"mixed traversal", "../secret/key.pem", "key.pem"},
19 {"hidden file", ".bashrc", "attachment"},
20 {"dot only", ".", "attachment"},
21 {"dot dot", "..", "_"},
22 {"empty string", "", "attachment"},
23 {"absolute unix path", "/etc/shadow", "shadow"},
24 {"absolute windows path", "C:\\Users\\secret.txt", "secret.txt"},
25 {"double dot in middle", "file..name.txt", "file_name.txt"},
26 {"multiple slashes", "path/to/file.txt", "file.txt"},
27 {"null bytes removed", "file\x00name.txt", "file\x00name.txt"},
28 {"unicode filename", "日本語.txt", "日本語.txt"},
29 {"long traversal chain", "a/b/c/../../../d/e/f.txt", "f.txt"},
30 {"exact 255 chars", strings.Repeat("a", 255), strings.Repeat("a", 255)},
31 {"256 chars", strings.Repeat("a", 256), strings.Repeat("a", 255)},
32 {"long with extension", strings.Repeat("b", 260) + ".txt", strings.Repeat("b", 251) + ".txt"},
33 {"long extension only", "a." + strings.Repeat("c", 260), "." + strings.Repeat("c", 254)},
34 }
35
36 for _, tt := range tests {
37 t.Run(tt.name, func(t *testing.T) {
38 got := sanitizeFilename(tt.input)
39 if got != tt.expected {
40 t.Errorf("sanitizeFilename(%q) = %q, want %q", tt.input, got, tt.expected)
41 }
42 // Verify the sanitized name never allows escaping the download directory
43 if got == "" {
44 t.Error("sanitizeFilename returned empty string")
45 }
46 if got == "." || got == ".." {
47 t.Error("sanitizeFilename returned a dangerous name: " + got)
48 }
49 })
50 }
51}
52
53func TestSanitizeFilenameNoPathSeparators(t *testing.T) {
54 // Ensure no sanitized output contains path separators
55 dangerous := []string{
56 "a/b", "a\\b", "../a", "..\\a",
57 "/etc/passwd", "\\Windows\\System32",
58 "....//....//etc/passwd",
59 }
60 for _, input := range dangerous {
61 got := sanitizeFilename(input)
62 for _, ch := range got {
63 if ch == '/' || ch == '\\' {
64 t.Errorf("sanitizeFilename(%q) = %q contains path separator", input, got)
65 }
66 }
67 }
68}