Files
gemserve/server/server_test.go
2025-02-03 13:12:54 +02:00

226 lines
5.2 KiB
Go

package server
import (
"path/filepath"
"strings"
"testing"
)
func TestCalculateLocalPath(t *testing.T) {
tests := []struct {
name string
input string
basePath string
want string
expectError bool
}{
// Basic path handling
{
name: "Simple valid path",
input: "folder/file.txt",
basePath: "/base",
want: "/base/folder/file.txt",
expectError: false,
},
{
name: "Empty path",
input: "",
basePath: "/base",
want: "/base",
expectError: false,
},
{
name: "Current directory",
input: ".",
basePath: "/base",
want: "/base",
expectError: false,
},
// Leading/trailing slash handling
{
name: "Path with leading slash",
input: "/folder/file.txt",
basePath: "/base",
want: "/base/folder/file.txt",
expectError: false,
},
{
name: "Path with trailing slash",
input: "folder/",
basePath: "/base",
want: "/base/folder",
expectError: false,
},
{
name: "Path with both leading and trailing slashes",
input: "/folder/",
basePath: "/base",
want: "/base/folder",
expectError: false,
},
// Path traversal attempts
{
name: "Simple path traversal attempt",
input: "../file.txt",
basePath: "/base",
want: "",
expectError: true,
},
{
name: "Complex path traversal attempt",
input: "folder/../../../etc/passwd",
basePath: "/base",
want: "",
expectError: true,
},
{
name: "Encoded path traversal attempt",
input: "folder/..%2F..%2F..%2Fetc%2Fpasswd",
basePath: "/base",
want: "/base/folder/..%2F..%2F..%2Fetc%2Fpasswd",
expectError: false,
},
{
name: "Double dot hidden in path",
input: "folder/.../.../etc/passwd",
basePath: "/base",
want: "/base/folder/.../.../etc/passwd",
expectError: false,
},
// Edge cases
{
name: "Multiple sequential slashes",
input: "folder///subfolder////file.txt",
basePath: "/base",
want: "",
expectError: true,
},
{
name: "Unicode characters in path",
input: "фольдер/файл.txt",
basePath: "/base",
want: "/base/фольдер/файл.txt",
expectError: false,
},
{
name: "Path with spaces and special characters",
input: "my folder/my file!@#$%.txt",
basePath: "/base",
want: "/base/my folder/my file!@#$%.txt",
expectError: false,
},
{
name: "Very long path",
input: "a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/file.txt",
basePath: "/base",
want: "/base/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/file.txt",
expectError: false,
},
// Base path variations
{
name: "Empty base path",
input: "file.txt",
basePath: "",
want: "file.txt",
expectError: false,
},
{
name: "Relative base path",
input: "file.txt",
basePath: "base/folder",
want: "base/folder/file.txt",
expectError: false,
},
{
name: "Base path with trailing slash",
input: "file.txt",
basePath: "/base/",
want: "/base/file.txt",
expectError: false,
},
// Symbolic link-like paths (if supported)
{
name: "Path with symbolic link-like components",
input: "folder/symlink/../file.txt",
basePath: "/base",
want: "",
expectError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := calculateLocalPath(tt.input, tt.basePath)
// Check error expectation
if (err != nil) != tt.expectError {
t.Errorf("calculateLocalPath() error = %v, expectError = %v", err, tt.expectError)
return
}
// If we expect an error, don't check the returned path
if tt.expectError {
return
}
// Check if the returned path matches expected
if got != tt.want {
t.Errorf("calculateLocalPath() = %v, want %v", got, tt.want)
}
// Additional security checks for non-error cases
if !tt.expectError {
// Verify the returned path is within base path
if !isWithinBasePath(got, tt.basePath) {
t.Errorf("Result path %v escapes base path %v", got, tt.basePath)
}
// Verify no '..' components in final path
if containsParentRef(got) {
t.Errorf("Result path %v contains parent references", got)
}
}
})
}
}
// Helper function to check if a path is contained within the base path
func isWithinBasePath(path, basePath string) bool {
if basePath == "" {
return true
}
absBase, err := filepath.Abs(basePath)
if err != nil {
return false
}
absPath, err := filepath.Abs(path)
if err != nil {
return false
}
rel, err := filepath.Rel(absBase, absPath)
if err != nil {
return false
}
return !strings.HasPrefix(rel, "..")
}
// Helper function to check if a path contains parent directory references
func containsParentRef(path string) bool {
parts := strings.Split(filepath.Clean(path), string(filepath.Separator))
for _, part := range parts {
if part == ".." {
return true
}
}
return false
}