1
0
mirror of https://github.com/tw93/Mole.git synced 2026-03-22 16:10:08 +00:00

security: add regression tests for validatePath with special chars

- Add TestValidatePath covering Chinese, emoji, and special characters
- Add TestValidatePathWithChineseAndSpecialChars for filesystem tests
- Fix validatePath to detect .. components without rejecting valid paths

Ensures paths with $, ;, :, emoji, Chinese chars are not rejected
while still blocking path traversal attempts.
This commit is contained in:
Tw93
2026-03-14 08:26:45 +08:00
parent 951e395ab7
commit 0d2f217f28
2 changed files with 87 additions and 5 deletions

View File

@@ -153,7 +153,7 @@ func moveToTrash(path string) error {
}
// validatePath checks path safety for external commands.
// Returns error if path is empty, relative, contains null bytes, or escapes root.
// Returns error if path is empty, relative, contains null bytes, or has traversal.
func validatePath(path string) error {
if path == "" {
return fmt.Errorf("path is empty")
@@ -164,10 +164,11 @@ func validatePath(path string) error {
if strings.Contains(path, "\x00") {
return fmt.Errorf("path contains null bytes")
}
// Ensure Clean doesn't radically alter the path (path traversal check).
clean := filepath.Clean(path)
if !strings.HasPrefix(clean, "/") {
return fmt.Errorf("path escapes root: %s", path)
// Check for path traversal attempts (.. components).
for _, component := range strings.Split(path, string(filepath.Separator)) {
if component == ".." {
return fmt.Errorf("path contains traversal components: %s", path)
}
}
return nil
}

View File

@@ -81,3 +81,84 @@ func TestMoveToTrashNonExistent(t *testing.T) {
t.Fatal("expected error for non-existent path")
}
}
func TestValidatePath(t *testing.T) {
tests := []struct {
name string
path string
wantErr bool
}{
// 基本合法路径
{"absolute path", "/Users/test/file.txt", false},
{"path with spaces", "/Users/test/My Documents/file.txt", false},
{"root", "/", false},
// 中文路径
{"chinese path", "/Users/test/中文文件夹/文件.txt", false},
{"chinese mixed", "/Users/test/Downloads/报告2024.pdf", false},
// Emoji 路径
{"emoji path", "/Users/test/📁文件夹/📝笔记.txt", false},
{"emoji only", "/Users/test/🎉/🎊.txt", false},
// 特殊字符路径 (之前被错误拒绝的)
{"dollar sign", "/Users/test/$HOME/workspace", false},
{"semicolon", "/Users/test/project;v2", false},
{"colon", "/Users/test/project:2024", false},
{"ampersand", "/Users/test/R&D/project", false},
{"at sign", "/Users/test/user@domain", false},
{"hash", "/Users/test/project#123", false},
{"percent", "/Users/test/100% complete", false},
{"exclamation", "/Users/test/important!.txt", false},
{"single quote", "/Users/test/user's files", false},
{"equals", "/Users/test/key=value", false},
{"plus", "/Users/test/file+v2", false},
{"brackets", "/Users/test/[2024] report", false},
{"parentheses", "/Users/test/project (copy)", false},
{"comma", "/Users/test/file, backup", false},
// 非法路径
{"empty", "", true},
{"relative", "relative/path", true},
{"relative dot", "./file.txt", true},
{"null byte", "/Users/test\x00/file", true},
{"path traversal", "/Users/test/../../../etc", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validatePath(tt.path)
if (err != nil) != tt.wantErr {
t.Errorf("validatePath(%q) error = %v, wantErr %v", tt.path, err, tt.wantErr)
}
})
}
}
func TestValidatePathWithChineseAndSpecialChars(t *testing.T) {
// 专门测试之前会导致兼容性回退的路径
parent := t.TempDir()
testCases := []struct {
name string
path string
}{
{"chinese", "中文文件夹"},
{"emoji", "📁 文档"},
{"mixed", "报告-2024_v2 (终稿) [已审核]"},
{"special", "Project$2024; Q1: R&D"},
{"complex", "用户@公司 100% 完成!"},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
fullPath := filepath.Join(parent, tc.path)
if err := os.MkdirAll(fullPath, 0o755); err != nil {
t.Fatalf("mkdir %q: %v", tc.path, err)
}
if err := validatePath(fullPath); err != nil {
t.Errorf("validatePath rejected valid path %q: %v", tc.path, err)
}
})
}
}