package local import ( "fmt" "os" "path/filepath" "strings" ) func (c *Client) resolveObjectPath(objectKey string) (string, error) { if strings.TrimSpace(objectKey) == "" || filepath.IsAbs(objectKey) || strings.Contains(objectKey, "\\") { return "", fmt.Errorf("对象键不是安全的相对路径") } for _, segment := range strings.Split(objectKey, "/") { if segment == "" || segment == "." || segment == ".." { return "", fmt.Errorf("对象键包含无效路径段") } } target := filepath.Join(c.objectsPath, filepath.FromSlash(objectKey)) relative, err := filepath.Rel(c.objectsPath, target) if err != nil || relative == ".." || strings.HasPrefix(relative, ".."+string(filepath.Separator)) { return "", fmt.Errorf("对象路径超出存储目录") } if err := c.ensureNoSymlink(filepath.Dir(target), c.objectsPath); err != nil { return "", err } return target, nil } func (c *Client) ensureDirectory(directory string) error { if err := os.MkdirAll(directory, 0o750); err != nil { return fmt.Errorf("创建本地存储目录 %s: %w", directory, err) } if err := c.ensureNoSymlink(directory, c.rootPath); err != nil { return err } return os.Chmod(directory, 0o750) } func (c *Client) ensureNoSymlink(target, boundary string) error { target = filepath.Clean(target) boundary = filepath.Clean(boundary) relative, err := filepath.Rel(boundary, target) if err != nil || relative == ".." || strings.HasPrefix(relative, ".."+string(filepath.Separator)) { return fmt.Errorf("路径超出本地存储边界") } current := boundary if relative == "." { return rejectSymlink(current) } if err := rejectSymlink(current); err != nil { return err } for _, segment := range strings.Split(relative, string(filepath.Separator)) { current = filepath.Join(current, segment) info, statErr := os.Lstat(current) if os.IsNotExist(statErr) { continue } if statErr != nil { return fmt.Errorf("检查路径 %s: %w", current, statErr) } if info.Mode()&os.ModeSymlink != 0 { return fmt.Errorf("本地存储路径不允许符号链接: %s", current) } } return nil } func rejectSymlink(path string) error { info, err := os.Lstat(path) if err != nil { return err } if info.Mode()&os.ModeSymlink != 0 { return fmt.Errorf("本地存储路径不允许符号链接: %s", path) } return nil }