package httpapi import "testing" // 文件名收敛规则:取 Base、只保留 [A-Za-z0-9._-] 与汉字、其余换成下划线, // 结果为空或只剩扩展名时拒绝。它同时守住落盘路径与 URL,不允许有例外。 func TestSafeFontName(t *testing.T) { ok := map[string]string{ "my-font.ttf": "my-font.ttf", "思源宋体.otf": "思源宋体.otf", "Noto Sans SC.woff2": "Noto_Sans_SC.woff2", `C:\Fonts\思源 宋体.TTF`: "思源_宋体.TTF", "../../etc/passwd.ttf": "passwd.ttf", "/abs/path/font.woff": "font.woff", "字体 v2 (正式).woff2": "字体_v2__正式_.woff2", "a/b/c/d/..name...ttf": "..name...ttf", `sub\dir\zh-cn-bold.ttf`: "zh-cn-bold.ttf", } for raw, want := range ok { got, valid := safeFontName(raw) if !valid || got != want { t.Errorf("safeFontName(%q) = (%q, %v),期望 (%q, true)", raw, got, valid, want) } } // 只有扩展名、空名、非白名单扩展名一律拒绝。 for _, raw := range []string{"", " ", ".ttf", ".woff2", "字体.xlsx", "font", `C:\Fonts\`, "..", "../"} { if got, valid := safeFontName(raw); valid { t.Errorf("safeFontName(%q) 应拒绝,得到 %q", raw, got) } } } // 静态访问只认「上传时收敛过的名字」,避免用别名读到别人的文件或目录。 func TestFontContentType(t *testing.T) { want := map[string]string{ "a.ttf": "font/ttf", "a.OTF": "font/otf", "a.woff": "font/woff", "a.woff2": "font/woff2", "思源宋体.ttf": "font/ttf", } for name, ctype := range want { got, ok := fontContentType(name) if !ok || got != ctype { t.Errorf("fontContentType(%q) = (%q, %v),期望 %q", name, got, ok, ctype) } } for _, name := range []string{"", "a.png", "..", "../a.ttf", "sub/a.ttf", `sub\a.ttf`, "a b.ttf", "没有扩展名"} { if _, ok := fontContentType(name); ok { t.Errorf("fontContentType(%q) 应拒绝", name) } } } // 字体 id 的解析与生成必须对称,否则删除接口会找不到文件。 func TestCustomFontName(t *testing.T) { name, ok := customFontName("custom:思源宋体.ttf") if !ok || name != "思源宋体.ttf" { t.Fatalf("customFontName = (%q, %v)", name, ok) } for _, id := range []string{"builtin:noto-sans", "custom:", "custom:../x.ttf", "custom:x.png", "思源宋体.ttf"} { if _, ok := customFontName(id); ok { t.Errorf("customFontName(%q) 应拒绝", id) } } if !isBuiltinFontID("builtin:misans") || isBuiltinFontID("builtin:nope") { t.Error("builtin 字体 id 白名单不正确") } }