mirror of
https://github.com/Noooste/garage-ui.git
synced 2026-08-08 12:53:13 +00:00
dd275d2e78
Signed-off-by: Noooste <83548733+Noooste@users.noreply.github.com>
104 lines
2.9 KiB
Go
104 lines
2.9 KiB
Go
package handlers
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestSafeContentType_RewritesExecutableTypes(t *testing.T) {
|
|
cases := []struct {
|
|
in, want string
|
|
}{
|
|
{"text/html", "application/octet-stream"},
|
|
{"text/html; charset=utf-8", "application/octet-stream"},
|
|
{"TEXT/HTML", "application/octet-stream"},
|
|
{" text/html ", "application/octet-stream"},
|
|
{"application/xhtml+xml", "application/octet-stream"},
|
|
{"image/svg+xml", "application/octet-stream"},
|
|
{"application/xml", "application/octet-stream"},
|
|
{"text/xml", "application/octet-stream"},
|
|
{"application/javascript", "application/octet-stream"},
|
|
{"text/javascript", "application/octet-stream"},
|
|
// Safe types pass through unchanged (including parameters).
|
|
{"image/png", "image/png"},
|
|
{"application/octet-stream", "application/octet-stream"},
|
|
{"text/plain; charset=utf-8", "text/plain; charset=utf-8"},
|
|
{"", ""},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.in, func(t *testing.T) {
|
|
if got := safeContentType(tc.in); got != tc.want {
|
|
t.Errorf("safeContentType(%q) = %q, want %q", tc.in, got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestContentDispositionHeader_StripsPathAndEscapes(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
disp, key string
|
|
mustContain []string
|
|
mustNotContain []string
|
|
}{
|
|
{
|
|
name: "simple filename",
|
|
disp: "inline", key: "photo.png",
|
|
mustContain: []string{
|
|
`inline; filename="photo.png"; filename*=UTF-8''photo.png`,
|
|
},
|
|
},
|
|
{
|
|
name: "path components stripped",
|
|
disp: "attachment", key: "a/b/c/file.txt",
|
|
mustContain: []string{`filename="file.txt"`, `filename*=UTF-8''file.txt`},
|
|
mustNotContain: []string{`a/b/c`},
|
|
},
|
|
{
|
|
name: "quote and backslash replaced in ASCII fallback",
|
|
disp: "inline", key: `"evil\name".txt`,
|
|
mustContain: []string{`filename="_evil_name_.txt"`},
|
|
mustNotContain: []string{`"evil`, `\name`},
|
|
},
|
|
{
|
|
name: "control character replaced",
|
|
disp: "inline", key: "line\nbreak.txt",
|
|
mustContain: []string{`filename="line_break.txt"`},
|
|
},
|
|
{
|
|
name: "non-ASCII preserved in filename* only",
|
|
disp: "inline", key: "漢字.txt",
|
|
mustContain: []string{
|
|
`filename*=UTF-8''`,
|
|
// fallback replaced every non-ASCII rune with _ (2 runes + .txt).
|
|
`filename="__.txt"`,
|
|
},
|
|
},
|
|
{
|
|
name: "empty key falls back to download",
|
|
disp: "inline", key: "",
|
|
mustContain: []string{`filename="download"`, `filename*=UTF-8''download`},
|
|
},
|
|
{
|
|
name: "key of only slashes falls back to download",
|
|
disp: "inline", key: "/",
|
|
mustContain: []string{`filename="download"`, `filename*=UTF-8''`},
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got := contentDispositionHeader(tc.disp, tc.key)
|
|
for _, sub := range tc.mustContain {
|
|
if !strings.Contains(got, sub) {
|
|
t.Errorf("got %q\nmust contain %q", got, sub)
|
|
}
|
|
}
|
|
for _, sub := range tc.mustNotContain {
|
|
if strings.Contains(got, sub) {
|
|
t.Errorf("got %q\nmust NOT contain %q", got, sub)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|