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_dependency_checkout.py
forge-recoveryandClaude Sonnet 5.5 a36f17b857
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 6s
feat(ci): advance shared pipeline capabilities
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 10:30:25 +00:00

364 lines
20 KiB
Python

"""Pinned private dependency checkout: authorization, exact commit, destination containment, credential isolation and redaction.
NOT EXECUTED when written (test execution was suspended). Every test uses a fake Git runner and temporary directories; no process is started, no
network is used, and the only "token" is an obviously fake string.
"""
import base64
import subprocess
import tempfile
import unittest
from collections.abc import Mapping
from pathlib import Path
from typing import cast, override
from unittest import mock
from platform_ci.dependency_checkout import (
CHECKOUT_SECRET_NAME,
TRUSTED_FORGE,
Binding,
DependencyCheckoutError,
authorize,
checkout_dependencies,
load_bindings,
run_git,
)
from platform_ci.plan import CheckoutTarget, Target
PINNED = "a" * 40
OTHER = "b" * 40
TOKEN = "fake-token-not-a-secret-0123" # noqa: S105
CONSUMER = "Org/consumer"
DEPENDENCY = "Org/content"
BINDINGS = {"content-read": Binding(name="content-read", secret=CHECKOUT_SECRET_NAME, consumers=(CONSUMER,), repositories=(DEPENDENCY,))}
ENVIRONMENT = {"CI_REPOSITORY": CONSUMER, "CI_CHECKOUT_TOKEN": TOKEN}
def _target(**checkouts: CheckoutTarget) -> Target:
"""Return a target carrying only checkouts: the module under test reads nothing else."""
return cast("Target", {"checkouts": checkouts})
def _checkout(path: str = "deps/content", ref: str = PINNED, binding: str = "content-read", repository: str = DEPENDENCY) -> CheckoutTarget:
"""Return one planned checkout."""
return CheckoutTarget(binding=binding, repository=repository, ref=ref, path=path)
class FakeGit:
"""A GitRunner that records every call and behaves like a successful `init`, `fetch`, `checkout` and `rev-parse` of `head`."""
def __init__(self, head: str = PINNED, config_text: str = "[core]\n\tbare = false\n") -> None:
self.calls: list[tuple[Path, tuple[str, ...], dict[str, str]]] = []
self.head = head
self.config_text = config_text
def __call__(self, directory: Path, arguments: tuple[str, ...], environment: Mapping[str, str]) -> bytes:
"""Record the call; `init` creates the Git directory and its configuration, `rev-parse` returns the configured head."""
self.calls.append((directory, arguments, dict(environment)))
if arguments[0] == "init":
(directory / ".git").mkdir()
(directory / ".git" / "config").write_text(self.config_text, encoding="utf-8")
if arguments[0] == "rev-parse":
return f"{self.head}\n".encode()
return b""
def verbs(self) -> list[str]:
"""Return the Git subcommand of each call, in order."""
return [arguments[0] for _directory, arguments, _environment in self.calls]
class _CheckoutCase(unittest.TestCase):
"""A temporary checkout root for each test."""
@override
def setUp(self) -> None:
"""Create the checkout directory."""
scratch = tempfile.TemporaryDirectory()
self.addCleanup(scratch.cleanup)
self.scratch = Path(scratch.name)
self.root = self.scratch / "checkout"
self.root.mkdir()
def run_checkout(self, target: Target, git: FakeGit, environment: Mapping[str, str] = ENVIRONMENT) -> list[str]:
"""Run the real function with the fixed bindings, so no manifest file is needed."""
return checkout_dependencies(target, self.root, environment, BINDINGS, git)
class AuthorizationTest(_CheckoutCase):
"""A dependency is fetched only when the operator's logical binding allows this consumer to read this repository."""
def test_an_authorized_consumer_fetches_exactly_the_pinned_dependency(self) -> None:
"""Allow: the result names the repository and the short commit, and the work tree is under the declared path."""
git = FakeGit()
lines = self.run_checkout(_target(content=_checkout()), git)
self.assertEqual(lines, [f"dependency content: {DEPENDENCY} at {PINNED[:12]} in deps/content"])
self.assertEqual(git.verbs(), ["init", "fetch", "checkout", "rev-parse"])
self.assertEqual({directory for directory, _arguments, _environment in git.calls}, {(self.root / "deps" / "content").resolve()})
def test_every_denial_happens_before_anything_is_fetched_or_created(self) -> None:
"""Deny: unknown binding, a consumer the binding does not list, a repository it does not list; Git never runs and no directory appears."""
cases = {
"unknown binding": (_checkout(binding="other-read"), CONSUMER),
"consumer not listed": (_checkout(), "Org/someone-else"),
"repository not listed": (_checkout(repository="Org/secret-repo"), CONSUMER),
}
for label, (checkout, consumer) in cases.items():
with self.subTest(label):
git = FakeGit()
with self.assertRaises(DependencyCheckoutError):
self.run_checkout(_target(content=checkout), git, {"CI_REPOSITORY": consumer, "CI_CHECKOUT_TOKEN": TOKEN})
self.assertEqual(git.calls, [])
self.assertFalse((self.root / "deps").exists())
def test_the_denial_names_the_binding_not_the_credential(self) -> None:
"""The refusal message is about the binding and repository and never contains the token."""
with self.assertRaises(DependencyCheckoutError) as caught:
authorize(_checkout(repository="Org/secret-repo"), CONSUMER, BINDINGS)
self.assertIn("content-read", str(caught.exception))
self.assertNotIn(TOKEN, str(caught.exception))
def test_a_missing_identity_or_binding_secret_stops_before_any_work(self) -> None:
"""Without the consuming repository or the forwarded secret the step fails closed instead of fetching anonymously."""
for label, environment in {"no consumer": {"CI_CHECKOUT_TOKEN": TOKEN}, "no token": {"CI_REPOSITORY": CONSUMER}}.items():
with self.subTest(label):
git = FakeGit()
with self.assertRaises(DependencyCheckoutError):
self.run_checkout(_target(content=_checkout()), git, environment)
self.assertEqual(git.calls, [])
def test_a_target_without_checkouts_needs_neither_identity_nor_token(self) -> None:
"""Most targets carry no checkouts; nothing is required and nothing runs."""
git = FakeGit()
self.assertEqual(checkout_dependencies(_target(), self.root, {}, BINDINGS, git), [])
self.assertEqual(git.calls, [])
class PinnedCommitTest(_CheckoutCase):
"""The fetched commit must equal the pinned `ref` exactly."""
def test_a_head_that_is_not_the_pinned_commit_is_refused(self) -> None:
"""Mismatch: a remote that answers with a different commit fails the step."""
with self.assertRaisesRegex(DependencyCheckoutError, "not the pinned one"):
self.run_checkout(_target(content=_checkout()), FakeGit(head=OTHER))
def test_a_prefix_of_the_pinned_commit_does_not_match(self) -> None:
"""Only the full id counts; an abbreviated or longer head is a mismatch."""
for head in (PINNED[:12], PINNED + "0"):
with self.subTest(head=head), self.assertRaises(DependencyCheckoutError):
self.run_checkout(_target(content=_checkout(path=f"deps/{len(head)}")), FakeGit(head=head))
def test_the_pinned_commit_is_what_is_fetched(self) -> None:
"""The fetch asks the trusted forge for exactly the declared repository and commit, with no tags and a depth of one."""
git = FakeGit()
self.run_checkout(_target(content=_checkout()), git)
fetch = next(arguments for _directory, arguments, _environment in git.calls if arguments[0] == "fetch")
self.assertEqual(fetch[-2:], (f"{TRUSTED_FORGE}/{DEPENDENCY}.git", PINNED))
self.assertIn("--no-tags", fetch)
self.assertIn("--depth=1", fetch)
class DestinationTest(_CheckoutCase):
"""The destination must stay inside the checkout and must not already hold files (as the module actually enforces)."""
def test_a_path_that_leaves_the_checkout_is_refused_before_git_runs(self) -> None:
"""`..` escapes resolve outside the root and are refused; nothing is created outside it."""
git = FakeGit()
with self.assertRaisesRegex(DependencyCheckoutError, "outside the checkout"):
self.run_checkout(_target(content=_checkout(path="../escaped")), git)
self.assertEqual(git.calls, [])
self.assertFalse((self.scratch / "escaped").exists())
def test_a_symlinked_parent_that_points_outside_is_refused(self) -> None:
"""A link inside the checkout aimed at another directory resolves outside the root, so the fetch never happens there."""
outside = self.scratch / "outside"
outside.mkdir()
try:
(self.root / "link").symlink_to(outside, target_is_directory=True)
except (OSError, NotImplementedError):
self.skipTest("this host cannot create symbolic links")
git = FakeGit()
with self.assertRaisesRegex(DependencyCheckoutError, "symbolic link"):
self.run_checkout(_target(content=_checkout(path="link/dep")), git)
self.assertEqual(git.calls, [])
self.assertEqual(list(outside.iterdir()), [])
def test_a_link_inside_the_checkout_to_its_own_metadata_is_refused_without_git_or_mutation(self) -> None:
"""A link that stays inside the root but points at `.git` or `.platform-ci` is refused: no Git call, and the metadata is byte-for-byte unchanged."""
for protected in (".git", ".platform-ci"):
with self.subTest(protected):
self.setUp()
target_directory = self.root / protected / "refs"
target_directory.mkdir(parents=True)
marker = self.root / protected / "marker"
marker.write_text("original", encoding="utf-8")
try:
(self.root / "deps").symlink_to(target_directory, target_is_directory=True)
except (OSError, NotImplementedError):
self.skipTest("this host cannot create symbolic links")
before = sorted(path.relative_to(self.root).as_posix() for path in self.root.rglob("*") if not path.is_symlink())
git = FakeGit()
with self.assertRaisesRegex(DependencyCheckoutError, "symbolic link"):
self.run_checkout(_target(content=_checkout(path="deps/content")), git)
self.assertEqual(git.calls, [])
self.assertEqual(sorted(path.relative_to(self.root).as_posix() for path in self.root.rglob("*") if not path.is_symlink()), before)
self.assertEqual(marker.read_text(encoding="utf-8"), "original")
def test_a_link_as_the_destination_itself_is_refused(self) -> None:
"""The final component may not be a link either, even to an empty directory inside the checkout."""
(self.root / "elsewhere").mkdir()
try:
(self.root / "dep").symlink_to(self.root / "elsewhere", target_is_directory=True)
except (OSError, NotImplementedError):
self.skipTest("this host cannot create symbolic links")
git = FakeGit()
with self.assertRaisesRegex(DependencyCheckoutError, "symbolic link"):
self.run_checkout(_target(content=_checkout(path="dep")), git)
self.assertEqual(git.calls, [])
def test_a_valid_nested_path_still_works(self) -> None:
"""Plain nested directories, created as needed, are accepted."""
git = FakeGit()
self.run_checkout(_target(content=_checkout(path="vendor/deep/content")), git)
self.assertTrue((self.root / "vendor" / "deep" / "content" / ".git").is_dir())
def test_a_destination_that_already_holds_files_is_refused_and_left_untouched(self) -> None:
"""Existing content is never overlaid or replaced."""
existing = self.root / "deps" / "content"
existing.mkdir(parents=True)
(existing / "keep.txt").write_text("mine", encoding="utf-8")
git = FakeGit()
with self.assertRaisesRegex(DependencyCheckoutError, "already holds files"):
self.run_checkout(_target(content=_checkout()), git)
self.assertEqual(git.calls, [])
self.assertEqual((existing / "keep.txt").read_text(encoding="utf-8"), "mine")
def test_an_existing_empty_directory_is_usable(self) -> None:
"""An empty directory is a valid destination."""
(self.root / "deps" / "content").mkdir(parents=True)
self.assertEqual(len(self.run_checkout(_target(content=_checkout()), FakeGit())), 1)
class CredentialIsolationTest(_CheckoutCase):
"""The token reaches only the environment of the fetch process, never a URL, a file or a later step."""
def test_only_the_fetch_child_environment_carries_the_credential(self) -> None:
"""No argument, no local-command environment and no checked-out file holds the token; the caller's environment is not modified."""
git = FakeGit()
caller = dict(ENVIRONMENT)
self.run_checkout(_target(content=_checkout()), git, caller)
encoded = base64.b64encode(f"x-access-token:{TOKEN}".encode()).decode()
self.assertEqual(caller, ENVIRONMENT)
for directory, arguments, environment in git.calls:
self.assertFalse(any(TOKEN in argument or encoded in argument for argument in arguments), "the credential must never be an argument or part of a URL")
self.assertNotIn("CI_CHECKOUT_TOKEN", environment)
if arguments[0] == "fetch":
carried = [key for key, value in environment.items() if TOKEN in value or encoded in value]
self.assertEqual(carried, ["GIT_CONFIG_VALUE_0"])
self.assertEqual(environment["GIT_CONFIG_NOSYSTEM"], "1")
# the header is scoped to the trusted forge's origin, and the fetch is configured not to follow redirects (configuration only, not behavior)
self.assertEqual(environment["GIT_CONFIG_COUNT"], "2")
self.assertEqual(environment["GIT_CONFIG_KEY_0"], f"http.{TRUSTED_FORGE}/.extraheader")
self.assertEqual((environment["GIT_CONFIG_KEY_1"], environment["GIT_CONFIG_VALUE_1"]), ("http.followRedirects", "false"))
else:
self.assertFalse([key for key in environment if key.startswith(("GIT_CONFIG_KEY", "GIT_CONFIG_VALUE"))], arguments[0])
self.assertFalse([value for value in environment.values() if TOKEN in value or encoded in value], arguments[0])
self.assertNotIn(TOKEN, (directory / ".git" / "config").read_text(encoding="utf-8"))
def test_a_credential_that_reaches_the_repository_configuration_fails_the_step_without_echoing_it(self) -> None:
"""If anything wrote the token or an Authorization header into `.git/config` the step fails, and the error does not repeat the token."""
for leaked in ("[http]\n\textraheader = Authorization: Basic xyz\n", f"[remote]\n\turl = https://{TOKEN}@host/x.git\n"):
with self.subTest(leaked=leaked[:20]):
self.setUp()
with self.assertRaisesRegex(DependencyCheckoutError, "reached the checkout configuration") as caught:
self.run_checkout(_target(content=_checkout()), FakeGit(config_text=leaked))
self.assertNotIn(TOKEN, str(caught.exception))
class UnreadableConfigurationTest(_CheckoutCase):
"""If the repository configuration cannot be read the step fails with a redacted class name, not a raw OS error."""
def test_an_unreadable_git_config_is_a_checkout_error(self) -> None:
"""A runner that leaves no `.git/config` (here: a directory in its place) yields DependencyCheckoutError naming only the error class."""
def git(directory: Path, arguments: tuple[str, ...], _environment: Mapping[str, str]) -> bytes:
if arguments[0] == "init":
(directory / ".git" / "config").mkdir(parents=True)
return f"{PINNED}\n".encode() if arguments[0] == "rev-parse" else b""
with self.assertRaisesRegex(DependencyCheckoutError, "cannot be read: ") as caught:
checkout_dependencies(_target(content=_checkout()), self.root, ENVIRONMENT, BINDINGS, git)
self.assertNotIn(str(self.root), str(caught.exception))
self.assertNotIn(TOKEN, str(caught.exception))
class RedactionTest(unittest.TestCase):
"""What the real Git runner surfaces: a fixed message, never Git's own output, which can echo the remote or a header."""
def test_a_failed_git_command_surfaces_no_stderr(self) -> None:
"""The child's stderr (with a URL and a token in it) is dropped; only a generic message is raised."""
failed = subprocess.CompletedProcess(args=[], returncode=128, stdout=b"", stderr=f"fatal: could not read from https://{TOKEN}@git.example/x.git".encode())
with mock.patch("platform_ci.dependency_checkout.subprocess.run", return_value=failed), self.assertRaises(DependencyCheckoutError) as caught:
run_git(Path.cwd(), ("fetch",), {})
self.assertNotIn(TOKEN, str(caught.exception))
self.assertNotIn("git.example", str(caught.exception))
def test_a_start_failure_or_timeout_reports_only_the_error_type(self) -> None:
"""An OS error or a timeout whose text could hold paths or arguments is reduced to its class name."""
for error in (OSError(f"cannot run with {TOKEN}"), subprocess.TimeoutExpired(cmd=["git", TOKEN], timeout=1)):
with self.subTest(error=type(error).__name__), mock.patch("platform_ci.dependency_checkout.subprocess.run", side_effect=error), self.assertRaises(DependencyCheckoutError) as caught:
run_git(Path.cwd(), ("fetch",), {})
self.assertNotIn(TOKEN, str(caught.exception))
self.assertIn(type(error).__name__, str(caught.exception))
def test_git_runs_with_hooks_off_and_exactly_the_given_environment(self) -> None:
"""The real runner disables hooks and passes the supplied environment, not the process's own."""
done = subprocess.CompletedProcess(args=[], returncode=0, stdout=b"ok", stderr=b"")
with mock.patch("platform_ci.dependency_checkout.subprocess.run", return_value=done) as runner:
self.assertEqual(run_git(Path.cwd(), ("rev-parse", "HEAD"), {"ONLY": "this"}), b"ok")
command = runner.call_args.args[0]
self.assertTrue(any(part.startswith("core.hooksPath=") for part in command))
self.assertEqual(runner.call_args.kwargs["env"], {"ONLY": "this"})
class BindingsManifestTest(unittest.TestCase):
"""The operator manifest authorizes only what it names, and a bad or missing one is an error, not an empty pass."""
@override
def setUp(self) -> None:
"""Create a directory for manifests."""
scratch = tempfile.TemporaryDirectory()
self.addCleanup(scratch.cleanup)
self.directory = Path(scratch.name)
def write(self, text: str) -> Path:
"""Write a manifest file and return its path."""
path = self.directory / "bindings.yaml"
path.write_text(text, encoding="utf-8")
return path
def test_a_valid_manifest_yields_sorted_bindings(self) -> None:
"""The binding keeps its secret slot, consumers and readable repositories."""
path = self.write(f"schema: 1\nbindings:\n content-read:\n secret: {CHECKOUT_SECRET_NAME}\n consumers: [Org/b, Org/a]\n repositories: [Org/content]\n")
binding = load_bindings(path)["content-read"]
self.assertEqual((binding.consumers, binding.repositories), (("Org/a", "Org/b"), ("Org/content",)))
def test_malformed_manifests_are_refused(self) -> None:
"""Wrong secret slot, an extra field, an empty list, a non owner/name entry, a wrong schema or a missing file all fail."""
header = "schema: 1\nbindings:\n content-read:\n"
bad = {
"wrong secret": f"{header} secret: SOME_OTHER_SECRET\n consumers: [Org/a]\n repositories: [Org/c]\n",
"extra field": f"{header} secret: {CHECKOUT_SECRET_NAME}\n consumers: [Org/a]\n repositories: [Org/c]\n admin: true\n",
"empty consumers": f"{header} secret: {CHECKOUT_SECRET_NAME}\n consumers: []\n repositories: [Org/c]\n",
"not owner/name": f"{header} secret: {CHECKOUT_SECRET_NAME}\n consumers: [consumer]\n repositories: [Org/c]\n",
"wrong schema": "schema: 2\nbindings: {}\n",
}
for label, text in bad.items():
with self.subTest(label), self.assertRaises(DependencyCheckoutError):
load_bindings(self.write(text))
with self.assertRaises(DependencyCheckoutError):
load_bindings(self.directory / "missing.yaml")
if __name__ == "__main__":
unittest.main()