mirror of
https://github.com/tw93/Mole.git
synced 2026-03-22 18:30: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:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user