/
/
/
1"""Security utilities for input validation."""
2
3from __future__ import annotations
4
5import os
6from pathlib import Path
7
8
9def is_safe_path(path: str, base_path: str | None = None) -> bool:
10 """
11 Check if path is free from path traversal components.
12
13 :param path: The path to validate.
14 :param base_path: If given, additionally require that path (resolved against
15 base_path when relative) stays inside this base directory.
16 """
17 norm_path = os.path.normpath(path)
18 if norm_path.startswith("..") or "/../" in norm_path or "\\..\\" in norm_path:
19 return False
20 if base_path is None:
21 return True
22 # Purely lexical containment check: no filesystem IO, so safe to call on the event loop.
23 norm_base = os.path.normpath(base_path)
24 if not Path(norm_path).is_absolute():
25 norm_path = os.path.normpath(os.path.join(norm_base, norm_path))
26 try:
27 return os.path.commonpath((norm_base, norm_path)) == norm_base
28 except ValueError:
29 # commonpath raises for paths on different (Windows) drives
30 return False
31
32
33def is_safe_name(name: str) -> bool:
34 """Check if name is safe for use (no path separators or traversal components)."""
35 return not ("/" in name or "\\" in name or ".." in name)
36