"""Git trust for the source check: exactly the one checkout, for that one invocation, with no host, user or inherited Git configuration involved. The builtin checkout's owner can differ from the user a job runs as, so `git ls-files` inside the unchanged helper reports `dubious ownership`. The adapter therefore starts the helper with command-scope Git configuration naming only that checkout's resolved path. These tests prove what the environment contains, that hostile inherited Git variables never reach the helper, that other tools get none of it, and that real Git sees exactly that one path. They cannot reproduce a foreign-owned checkout locally, so the live source runs on the synthetic repositories remain the proof of the failure itself. """ import os import shutil import subprocess import tempfile import unittest from pathlib import Path from typing import override from platform_ci.build_tools import BUILD_TOOLS, SOURCE_CHECK_PROGRAM from platform_ci.configuration import parse_project_configuration from platform_ci.execution import git_trust_environment, run_target_commands from tests.support import event, example_configuration, plan_for from tests.test_pool_and_source import HELPERS, Recorder, source_configuration HOSTILE_PARENT = { "PATH": "/usr/bin", "HOME": "/tmp", # noqa: S108 "GIT_CONFIG_COUNT": "3", "GIT_CONFIG_KEY_0": "safe.directory", "GIT_CONFIG_VALUE_0": "*", "GIT_CONFIG_KEY_1": "core.hooksPath", "GIT_CONFIG_VALUE_1": "/evil/hooks", "GIT_CONFIG_KEY_2": "core.fsmonitor", "GIT_CONFIG_VALUE_2": "/evil/monitor", "GIT_CONFIG_PARAMETERS": "'safe.directory=*'", "GIT_CONFIG_GLOBAL": "/evil/gitconfig", "GIT_CONFIG_SYSTEM": "/evil/system", "GIT_DIR": "/evil/dir", "GIT_WORK_TREE": "/evil/tree", "GIT_SSH_COMMAND": "evil", "GIT_EXEC_PATH": "/evil/exec", } class GitTrustEnvironmentTest(unittest.TestCase): """The environment names one exact path and nothing else.""" def test_exactly_one_command_scope_entry_for_the_resolved_path(self) -> None: with tempfile.TemporaryDirectory() as directory: checkout = Path(directory) / "checkout" checkout.mkdir() environment = git_trust_environment(checkout) self.assertEqual(environment, {"GIT_CONFIG_COUNT": "1", "GIT_CONFIG_KEY_0": "safe.directory", "GIT_CONFIG_VALUE_0": checkout.resolve().as_posix()}) self.assertNotIn("\\", environment["GIT_CONFIG_VALUE_0"]) self.assertNotIn("*", environment["GIT_CONFIG_VALUE_0"]) self.assertEqual(git_trust_environment(checkout / ".." / "checkout"), environment) # a relative or dotted spelling resolves to the same single path def test_a_different_directory_gets_a_different_path_never_a_pattern(self) -> None: with tempfile.TemporaryDirectory() as directory: first, second = Path(directory) / "a", Path(directory) / "b" first.mkdir() second.mkdir() self.assertNotEqual(git_trust_environment(first)["GIT_CONFIG_VALUE_0"], git_trust_environment(second)["GIT_CONFIG_VALUE_0"]) class GitTrustInvocationTest(unittest.TestCase): """Only the source tool gets the trust, and inherited Git variables never reach it.""" @override def setUp(self) -> None: """Create a checkout with the platform's helper directory beside it, as the workflow does.""" directory = tempfile.TemporaryDirectory() self.addCleanup(directory.cleanup) self.source = Path(directory.name) / "checkout" shutil.copytree(HELPERS, self.source / ".platform-ci" / "source_checks") self.target = plan_for(parse_project_configuration(source_configuration()), event())["targets"][0] def test_the_source_tool_runs_with_exactly_the_one_checkout_trusted(self) -> None: recorder = Recorder() self.assertTrue(run_target_commands(self.target, self.source, recorder, {"PATH": "/usr/bin"})) ((command, environment),) = recorder.calls self.assertEqual(command, ["node", SOURCE_CHECK_PROGRAM]) git_variables = {name: value for name, value in environment.items() if name.startswith("GIT_")} self.assertEqual(git_variables, git_trust_environment(self.source)) def test_hostile_inherited_git_variables_are_dropped_and_replaced_by_the_single_entry(self) -> None: recorder = Recorder() run_target_commands(self.target, self.source, recorder, HOSTILE_PARENT) ((_command, environment),) = recorder.calls self.assertEqual({name: value for name, value in environment.items() if name.startswith("GIT_")}, git_trust_environment(self.source)) for hostile in ("GIT_CONFIG_PARAMETERS", "GIT_CONFIG_GLOBAL", "GIT_CONFIG_SYSTEM", "GIT_DIR", "GIT_WORK_TREE", "GIT_SSH_COMMAND", "GIT_EXEC_PATH", "GIT_CONFIG_KEY_1", "GIT_CONFIG_VALUE_2"): self.assertNotIn(hostile, environment) self.assertNotEqual(environment["GIT_CONFIG_VALUE_0"], "*") def test_other_tools_receive_no_git_configuration_at_all(self) -> None: self.assertEqual([name for name, tool in BUILD_TOOLS.items() if tool.inspects_checkout_with_git], ["source"]) maven = plan_for(example_configuration("maven.ci.yaml"), event())["targets"][0] recorder = Recorder() with tempfile.TemporaryDirectory() as home: run_target_commands(maven, self.source, recorder, {**HOSTILE_PARENT, "HOME": home}) for _command, environment in recorder.calls: self.assertEqual([name for name in environment if name.startswith("GIT_")], []) def test_no_code_or_workflow_writes_global_git_state_or_a_wildcard(self) -> None: root = Path(__file__).resolve().parent.parent for path in (*(root / "platform_ci").rglob("*.py"), root / ".gitea" / "scoped_workflows" / "pipeline.yaml"): text = path.read_text(encoding="utf-8") for forbidden in ("--global", "--system", "safe.directory=*", "safe.directory *"): self.assertNotIn(forbidden, text, path.name) @unittest.skipUnless(shutil.which("git"), "needs git on PATH") class RealGitSeesOnlyTheTrustedPathTest(unittest.TestCase): """Real Git, given the environment, reports exactly one command-scope safe.directory: the checkout itself.""" def _command_scope_values(self, directory: Path) -> list[str]: environment = {name: value for name, value in os.environ.items() if not name.startswith("GIT_")} | git_trust_environment(directory) done = subprocess.run(["git", "config", "--show-scope", "--get-all", "safe.directory"], cwd=directory, env=environment, capture_output=True, text=True, check=False) # noqa: S607 return [line.split("\t", 1)[1] for line in done.stdout.splitlines() if line.startswith("command\t")] def test_exactly_the_checkout_is_trusted_and_no_other_path_matches(self) -> None: with tempfile.TemporaryDirectory() as directory: checkout = Path(directory) / "checkout" (checkout / "child").mkdir(parents=True) (Path(directory) / "sibling").mkdir() self.assertEqual(self._command_scope_values(checkout), [checkout.resolve().as_posix()]) trusted = set(self._command_scope_values(checkout)) for other in (checkout / "child", Path(directory) / "sibling", Path(directory), checkout.parent.parent): self.assertNotIn(other.resolve().as_posix(), trusted) def test_the_trust_exists_only_in_that_environment_not_in_any_git_file(self) -> None: with tempfile.TemporaryDirectory() as directory: checkout = Path(directory) environment = {name: value for name, value in os.environ.items() if not name.startswith("GIT_")} done = subprocess.run(["git", "config", "--show-scope", "--get-all", "safe.directory"], cwd=checkout, env=environment, capture_output=True, text=True, check=False) # noqa: S607 self.assertNotIn(checkout.resolve().as_posix(), done.stdout) # without the environment the checkout is not trusted anywhere if __name__ == "__main__": unittest.main()