paths.py python
75 lines 2.2 KB
Raw
sha256:0e9549ec7b463911bc08b7d586dc320b1ac9b1f5c943ee7e3865dcc6cb0f6f83 chore(governance): sync handover+roadmap to 84db8c8 (drift:… Human 8 hours ago
1 """Repo-root resolution and path confinement (§K4.1 / §K4.9)."""
2
3 from __future__ import annotations
4
5 import os
6 from pathlib import Path
7
8
9 class PathEscapeError(Exception):
10 """Raised when a path would escape the resolved repo root."""
11
12
13 def resolve_repo_root(
14 *,
15 cwd: Path,
16 repo_arg: str | None,
17 command: str,
18 ) -> Path:
19 """Resolve and absolutize the consumer repo root per §K4.1."""
20 if repo_arg is not None:
21 return Path(repo_arg).expanduser().resolve()
22
23 if command == "init":
24 return cwd.resolve()
25
26 current = cwd.resolve()
27 for directory in (current, *current.parents):
28 if (directory / ".overseer").is_dir():
29 return directory
30 return current
31
32
33 def resolve_config_path(repo_root: Path, config_arg: str | None) -> Path:
34 """Resolve the config file path; default ``<repo>/.overseer/config.yaml``."""
35 if config_arg is not None:
36 path = Path(config_arg).expanduser()
37 if not path.is_absolute():
38 path = repo_root / path
39 return path.resolve()
40 return (repo_root / ".overseer" / "config.yaml").resolve()
41
42
43 def repo_relative(repo_root: Path, path: Path) -> str:
44 """Return a POSIX repo-relative path, or ``.`` for the root."""
45 resolved = path.resolve()
46 try:
47 rel = resolved.relative_to(repo_root.resolve())
48 except ValueError:
49 raise PathEscapeError(f"path escapes repo root: {path}") from None
50 text = rel.as_posix()
51 return text if text else "."
52
53
54 def confine_path(repo_root: Path, user_path: str) -> Path:
55 """Resolve ``user_path`` under ``repo_root``; reject traversal escapes."""
56 root = repo_root.resolve()
57 candidate = Path(user_path).expanduser()
58 if candidate.is_absolute():
59 resolved = candidate.resolve()
60 else:
61 resolved = (root / candidate).resolve()
62 try:
63 resolved.relative_to(root)
64 except ValueError:
65 raise PathEscapeError(f"path escapes repo root: {user_path}") from None
66 return resolved
67
68
69 def is_within_repo(repo_root: Path, path: Path) -> bool:
70 """Return True if ``path`` is inside ``repo_root``."""
71 try:
72 path.resolve().relative_to(repo_root.resolve())
73 return True
74 except ValueError:
75 return False
File History 1 commit
sha256:6abcf1fa82a7a621ccbc945f19acdba5bc0db54569599404a1452fb4a096a199 fix(ISR): default require_independent_second_reviewer to require Human minor 7 hours ago