36 lines
903 B
Python
36 lines
903 B
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
class PathSecurityError(ValueError):
|
|
pass
|
|
|
|
|
|
def ensure_inside(path: Path, root: Path) -> Path:
|
|
resolved = path.expanduser().resolve()
|
|
root_resolved = root.expanduser().resolve()
|
|
try:
|
|
resolved.relative_to(root_resolved)
|
|
except ValueError as exc:
|
|
raise PathSecurityError(f"path escapes root: {resolved}") from exc
|
|
return resolved
|
|
|
|
|
|
def resolve_user_path(value: str | None, default: Path, root: Path) -> Path:
|
|
if value:
|
|
candidate = Path(value).expanduser()
|
|
if not candidate.is_absolute():
|
|
candidate = root / candidate
|
|
else:
|
|
candidate = default
|
|
return ensure_inside(candidate, root)
|
|
|
|
|
|
def rel(path: Path, root: Path) -> str:
|
|
try:
|
|
return str(path.resolve().relative_to(root.resolve()))
|
|
except ValueError:
|
|
return str(path.resolve())
|
|
|