package platform import ( "errors" "os" "path/filepath" "strings" ) func ValidateRestoreTarget(target, restoreRoot string, inPlace, confirmed bool) (string, error) { if !filepath.IsAbs(target) { return "", errors.New("validation: restore target must be absolute") } clean := filepath.Clean(target) if clean == "/" || clean == "/boot" || clean == "/etc" || clean == "/usr" || clean == "/var" { return "", errors.New("validation: protected system restore target") } if !inPlace { root := filepath.Clean(restoreRoot) if clean != root && !strings.HasPrefix(clean, root+string(os.PathSeparator)) { return "", errors.New("validation: staging restore must remain below restoreRoot") } if err := rejectSymlinks(root, clean); err != nil { return "", err } } else if !confirmed { return "", errors.New("validation: in-place restore requires explicit confirmation") } if inPlace { anchor := string(os.PathSeparator) + strings.Split(strings.TrimPrefix(clean, string(os.PathSeparator)), string(os.PathSeparator))[0] if err := rejectSymlinks(anchor, clean); err != nil { return "", err } } return clean, nil } func rejectSymlinks(root, target string) error { relative, err := filepath.Rel(root, target) if err != nil { return err } current := root for _, component := range strings.Split(relative, string(os.PathSeparator)) { if component == "." || component == "" { continue } current = filepath.Join(current, component) info, err := os.Lstat(current) if errors.Is(err, os.ErrNotExist) { return nil } if err != nil { return err } if info.Mode()&os.ModeSymlink != 0 { return errors.New("validation: restore target traverses a symlink") } } return nil }