143 lines
5.8 KiB
Python
143 lines
5.8 KiB
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
import shlex
|
|
import subprocess
|
|
import sys
|
|
import unittest
|
|
from unittest.mock import patch
|
|
|
|
|
|
META_ROOT = Path(__file__).resolve().parents[1]
|
|
RELEASE_TOOLS_ROOT = META_ROOT / "tools" / "release"
|
|
|
|
if str(RELEASE_TOOLS_ROOT) not in sys.path:
|
|
sys.path.insert(0, str(RELEASE_TOOLS_ROOT))
|
|
|
|
from govoplan_release import git_state # noqa: E402
|
|
|
|
|
|
class ReleaseGitStateTests(unittest.TestCase):
|
|
def test_unset_ssh_address_family_preserves_original_command_and_operator_config(self) -> None:
|
|
environment = git_state.sanitized_git_environment({})
|
|
|
|
self.assertEqual(
|
|
[
|
|
"/usr/bin/ssh", "-o", "BatchMode=yes", "-o", "ConnectTimeout=8",
|
|
],
|
|
shlex.split(environment["GIT_SSH_COMMAND"]),
|
|
)
|
|
self.assertNotIn("GOVOPLAN_RELEASE_SSH_ADDRESS_FAMILY", environment)
|
|
self.assertEqual(environment, git_state.sanitized_git_environment(environment))
|
|
|
|
def test_ssh_address_family_accepts_only_fixed_choices_and_survives_resanitizing(self) -> None:
|
|
for family in ("any", "inet", "inet6"):
|
|
with self.subTest(family=family):
|
|
environment = git_state.sanitized_git_environment({
|
|
"GOVOPLAN_RELEASE_SSH_ADDRESS_FAMILY": family,
|
|
"GIT_SSH_COMMAND": "/attacker/ssh -o StrictHostKeyChecking=no",
|
|
"GIT_SSH": "/attacker/ssh",
|
|
"PATH": "/attacker/bin",
|
|
})
|
|
|
|
self.assertEqual(
|
|
[
|
|
"/usr/bin/ssh", "-o", "BatchMode=yes", "-o", "ConnectTimeout=8",
|
|
"-o", f"AddressFamily={family}",
|
|
],
|
|
shlex.split(environment["GIT_SSH_COMMAND"]),
|
|
)
|
|
self.assertNotIn("GIT_SSH", environment)
|
|
self.assertEqual("/usr/bin:/bin", environment["PATH"])
|
|
self.assertEqual(environment, git_state.sanitized_git_environment(environment))
|
|
|
|
def test_invalid_ssh_address_family_is_rejected_before_git_runs(self) -> None:
|
|
for invalid in (
|
|
"", "INET", "ipv4", " inet", "inet ", "inet\n",
|
|
"inet; touch /not-executed", "inet -o StrictHostKeyChecking=no",
|
|
"$(not-executed)",
|
|
):
|
|
with (
|
|
self.subTest(value=invalid),
|
|
patch.dict("os.environ", {"GOVOPLAN_RELEASE_SSH_ADDRESS_FAMILY": invalid}),
|
|
patch.object(git_state.subprocess, "run") as run,
|
|
):
|
|
with self.assertRaisesRegex(
|
|
ValueError, "GOVOPLAN_RELEASE_SSH_ADDRESS_FAMILY must be any, inet, or inet6",
|
|
):
|
|
git_state.git(Path("/workspace/govoplan-core"), "status", "--porcelain")
|
|
run.assert_not_called()
|
|
|
|
def test_source_provenance_readback_keeps_family_but_discards_ssh_command_override(self) -> None:
|
|
from govoplan_release.source_provenance import inspect_remote_tag
|
|
|
|
completed = subprocess.CompletedProcess(
|
|
[], 0, f"{'a' * 40}\trefs/tags/v1.2.3\n{'b' * 40}\trefs/tags/v1.2.3^{{}}\n", "",
|
|
)
|
|
with (
|
|
patch.dict("os.environ", {
|
|
"GOVOPLAN_RELEASE_SSH_ADDRESS_FAMILY": "inet",
|
|
"GIT_SSH_COMMAND": "/attacker/ssh -o StrictHostKeyChecking=no",
|
|
}),
|
|
patch("govoplan_release.repository_tag.subprocess.run", return_value=completed) as run,
|
|
):
|
|
result = inspect_remote_tag(
|
|
path=Path("/workspace/govoplan-core"), remote="origin",
|
|
remote_url="git@git.add-ideas.de:GovOPlaN/govoplan-core.git", tag="v1.2.3",
|
|
)
|
|
|
|
self.assertEqual("b" * 40, result.commit)
|
|
self.assertEqual(
|
|
"/usr/bin/ssh -o BatchMode=yes -o ConnectTimeout=8 -o AddressFamily=inet",
|
|
run.call_args.kwargs["env"]["GIT_SSH_COMMAND"],
|
|
)
|
|
|
|
def test_manifest_version_does_not_confuse_interface_versions(self) -> None:
|
|
from tempfile import TemporaryDirectory
|
|
|
|
with TemporaryDirectory() as directory:
|
|
root = Path(directory) / "govoplan-example"
|
|
manifest = root / "src" / "govoplan_example" / "backend" / "manifest.py"
|
|
manifest.parent.mkdir(parents=True)
|
|
manifest.write_text(
|
|
"\n".join(
|
|
(
|
|
'MODULE_VERSION = "1.2.3"',
|
|
"manifest = ModuleManifest(",
|
|
' id="example",',
|
|
" version=MODULE_VERSION,",
|
|
" provides_interfaces=(",
|
|
' ModuleInterfaceProvider(name="example.items", version="9.8.7"),',
|
|
" ),",
|
|
")",
|
|
"",
|
|
)
|
|
),
|
|
encoding="utf-8",
|
|
)
|
|
|
|
versions = git_state.read_manifest_versions(root)
|
|
|
|
self.assertEqual(("1.2.3",), versions)
|
|
|
|
def test_git_trusts_only_the_resolved_repository_for_each_command(self) -> None:
|
|
repository = Path("/workspace/../workspace/govoplan-core")
|
|
completed = subprocess.CompletedProcess([], 0, "", "")
|
|
|
|
with patch.object(git_state.subprocess, "run", return_value=completed) as run:
|
|
result = git_state.git(repository, "status", "--porcelain")
|
|
|
|
self.assertEqual(result.returncode, 0)
|
|
command = run.call_args.args[0]
|
|
self.assertIn(f"safe.directory={repository.resolve()}", command)
|
|
self.assertNotIn("safe.directory=*", command)
|
|
self.assertEqual(run.call_args.kwargs["cwd"], repository)
|
|
self.assertEqual(
|
|
run.call_args.kwargs["env"]["GIT_CONFIG_GLOBAL"],
|
|
"/dev/null",
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|