4"""Keep nested Git fixtures independent from the invoking hook repository.
6Git exports repository-local environment variables while running hooks. A
7``git -C <temporary-directory>`` command does not override those variables, so
8an otherwise isolated selftest can read, stage, or commit the caller's index.
9Every child Git writer and fixture must enter :func:`isolated_git_environment`
10or pass :func:`sanitized_git_environment` before its first Git command.
11Read-only real-tree queries that deliberately judge the caller's index may
12inherit that routing; writers never may.
15from __future__
import annotations
25from collections.abc
import Iterator, Mapping, Sequence
26from contextlib
import contextmanager
27from pathlib
import Path
29TRUSTED_GIT_PATH = Path(
"/usr/bin/git")
30PUSH_CAPTURE_FIELDS = 4
32LOCAL_GIT_ENVIRONMENT = (
33 "GIT_ALTERNATE_OBJECT_DIRECTORIES",
37 "GIT_CONFIG_PARAMETERS",
40 "GIT_IMPLICIT_WORK_TREE",
42 "GIT_INTERNAL_SUPER_PREFIX",
43 "GIT_NO_REPLACE_OBJECTS",
44 "GIT_OBJECT_DIRECTORY",
46 "GIT_REPLACE_REF_BASE",
52class GitEnvironmentError(RuntimeError):
53 """A nested Git fixture escaped its repository boundary."""
56def _fail(message: str) ->
None:
57 """Raise a fixture-boundary error with caller-provided detail."""
58 raise GitEnvironmentError(message)
61def trusted_git_executable() -> str:
62 """Return the one absolute Git executable allowed for control-plane work."""
63 configured = os.environ.get(
"RA8_TRUSTED_GIT", str(TRUSTED_GIT_PATH))
64 if configured != str(TRUSTED_GIT_PATH):
65 _fail(f
"refusing non-authority Git executable: {configured}")
67 info = TRUSTED_GIT_PATH.lstat()
68 except OSError
as exc:
69 message =
"trusted /usr/bin/git is unavailable"
70 raise GitEnvironmentError(message)
from exc
71 if not stat.S_ISREG(info.st_mode)
or TRUSTED_GIT_PATH.is_symlink():
72 _fail(
"trusted /usr/bin/git is not a regular non-symlink executable")
73 if not os.access(TRUSTED_GIT_PATH, os.X_OK):
74 _fail(
"trusted /usr/bin/git is not executable")
75 return str(TRUSTED_GIT_PATH)
78def sanitized_git_environment(
79 source: Mapping[str, str] |
None =
None,
81 """Return a noninteractive environment isolated from caller Git policy.
83 Repository-local routing is only one way a nested Git command can escape
84 its fixture. Global or system configuration can select an attributes file,
85 and attributes can execute clean/smudge filters. Every inherited ``GIT_*``
86 selector and executable-helper variable is therefore removed here, then
87 the supported noninteractive controls are rebound to safe values.
89 environment = os.environ
if source
is None else source
90 helper_environment = frozenset(
109 for name, value
in environment.items()
110 if not name.startswith((
"GIT_",
"BASH_FUNC_"))
and name
not in helper_environment
114 "GIT_ATTR_NOSYSTEM":
"1",
115 "GIT_CONFIG_GLOBAL": os.devnull,
116 "GIT_CONFIG_NOSYSTEM":
"1",
117 "GIT_CONFIG_SYSTEM": os.devnull,
118 "GIT_EDITOR":
"false",
119 "GIT_OPTIONAL_LOCKS":
"0",
121 "RA8_TRUSTED_GIT": trusted_git_executable(),
122 "GIT_SEQUENCE_EDITOR":
"false",
123 "GIT_SSH_COMMAND":
"false",
124 "GIT_TERMINAL_PROMPT":
"0",
125 "GIT_CONFIG_COUNT":
"3",
126 "GIT_CONFIG_KEY_0":
"core.hooksPath",
127 "GIT_CONFIG_VALUE_0": os.devnull,
128 "GIT_CONFIG_KEY_1":
"core.fsmonitor",
129 "GIT_CONFIG_VALUE_1":
"false",
130 "GIT_CONFIG_KEY_2":
"core.attributesFile",
131 "GIT_CONFIG_VALUE_2": os.devnull,
139def _network_git_environment(source: Mapping[str, str]) -> dict[str, str]:
140 """Keep operator transport policy while removing every other Git selector."""
144 "GIT_CONFIG_NOSYSTEM",
149 "GIT_TERMINAL_PROMPT",
151 clean = {name: value
for name, value
in source.items()
if not name.startswith(
"GIT_")}
152 clean.update({name: source[name]
for name
in transport_names
if name
in source})
153 clean.update({
"GIT_OPTIONAL_LOCKS":
"0",
"GIT_PAGER":
"cat",
"PAGER":
"cat",
"TERM":
"dumb"})
158def isolated_git_environment() -> Iterator[None]:
159 """Temporarily install the hardened child environment, then restore all bytes."""
160 original = dict(os.environ)
162 os.environ.update(sanitized_git_environment(original))
167 os.environ.update(original)
170def _git(root: Path, *args: str, clean: bool =
True) -> bytes:
171 """Run Git for the helper's synthetic fixture."""
172 environment = sanitized_git_environment()
if clean
else os.environ.copy()
173 proc = subprocess.run(
174 [trusted_git_executable(),
"-C", str(root), *args],
179 if proc.returncode != 0:
180 detail = os.fsdecode(proc.stderr).strip()
181 message = f
"git {' '.join(args)} failed: {detail}"
186def _local_config_values(root: Path, key: str) -> tuple[str, ...]:
187 """Return every local repository config value, preserving cardinality."""
188 proc = subprocess.run(
190 trusted_git_executable(),
199 env=sanitized_git_environment(),
203 if proc.returncode == 1:
205 if proc.returncode != 0:
206 detail = os.fsdecode(proc.stderr).strip()
207 _fail(f
"cannot read local Git config {key}: {detail}")
208 return tuple(os.fsdecode(value)
for value
in proc.stdout.split(b
"\0")
if value)
211def _attribute_text_tokens(text: str) -> list[str]:
212 """Return the governed tokens from decoded Git attribute text."""
213 tokens: list[str] = []
214 for raw_line
in text.splitlines():
215 line = raw_line.lstrip()
216 if line
and not line.startswith(
"#"):
217 tokens.extend(line.split()[1:])
221def _attribute_tokens(path: Path) -> list[str]:
222 """Return attribute tokens from one real, bounded UTF-8 attribute file."""
225 except FileNotFoundError:
227 if not stat.S_ISREG(info.st_mode)
or stat.S_ISLNK(info.st_mode):
228 _fail(f
"refusing non-regular Git attribute file: {path}")
230 return _attribute_text_tokens(path.read_text(encoding=
"utf-8"))
231 except (OSError, UnicodeError)
as exc:
232 message = f
"refusing unreadable Git attribute file: {path}"
233 raise GitEnvironmentError(message)
from exc
236def _trusted_attribute(token: str) -> bool:
237 """Return whether an executable-shaped token matches the fixed tree policy."""
238 name, separator, value = token.partition(
"=")
241 return (name ==
"diff" and value
in {
"c",
"cpp",
"lfs"})
or (
242 name ==
"filter" and value ==
"lfs"
246def _validate_local_driver_config(root: Path) ->
None:
247 """Reject unexpected filter definitions and executable diff drivers."""
249 "filter.lfs.clean":
"git-lfs clean -- %f",
250 "filter.lfs.process":
"git-lfs filter-process",
251 "filter.lfs.required":
"true",
252 "filter.lfs.smudge":
"git-lfs smudge -- %f",
254 proc = subprocess.run(
256 trusted_git_executable(),
266 env=sanitized_git_environment(),
270 if proc.returncode
not in {0, 1}:
271 _fail(f
"cannot inventory local Git drivers: {os.fsdecode(proc.stderr).strip()}")
272 names = {os.fsdecode(item)
for item
in proc.stdout.split(b
"\0")
if item}
273 unexpected_filters = sorted(
274 name
for name
in names
if name.startswith(
"filter.")
and name
not in expected
276 executable_diffs = sorted(
279 if name ==
"diff.external" or name.endswith((
".command",
".textconv"))
281 if unexpected_filters
or executable_diffs:
282 bad =
", ".join([*unexpected_filters, *executable_diffs])
283 _fail(f
"refusing untrusted local Git driver config: {bad}")
284 actual = {key: _local_config_values(root, key)
for key
in expected}
285 exact = {key: (value,)
for key, value
in expected.items()}
286 if not (all(
not values
for values
in actual.values())
or actual == exact):
287 _fail(
"refusing drifted partial filter.lfs local configuration")
291 source: Mapping[str, str], *, network: bool =
False
292) -> tuple[tuple[str, str, str], ...]:
293 """Describe the strict child environment as shell-safe structured rows."""
294 clean = _network_git_environment(source)
if network
else sanitized_git_environment(source)
295 rows = [(
"unset", name,
"")
for name
in sorted(source)
if name
not in clean]
296 governed = {name
for name
in clean
if name.startswith(
"GIT_")
or name
in {
"PAGER",
"TERM"}}
297 rows.extend((
"set", name, clean[name])
for name
in sorted(governed))
301def _commit_attribute_sources(root: Path, commit: str) -> list[tuple[str, list[str]]]:
302 """Return every .gitattributes token set from one exact commit tree."""
303 resolved = os.fsdecode(_git(root,
"rev-parse",
"--verify", f
"{commit}^{{commit}}")).strip()
304 raw_paths = _git(root,
"ls-tree",
"-r",
"--name-only",
"-z", resolved)
305 sources: list[tuple[str, list[str]]] = []
306 for raw_path
in raw_paths.split(b
"\0"):
309 relative = os.fsdecode(raw_path)
310 if Path(relative).name !=
".gitattributes":
312 source = f
"{resolved}:{relative}"
314 text = _git(root,
"show", source).decode(
"utf-8")
315 except UnicodeError
as exc:
316 message = f
"refusing non-UTF-8 Git attribute blob: {source}"
317 raise GitEnvironmentError(message)
from exc
318 sources.append((source, _attribute_text_tokens(text)))
322def _worktree_attribute_sources(root: Path) -> list[tuple[str, list[str]]]:
323 """Return every live worktree .gitattributes token set."""
324 sources: list[tuple[str, list[str]]] = []
325 for directory, names, files
in os.walk(root, followlinks=
False):
326 names[:] = [name
for name
in names
if name !=
".git"]
327 if ".gitattributes" in files:
328 path = Path(directory) /
".gitattributes"
329 sources.append((str(path), _attribute_tokens(path)))
333def reject_untrusted_executable_attributes(root: Path, commit: str |
None =
None) ->
None:
334 """Refuse novel filter/diff attributes before a nested checkout runs.
336 The repository's exact built-in C/C++ diff drivers and locally configured
337 Git-LFS boundary are intentional. Any other driver name, or drift in those
338 local definitions, is executable policy and fails closed.
340 root = root.resolve()
341 common = Path(os.fsdecode(_git(root,
"rev-parse",
"--git-common-dir")).strip())
342 git_dir = Path(os.fsdecode(_git(root,
"rev-parse",
"--git-dir")).strip())
343 common = common
if common.is_absolute()
else (root / common).resolve()
344 git_dir = git_dir
if git_dir.is_absolute()
else (root / git_dir).resolve()
346 _commit_attribute_sources(root, commit)
347 if commit
is not None
348 else _worktree_attribute_sources(root)
350 info_paths = dict.fromkeys([common /
"info/attributes", git_dir /
"info/attributes"])
351 sources.extend((str(path), _attribute_tokens(path))
for path
in info_paths)
352 for source, tokens
in sources:
354 name = token.partition(
"=")[0]
355 if name
in {
"diff",
"filter"}
and not _trusted_attribute(token):
356 _fail(f
"refusing untrusted Git attribute {token!r} from {source}")
357 _validate_local_driver_config(root)
360def _tree_digest(root: Path) -> str:
361 """Hash worktree paths, modes, link targets, and regular-file bytes."""
362 digest = hashlib.sha256()
363 for path
in sorted(root.rglob(
"*"), key=
lambda item: os.fsencode(str(item))):
364 if ".git" in path.relative_to(root).parts:
366 rel = os.fsencode(path.relative_to(root).as_posix())
367 mode = stat.S_IMODE(path.lstat().st_mode)
368 digest.update(rel + b
"\0" + str(mode).encode(
"ascii") + b
"\0")
369 if path.is_symlink():
370 digest.update(b
"L" + os.fsencode(path.readlink()))
372 digest.update(b
"F" + path.read_bytes())
375 return digest.hexdigest()
378def _outer_snapshot(root: Path) -> tuple[bytes, bytes, bytes, bytes, str, str]:
379 """Capture the repository state a nested fixture must not mutate."""
381 _git(root,
"rev-parse",
"HEAD"),
382 (root /
".git" /
"index").read_bytes(),
383 (root /
".git" /
"config").read_bytes(),
384 _git(root,
"status",
"--porcelain=v1",
"-z"),
386 _tree_digest(root /
".git" /
"objects"),
390def _init_outer(root: Path) -> tuple[bytes, bytes, bytes, bytes, str, str]:
391 """Create and snapshot a synthetic repository representing a hook caller."""
393 _git(root,
"init",
"--quiet")
394 _git(root,
"config",
"user.email",
"selftest@invalid")
395 _git(root,
"config",
"user.name",
"selftest")
396 (root /
"sentinel.txt").write_text(
"outer sentinel\n", encoding=
"ascii")
397 _git(root,
"add",
"sentinel.txt")
398 _git(root,
"commit",
"--quiet",
"-m",
"outer sentinel")
399 return _outer_snapshot(root)
402def _exercise_nested_repo(outer: Path, inner: Path) ->
None:
403 """Prove the hostile environment routes unsanitized Git to ``outer``."""
404 os.environ[
"GIT_DIR"] = str(outer /
".git")
405 os.environ[
"GIT_WORK_TREE"] = str(outer)
406 os.environ[
"GIT_INDEX_FILE"] = str(outer /
".git" /
"index")
407 resolved = os.fsdecode(_git(inner,
"rev-parse",
"--show-toplevel", clean=
False)).strip()
408 if Path(resolved).resolve() != outer.resolve():
409 _fail(
"hostile Git environment did not reproduce outer routing")
410 with isolated_git_environment():
411 _git(inner,
"init",
"--quiet", clean=
False)
412 _git(inner,
"config",
"user.email",
"selftest@invalid", clean=
False)
413 _git(inner,
"config",
"user.name",
"selftest", clean=
False)
414 (inner /
"fixture.txt").write_text(
"inner fixture\n", encoding=
"ascii")
415 _git(inner,
"add",
"fixture.txt", clean=
False)
416 _git(inner,
"commit",
"--quiet",
"-m",
"inner fixture", clean=
False)
419def _hostile_config_environment(root: Path) -> tuple[dict[str, str], Path]:
420 """Create global/system config whose attributes execute a byte-preserving filter."""
422 marker = root /
"filter-executed"
423 helper = root /
"filter-helper.sh"
425 f
"#!/bin/sh\nprintf x >> {shlex.quote(str(marker))}\ncat\n",
429 attributes = root /
"global-attributes"
430 attributes.write_text(
"* filter=ra8-hostile\n", encoding=
"ascii")
431 system_config = root /
"system.config"
432 system_config.write_text(
433 f
"[core]\n\tattributesFile = {attributes}\n",
436 global_config = root /
"global.config"
437 global_config.write_text(
438 f
'[filter "ra8-hostile"]\n\tclean = {helper}\n\tsmudge = {helper}\n\trequired = true\n',
443 "GIT_ATTR_NOSYSTEM":
"0",
444 "GIT_CONFIG_GLOBAL": str(global_config),
445 "GIT_CONFIG_NOSYSTEM":
"0",
446 "GIT_CONFIG_SYSTEM": str(system_config),
452def _hostile_process_environment(root: Path) -> tuple[dict[str, str], tuple[Path, ...]]:
453 """Build PATH, shell-startup, exported-function, and Python-startup attacks."""
455 git_marker = root /
"git-authority-executed"
456 bash_marker = root /
"bash-startup-executed"
457 python_marker = root /
"python-startup-executed"
458 fake_bin = root /
"source/.venv/bin"
459 fake_bin.mkdir(parents=
True)
460 fake_git = fake_bin /
"git"
462 f
'#!/bin/sh\nprintf x >> {shlex.quote(str(git_marker))}\nexec /usr/bin/git "$@"\n',
465 fake_git.chmod(0o755)
466 bash_env = root /
"bash-env"
467 bash_env.write_text(f
"printf x >> {shlex.quote(str(bash_marker))}\n", encoding=
"ascii")
468 python_path = root /
"python-path"
472 (python_path /
"sitecustomize.py").write_text(
473 "from pathlib import Path\n"
474 f
"Path({str(python_marker)!r}).write_text('x', encoding='ascii')\n",
477 function = f
'() {{ printf x >> {shlex.quote(str(git_marker))}; /usr/bin/git "$@"; }}'
479 "BASH_ENV": str(bash_env),
480 "BASH_FUNC_git%%": function,
481 "ENV": str(bash_env),
483 "PATH": f
"{fake_bin}:{os.environ.get('PATH', '')}",
484 "PYTHONPATH": str(python_path),
485 "RA8_NON_GIT_SENTINEL":
"preserved",
487 return environment, (git_marker, bash_marker, python_marker)
490def _prove_hostile_process_attacks_execute(
491 hostile: Mapping[str, str], markers: Sequence[Path]
493 """Prove every interpreter/executable attack is live before strict boundaries."""
494 environment = os.environ.copy()
495 for name
in (
"SSH_CLIENT",
"SSH_CONNECTION",
"SSH_TTY"):
496 environment.pop(name,
None)
497 environment.update(hostile)
499 [
"/bin/bash",
"-c",
"git --version >/dev/null"],
504 [sys.executable,
"-c",
"pass"],
508 missing = [str(path)
for path
in markers
if not path.exists()]
510 _fail(f
"hostile executable/interpreter probes did not fire: {missing}")
515def _prove_hostile_config_executes(root: Path, hostile: Mapping[str, str], marker: Path) ->
None:
516 """Prove the hostile configuration is live before repaired code suppresses it."""
517 probe = root /
"config-probe"
519 _git(probe,
"init",
"--quiet")
520 (probe /
"probe.txt").write_text(
"probe\n", encoding=
"ascii")
521 environment = sanitized_git_environment()
522 for name
in tuple(environment):
523 if name ==
"GIT_CONFIG_COUNT" or name.startswith((
"GIT_CONFIG_KEY_",
"GIT_CONFIG_VALUE_")):
524 environment.pop(name)
525 environment.update(hostile)
526 proc = subprocess.run(
527 [trusted_git_executable(),
"-C", str(probe),
"add",
"probe.txt"],
532 if proc.returncode != 0
or not marker.is_file():
533 detail = os.fsdecode(proc.stderr).strip()
534 _fail(f
"hostile config/attribute probe did not execute its filter: {detail}")
538def _prove_inherited_global_config_remains_available(root: Path) ->
None:
539 """Prove direct real-tree callers may still inherit harmless global policy."""
540 config = root /
"harmless-global.config"
541 config.write_text(
"[ra8]\n\tharmless = visible\n", encoding=
"ascii")
542 direct = os.environ.copy()
545 "GIT_CONFIG_GLOBAL": str(config),
546 "GIT_CONFIG_NOSYSTEM":
"1",
547 "GIT_CONFIG_SYSTEM": os.devnull,
550 proc = subprocess.run(
551 [trusted_git_executable(),
"config",
"--global",
"--get",
"ra8.harmless"],
556 if proc.returncode != 0
or proc.stdout != b
"visible\n":
557 _fail(
"direct inherited Git environment lost harmless global config")
558 direct[
"RA8_NON_GIT_SENTINEL"] =
"preserved"
559 clean = sanitized_git_environment(direct)
560 if clean.get(
"RA8_NON_GIT_SENTINEL") !=
"preserved":
561 _fail(
"sanitizer removed an unrelated non-Git environment variable")
562 if clean.get(
"GIT_CONFIG_GLOBAL") != os.devnull
or clean.get(
"GIT_CONFIG_SYSTEM") != os.devnull:
563 _fail(
"sanitizer did not bind global/system Git configuration to safe files")
567 repo_root: Path, label: str, relative: str, *args: str
568) -> tuple[str, tuple[str, ...]]:
569 """Build one isolated, absolute-interpreter selftest command."""
570 return label, (sys.executable,
"-I", str(repo_root / relative), *args)
573def _registered_fixture_commands(repo_root: Path) -> tuple[tuple[str, tuple[str, ...]], ...]:
574 """Return the exact selftest suites protected by the nested-Git boundary."""
579 "scripts/checks/check_init_order_freshness.py",
585 "scripts/checks/check_roadmap_dashboard_freshness.py",
590 "markdown-references",
591 "scripts/checks/check_markdown_references.py",
596 "python-lock-policy",
597 "scripts/checks/check_python_lock_policy.py",
600 _python_selftest(repo_root,
"work-harness",
"scripts/dev/work/src/work.py",
"--selftest"),
603 "workspace-lifecycle",
604 "scripts/dev/work/tests/test_workspace_lifecycle.py",
608 "pre-commit-bootstrap",
609 "scripts/checks/check_hook_parity.py",
613 repo_root,
"candidate-assembly",
"scripts/dev/assemble_candidate.py",
"--selftest"
618def _run_registered_fixture(
620 hostile: Mapping[str, str],
621 markers: Sequence[Path],
623 argv: tuple[str, ...],
625 """Run one suite under hostile routing/config and prove no outer mutation."""
626 repo_root = Path(__file__).resolve().parents[2]
627 before = _outer_snapshot(outer)
628 environment = os.environ.copy()
629 for name
in (
"SSH_CLIENT",
"SSH_CONNECTION",
"SSH_TTY"):
630 environment.pop(name,
None)
633 "GIT_INDEX_FILE": str(outer /
".git" /
"index"),
634 "GIT_OBJECT_DIRECTORY": str(outer /
".git" /
"objects"),
635 "PYTHONDONTWRITEBYTECODE":
"1",
639 proc = subprocess.run(
647 if proc.returncode != 0:
648 detail = os.fsdecode(proc.stderr
or proc.stdout).strip()
649 _fail(f
"{label} failed under hostile Git routing: {detail}")
650 if before != _outer_snapshot(outer):
651 _fail(f
"{label} mutated hostile outer Git state")
652 fired = [str(path)
for path
in markers
if path.exists()]
654 _fail(f
"{label} executed hostile inherited policy: {fired}")
657def _exercise_registered_fixture_selftests(outer: Path) ->
None:
658 """Run every repaired fixture suite under routing and config/filter attacks."""
659 repo_root = Path(__file__).resolve().parents[2]
660 git_hostile, git_marker = _hostile_config_environment(outer.parent /
"config-attack")
661 process_hostile, process_markers = _hostile_process_environment(outer.parent /
"process-attack")
662 hostile = {**git_hostile, **process_hostile}
663 markers = (git_marker, *process_markers)
664 _prove_hostile_config_executes(outer.parent, git_hostile, git_marker)
665 _prove_hostile_process_attacks_execute(process_hostile, process_markers)
666 for label, argv
in _registered_fixture_commands(repo_root):
667 _run_registered_fixture(outer, hostile, markers, label, argv)
670def _prepare_snapshot_source(base: Path) -> tuple[Path, tuple[Path, Path, Path]]:
671 """Create a committed EOL/filter tree with executable local Git policy."""
672 source = base /
"snapshot-source"
674 _git(source,
"init",
"--quiet")
675 local_marker = base /
"source-local-filter"
676 fsmonitor_marker = base /
"source-local-fsmonitor"
677 template_marker = base /
"source-local-template-executed"
678 local_helper = base /
"source-local-helper.sh"
679 local_helper.write_text(
680 f
"#!/bin/sh\nprintf x >> {shlex.quote(str(local_marker))}\ncat\n",
683 local_helper.chmod(0o755)
684 fsmonitor = base /
"source-local-fsmonitor.sh"
685 fsmonitor.write_text(
686 f
"#!/bin/sh\nprintf x >> {shlex.quote(str(fsmonitor_marker))}\nprintf '\\n'\n",
689 fsmonitor.chmod(0o755)
690 template = base /
"source-local-template-dir"
691 (template /
"hooks").mkdir(parents=
True)
692 template_hook = template /
"hooks/reference-transaction"
693 template_hook.write_text(
694 f
"#!/bin/sh\nprintf x >> {shlex.quote(str(template_marker))}\n",
697 template_hook.chmod(0o755)
698 (source /
".gitattributes").write_text(
699 "sentinel.txt text eol=crlf\nevil.bin filter=evil\n", encoding=
"ascii"
701 (source /
"sentinel.txt").write_text(
"snapshot sentinel\n", encoding=
"ascii")
702 (source /
"evil.bin").write_bytes(b
"raw fixture\n")
703 _git(source,
"add",
".gitattributes",
"sentinel.txt",
"evil.bin")
707 "user.email=selftest@invalid",
709 "user.name=selftest",
715 _git(source,
"config",
"--local",
"filter.evil.clean", str(local_helper))
716 _git(source,
"config",
"--local",
"filter.evil.smudge", str(local_helper))
717 _git(source,
"config",
"--local",
"filter.evil.required",
"true")
718 _git(source,
"config",
"--local",
"core.fsmonitor", str(fsmonitor))
719 _git(source,
"config",
"--local",
"init.templateDir", str(template))
720 return source, (local_marker, fsmonitor_marker, template_marker)
723def _exercise_shell_snapshot_boundary(outer: Path) ->
None:
724 """Prove fresh checkout keeps EOL semantics without source Git helpers."""
725 repo_root = Path(__file__).resolve().parents[2]
726 hostile, marker = _hostile_config_environment(outer.parent /
"shell-config-attack")
727 _prove_hostile_config_executes(outer.parent /
"shell-config-attack", hostile, marker)
728 source, local_markers = _prepare_snapshot_source(outer.parent)
729 output = outer.parent /
"snapshot-output"
731 before = _outer_snapshot(outer)
732 environment = os.environ.copy()
735 "GIT_INDEX_FILE": str(outer /
".git" /
"index"),
736 "GIT_OBJECT_DIRECTORY": str(outer /
".git" /
"objects"),
742source "$1/scripts/dev/git_environment.sh"
743source "$1/scripts/ci/lib/snapshot.sh"
745materialise_head_snapshot "$3"
746git -C "$3" ls-files --error-unmatch sentinel.txt >/dev/null
748 proc = subprocess.run(
764 if proc.returncode != 0:
765 detail = os.fsdecode(proc.stderr
or proc.stdout).strip()
766 _fail(f
"shell snapshot boundary failed under hostile Git policy: {detail}")
767 if before != _outer_snapshot(outer):
768 _fail(
"shell snapshot boundary mutated hostile outer Git state")
769 if any(path.exists()
for path
in (marker, *local_markers)):
770 _fail(
"shell snapshot boundary executed inherited or source-local Git policy")
771 if (output /
"sentinel.txt").read_bytes() != b
"snapshot sentinel\r\n":
772 _fail(
"strict snapshot lost committed CRLF checkout semantics")
773 if (output /
"evil.bin").read_bytes() != b
"raw fixture\n":
774 _fail(
"strict snapshot changed an unconfigured filtered blob")
777def _exercise_shell_push_transport(root: Path) ->
None:
778 """Prove bounded push keeps SSH policy but cannot inherit repo routing."""
779 adapter = Path(__file__).with_suffix(
".sh")
780 capture = root /
"push-environment"
781 capture_helper = root /
"capture-push-environment"
782 capture_helper.write_text(
784 "printf '%s\\n%s\\n%s\\n%s\\n' \"${GIT_SSH_COMMAND-unset}\" "
785 '"${GIT_DIR-unset}" "${GIT_CONFIG_COUNT-unset}" "$*" >"$RA8_PUSH_CAPTURE"\n',
788 capture_helper.chmod(0o755)
789 environment = os.environ.copy()
792 "GIT_DIR": str(root /
"hostile.git"),
793 "GIT_CONFIG_COUNT":
"1",
794 "GIT_CONFIG_KEY_0":
"core.hooksPath",
795 "GIT_CONFIG_VALUE_0": str(root /
"hostile-hooks"),
796 "GIT_SSH_COMMAND":
"ssh -F operator-config",
797 "RA8_PUSH_CAPTURE": str(capture),
798 "RA8_PUSH_HELPER": str(capture_helper),
803 "run_git_network_with_inherited_transport -c "
804 "'alias.ra8-capture=! \"$RA8_PUSH_HELPER\"' "
805 "ra8-capture push origin gh-pages\n"
807 proc = subprocess.run(
808 [
"/bin/bash",
"-p",
"-c", script,
"shell-push", str(adapter)],
814 if proc.returncode != 0:
815 _fail(f
"shell push transport boundary failed: {os.fsdecode(proc.stderr).strip()}")
816 lines = capture.read_text(encoding=
"ascii").splitlines()
817 expected_tail = [
"unset",
"push origin gh-pages"]
818 hostile_dir = str(root /
"hostile.git")
820 len(lines) != PUSH_CAPTURE_FIELDS
821 or lines[0] !=
"ssh -F operator-config"
822 or lines[1] == hostile_dir
823 or lines[2:] != expected_tail
825 _fail(f
"shell push transport selected the wrong environment/argv: {lines!r}")
828def _exercise_shell_command_authority(root: Path) ->
None:
829 """Prove aliases, functions, hashes, PATH, and source tools cannot select Git."""
830 adapter = Path(__file__).with_suffix(
".sh")
839 shopt -s expand_aliases
840 alias git="'$helper'"
844 git() { "$helper" "$@"; }
848 hash -p "$helper" git
852 PATH="$(dirname "$helper"):$PATH"
860run_sanitized_git --version >/dev/null
863 for mode
in (
"alias",
"function",
"hash",
"path",
"source-venv"):
864 directory = root / (
"source/.venv/bin" if mode ==
"source-venv" else f
"{mode}-bin")
865 directory.mkdir(parents=
True)
866 marker = root / f
"{mode}.fired"
867 helper = directory /
"git"
868 helper.write_text(f
"#!/bin/sh\nprintf x >>{shlex.quote(str(marker))}\n", encoding=
"ascii")
870 proc = subprocess.run(
886 if proc.returncode != 0
or marker.exists():
887 detail = os.fsdecode(proc.stderr
or proc.stdout).strip()
888 _fail(f
"shell Git authority failed for {mode}: {detail}")
891def _prove_hostile_index_and_object_routing(outer: Path, inner: Path) ->
None:
892 """Prove the external index/object environment would mutate without isolation."""
895 _git(inner,
"init",
"--quiet")
896 (inner /
"probe.txt").write_text(
"hostile routing probe\n", encoding=
"ascii")
897 index_before = (outer /
".git" /
"index").read_bytes()
898 objects_before = _tree_digest(outer /
".git" /
"objects")
899 with isolated_git_environment():
900 os.environ[
"GIT_INDEX_FILE"] = str(outer /
".git" /
"index")
901 os.environ[
"GIT_OBJECT_DIRECTORY"] = str(outer /
".git" /
"objects")
902 _git(inner,
"add",
"probe.txt", clean=
False)
903 if (outer /
".git" /
"index").read_bytes() == index_before:
904 _fail(
"hostile GIT_INDEX_FILE probe did not redirect the fixture index")
905 if _tree_digest(outer /
".git" /
"objects") == objects_before:
906 _fail(
"hostile GIT_OBJECT_DIRECTORY probe did not redirect fixture objects")
909def run_selftest() -> int:
910 """Verify nested fixture activity cannot alter a hostile outer repository."""
911 original = dict(os.environ)
913 with tempfile.TemporaryDirectory(prefix=
"ra8-git-environment-")
as temp:
915 outer = base /
"outer"
916 inner = base /
"inner"
917 before = _init_outer(outer)
919 _exercise_nested_repo(outer, inner)
920 after = _outer_snapshot(outer)
922 _fail(
"nested fixture mutated outer Git or worktree state")
923 if os.fsdecode(_git(inner,
"status",
"--porcelain=v1")).strip():
924 _fail(
"nested fixture repository is not clean")
925 _exercise_registered_fixture_selftests(outer)
926 _exercise_shell_snapshot_boundary(outer)
927 _exercise_shell_push_transport(base)
928 _exercise_shell_command_authority(base)
929 _prove_hostile_index_and_object_routing(base /
"probe-outer", base /
"probe-inner")
930 _prove_inherited_global_config_remains_available(base)
931 except (GitEnvironmentError, OSError)
as exc:
932 print(f
"SELFTEST FAIL: {exc}")
936 os.environ.update(original)
938 "selftest: hostile routing/config/filter environment, shell snapshot/push, and "
939 "8 registered fixture suites stay isolated: OK"
945 """Print the shared variable list or run its mutation regression."""
946 parser = argparse.ArgumentParser(description=__doc__)
947 group = parser.add_mutually_exclusive_group(required=
True)
948 group.add_argument(
"--names", action=
"store_true")
949 group.add_argument(
"--shell-contract", action=
"store_true")
950 group.add_argument(
"--network-shell-contract", action=
"store_true")
951 group.add_argument(
"--selftest", action=
"store_true")
952 group.add_argument(
"--check-attributes", metavar=
"ROOT")
953 parser.add_argument(
"--commit")
954 args = parser.parse_args()
955 if args.commit
is not None and args.check_attributes
is None:
956 parser.error(
"--commit requires --check-attributes")
958 print(
"\n".join(LOCAL_GIT_ENVIRONMENT))
960 if args.shell_contract
or args.network_shell_contract:
961 for action, name, value
in _shell_contract(os.environ, network=args.network_shell_contract):
962 print(f
"{action}\t{name}\t{value}")
965 return run_selftest()
967 reject_untrusted_executable_attributes(Path(args.check_attributes), args.commit)
968 except (GitEnvironmentError, OSError)
as exc:
969 print(f
"Git attribute policy: FAIL: {exc}", file=sys.stderr)
971 print(
"Git attribute policy: PASS")
975if __name__ ==
"__main__":
976 raise SystemExit(
main())
void main(void)
The application entry point Reset_Handler hands control to.