This repository has been archived on 2026-11-08. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
platform-ci/tests/test_git_trust.py
forge-recoveryandClaude Sonnet 5.5 84c6f19438
KofTwentyTwo/ci-policy: ci / fork-guard (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / go-0 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / go-nix-1 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / gradle-2 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / gradle-3 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / java-docker-4 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / java-maven-5 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / manifests-6 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / nix-7 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / node-8 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / python-9 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / rust-10 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / rust-11 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / rust-12 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / source-config-13 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / source-config-14 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / image-build-0 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / image-build-1 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / image-build-2 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / image-build-3 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / image-build-4 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / image-build-5 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / image-build-6 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / image-build-7 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / not-enrolled (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / content-validate-0 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / standards-0 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / node-matrix-0 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / source-config-15 (pull_request) Blocked by required conditions
KofTwentyTwo/ci-policy: Shared source and config CI / verify (pull_request) Blocked by required conditions
KofTwentyTwo/ci-policy: Shared source and config CI / secrets (pull_request) Blocked by required conditions
KofTwentyTwo/ci-policy: Shared source and config CI / gate (pull_request) Waiting for needed jobs
KofTwentyTwo/ci-policy: ci / ci-result (pull_request) Waiting for needed jobs
KofTwentyTwo/ci-policy: native / fork-deny (pull_request) Skipped
KofTwentyTwo/ci-policy: native / build-macos-0 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / build-macos-1 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / build-macos-2 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / build-macos-3 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / build-windows-4 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / build-windows-5 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / build-windows-6 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / build-windows-7 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / dotnet-8 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / dotnet-9 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / dotnet-10 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / dotnet-11 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-12 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-13 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-14 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-15 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-16 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-17 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-18 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-19 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / native-result (pull_request) Canceled after 4s
feat(ci): advance shared pipeline capabilities
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 05:03:47 +00:00

139 lines
7.8 KiB
Python

"""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()