4"""Hostile HOME Git-policy regressions for the immutable pre-commit owner."""
6from __future__
import annotations
9from collections.abc
import Callable
10from pathlib
import Path
13from scripts.dev.git_environment
import trusted_git_executable
16class HostileGitPolicyError(RuntimeError):
17 """The hook executed inherited Git policy or lost its refusal path."""
20def _fail(message: str) ->
None:
21 raise HostileGitPolicyError(message)
24def _write(path: Path, text: str, *, executable: bool =
False) ->
None:
25 path.parent.mkdir(parents=
True, exist_ok=
True)
26 path.write_text(text, encoding=
"utf-8")
31def _hostile_home_policy(
32 base: Path, transport_environment: Callable[[Path, Path], dict[str, str]]
33) -> tuple[dict[str, str], tuple[Path, ...]]:
34 """Create ordinary HOME policy covering filters, templates, and fsmonitor."""
35 home = base /
"hostile-home"
36 template = home /
"template/hooks"
37 template.mkdir(parents=
True)
38 markers = tuple(base / name
for name
in (
"filter.ran",
"template.ran",
"fsmonitor.ran"))
39 filter_helper = home /
"filter.sh"
40 fsmonitor_helper = home /
"fsmonitor.sh"
41 _write(filter_helper, f
"#!/bin/sh\nprintf x >>{markers[0]}\ncat\n", executable=
True)
43 template /
"reference-transaction",
44 f
"#!/bin/sh\nprintf x >>{markers[1]}\ncat >/dev/null\n",
49 f
"#!/bin/sh\nprintf x >>{markers[2]}\nprintf '\n'\n",
52 attributes = home /
"attributes"
53 _write(attributes,
"* filter=evil\n")
57 f
"\tattributesFile = {attributes}\n\tfsmonitor = {fsmonitor_helper}\n"
58 f
"[init]\n\ttemplateDir = {template.parent}\n"
59 f
'[filter "evil"]\n\tclean = {filter_helper}\n'
60 f
"\tsmudge = {filter_helper}\n\trequired = true\n",
62 environment = transport_environment(base, base)
63 for name
in tuple(environment):
64 if name.startswith(
"GIT_CONFIG_")
or name ==
"GIT_ATTR_NOSYSTEM":
66 environment.update(HOME=str(home), XDG_CONFIG_HOME=str(home /
"xdg"))
67 return environment, markers
70def _prove_hostile_home_is_live(
71 base: Path, environment: dict[str, str], markers: tuple[Path, ...]
73 """Prove every planted ordinary HOME helper executes without the repair."""
74 root = base /
"unprotected-home-probe"
76 _write(root /
"probe.txt",
"probe\n")
80 (
"status",
"--porcelain"),
83 "user.email=selftest@invalid",
93 proc = subprocess.run(
94 [trusted_git_executable(),
"-C", str(root), *args],
99 if proc.returncode != 0:
100 _fail(f
"hostile HOME probe did not execute {args[-1]}: {proc.stderr!r}")
101 if any(
not marker.exists()
for marker
in markers):
102 _fail(
"hostile HOME probe did not activate filter, template, and fsmonitor")
103 for marker
in markers:
107def _install_core_wrappers(directory: Path) ->
None:
108 """Install source/PATH core-utility attacks used by the real owner test."""
109 for name
in (
"cp",
"mkdir",
"mktemp",
"ln",
"readlink",
"rm"):
113 'printf "ran\\n" >"${RA8_PATH_MARKER_DIR:?}/${RA8_PATH_MARKER_PREFIX:?}.${0##*/}"\n'
119def _prove_core_wrappers_live(directory: Path, marker_dir: Path, prefix: str) ->
None:
120 """Prove the hostile PATH would select every planted wrapper."""
122 for name
in (
"cp",
"mkdir",
"mktemp",
"ln",
"readlink",
"rm"):
124 "PATH": f
"{directory}:/usr/bin:/bin",
125 "RA8_PATH_MARKER_DIR": str(marker_dir),
126 "RA8_PATH_MARKER_PREFIX": prefix,
128 result = subprocess.run(
129 [
"/bin/bash",
"-p",
"-c", name],
134 marker = marker_dir / f
"{prefix}.{name}"
135 if result.returncode != expected_return
or not marker.is_file():
136 _fail(f
"hostile core-utility control did not execute {prefix}/{name}")
140def _hostile_owner_path_case(
142 callbacks: tuple[Callable[..., Any], ...],
144 """Prove source-local and arbitrary PATH core utilities cannot become owner tools."""
145 make_fixture, _git, source_state, run_owner, transport_environment = callbacks
146 root, temp_root = base /
"hostile-owner-path", base /
"hostile-owner-path-tmp"
147 arbitrary = base /
"arbitrary-path"
148 marker_dir = base /
"path-markers"
154 source_bin = root /
".venv/bin"
155 source_bin.mkdir(parents=
True, exist_ok=
True)
156 _install_core_wrappers(source_bin)
157 _install_core_wrappers(arbitrary)
158 _prove_core_wrappers_live(source_bin, marker_dir,
"source")
159 _prove_core_wrappers_live(arbitrary, marker_dir,
"arbitrary")
160 before = source_state(root)
161 environment = transport_environment(base, root)
163 PATH=f
"{source_bin}:{arbitrary}:{environment.get('PATH', '')}",
164 RA8_PATH_MARKER_DIR=str(marker_dir),
165 RA8_PATH_MARKER_PREFIX=
"attack",
167 result = run_owner(root, temp_root, environment)
168 if result.returncode:
169 _fail(f
"owner core-utility isolation case failed: {result.stderr}")
170 if tuple(marker_dir.iterdir()):
171 _fail(
"immutable owner executed a source-local or arbitrary PATH core utility")
172 if source_state(root) != before
or tuple(temp_root.iterdir()):
173 _fail(
"owner core-utility isolation changed source state or left residue")
176def run_hostile_owner_cases(
178 callbacks: tuple[Callable[..., Any], ...],
180 """Prove the real owner ignores HOME policy and rejects staged drivers."""
181 make_fixture, git, source_state, run_owner, transport_environment = callbacks
182 _hostile_owner_path_case(base, callbacks)
183 environment, markers = _hostile_home_policy(base, transport_environment)
184 _prove_hostile_home_is_live(base, environment, markers)
185 for name, hostile_attributes, expected
in (
186 (
"hostile-home-success",
False, 0),
187 (
"hostile-candidate-attribute",
True, 1),
189 root, temp_root = base / name, base / f
"{name}-tmp"
193 (root /
".venv/bin").mkdir(parents=
True)
194 case_environment = {**environment,
"RA8_SELFTEST_VENV_BIN": str(root /
".venv/bin")}
195 if hostile_attributes:
196 _write(root /
".gitattributes",
"* filter=evil\n")
197 git(root,
"add",
".gitattributes")
198 before = source_state(root)
199 result = run_owner(root, temp_root, case_environment)
200 if result.returncode != expected:
201 detail = result.stderr
or result.stdout
202 _fail(f
"{name}: expected {expected}, got {result.returncode}: {detail}")
203 executed = tuple(marker.name
for marker
in markers
if marker.exists())
205 _fail(f
"{name}: owner executed ordinary HOME Git policy: {executed}")
206 if source_state(root) != before
or tuple(temp_root.iterdir()):
207 _fail(f
"{name}: owner changed source state or left residue")