import importlib.util from pathlib import Path import subprocess from unittest.mock import Mock import pytest ROOT = Path(__file__).resolve().parents[1] SPEC = importlib.util.spec_from_file_location("runner_installer", ROOT / "install-runner-hardening.py") installer = importlib.util.module_from_spec(SPEC) SPEC.loader.exec_module(installer) def test_image_preflight_checks_nonroot_openssh_without_network_or_credentials(monkeypatch): run = Mock() monkeypatch.setattr(installer, "run", run) installer.prepare_image(ROOT) build, probe = [call.args for call in run.call_args_list] assert build[:2] == ("docker", "build") assert "--pull=false" in build and "--network=none" in build assert probe[:3] == ("docker", "run", "--rm") assert probe[probe.index("--network") + 1] == "none" assert probe[probe.index("--user") + 1] == "10001:10001" assert "--read-only" in probe assert "id -un" in probe[-1] and "ssh -G -F /dev/null" in probe[-1] assert "--volume" not in probe and "-v" not in probe def test_missing_passwd_or_broken_ssh_aborts_preflight(monkeypatch): monkeypatch.setattr(installer, "run", Mock(side_effect=["", subprocess.CalledProcessError(255, "ssh")])) with pytest.raises(subprocess.CalledProcessError): installer.prepare_image(ROOT) def test_compose_selects_image_with_real_nonroot_account(): dockerfile = (ROOT / "Dockerfile").read_text() assert "addgroup -g 10001 runner" in dockerfile assert "adduser -D -H -u 10001 -G runner" in dockerfile assert "USER 10001:10001" in dockerfile assert installer.RUNNER_IMAGE in (ROOT / "compose.yaml").read_text()