package fsx import ( "errors" "path/filepath" "strings" ) func CleanURLLike(p string) string { p = strings.TrimSpace(p) if p == "" || p == "/" { return "/" } parts := []string{} for _, seg := range strings.Split(strings.ReplaceAll(p, "\\", "/"), "/") { switch seg { case "", ".": continue case "..": if len(parts) > 0 { parts = parts[:len(parts)-1] } default: parts = append(parts, seg) } } return "/" + strings.Join(parts, "/") } func SafeRel(root, requested string) (string, error) { s := CleanURLLike(requested) if strings.HasPrefix(s, "/") { s = strings.TrimPrefix(s, "/") } full := filepath.Join(root, filepath.FromSlash(s)) rel, err := filepath.Rel(root, full) if err != nil { return "", err } if rel == "." { return "/", nil } sep := string(filepath.Separator) if strings.HasPrefix(rel, "..") || strings.Contains(rel, ".."+sep) { return "", errors.New("path escapes root") } return "/" + filepath.ToSlash(rel), nil } func ResponsePath(root, full string) string { rel, err := filepath.Rel(root, full) if err != nil || rel == "." { return "/" } return "/" + filepath.ToSlash(rel) }