"""Software-only regressions for external bootstrap install evidence.""" from __future__ import annotations import hashlib import hmac import json from pathlib import Path import sys import pytest from tools.rf001_bootstrap_install_orchestrator import ( EvidencePathError, PackageValidationError, SIGNATURE_DOMAIN, run_installer, snapshot_tree, validate_pristine_package, ) def _sha256(path: Path) -> str: return hashlib.sha256(path.read_bytes()).hexdigest() def _signed_package(tmp_path: Path) -> tuple[Path, Path]: package = tmp_path / "clean-bootstrap" package.mkdir() (package / "INSTALL_RX_AGENT.cmd").write_text("@echo off\n", encoding="utf-8") payload = package / "payload.bin" payload.write_bytes(b"fixed bootstrap payload") rows = [] for path in sorted(item for item in package.rglob("*") if item.is_file()): rows.append( { "path": path.relative_to(package).as_posix(), "bytes": path.stat().st_size, "sha256": _sha256(path), } ) manifest = { "schema_version": 1, "package_kind": "BOOTSTRAP", "bootstrap_id": "rf001-bootstrap-2099.01.01.1", "agent_release_id": "rf001-agent-2099.01.01.1", "agent_version": "2099.01.01.1", "files": rows, } manifest_bytes = ( json.dumps(manifest, sort_keys=True, separators=(",", ":")) + "\n" ).encode("utf-8") (package / "BOOTSTRAP_MANIFEST.json").write_bytes(manifest_bytes) token = tmp_path / "token.txt" token.write_text("fixed-test-token-0123456789abcdef", encoding="utf-8") signature = hmac.new( token.read_text(encoding="utf-8").encode("utf-8"), SIGNATURE_DOMAIN + manifest_bytes, hashlib.sha256, ).hexdigest() (package / "BOOTSTRAP_MANIFEST.hmac").write_text( signature + "\n", encoding="ascii", ) return package, token def test_pristine_package_uses_external_logs_and_remains_unchanged( tmp_path: Path, ) -> None: package, token = _signed_package(tmp_path) evidence = tmp_path / "external-evidence" before = snapshot_tree(package) command = [ sys.executable, "-c", "import sys; print('installer stdout'); print('installer stderr', file=sys.stderr)", ] result = run_installer(package, evidence, token, command=command) assert result["exit_code"] == 0 assert result["package_content_unchanged_immediate"] is True assert result["package_content_unchanged_after"] is True assert result["package_metadata_unchanged_after"] is True assert snapshot_tree(package) == before assert (evidence / "install.stdout.txt").read_text().strip() == "installer stdout" assert (evidence / "install.stderr.txt").read_text().strip() == "installer stderr" assert not (package / "install.stdout.txt").exists() assert not (package / "install.stderr.txt").exists() preflight = json.loads((evidence / "package_preflight.json").read_text()) assert preflight["hmac"] == "PASS" assert preflight["unexpected"] == 0 assert preflight["missing"] == 0 def test_contaminated_package_fails_closed_before_command(tmp_path: Path) -> None: package, token = _signed_package(tmp_path) (package / "install.stdout.txt").write_text("orchestration leak\n", encoding="utf-8") evidence = tmp_path / "external-evidence" command_marker = tmp_path / "command-ran.txt" command = [ sys.executable, "-c", f"from pathlib import Path; Path({str(command_marker)!r}).write_text('ran')", ] with pytest.raises(PackageValidationError, match="allowlist mismatch"): run_installer(package, evidence, token, command=command) assert not command_marker.exists() assert not (evidence / "install.stdout.txt").exists() assert not (evidence / "install.stderr.txt").exists() def test_evidence_directory_inside_package_is_rejected(tmp_path: Path) -> None: package, token = _signed_package(tmp_path) evidence = package / "runtime-evidence" with pytest.raises(EvidencePathError, match="outside the package"): run_installer(package, evidence, token, command=[sys.executable, "-c", "pass"]) def test_pristine_validation_reports_exact_allowlist(tmp_path: Path) -> None: package, token = _signed_package(tmp_path) result = validate_pristine_package(package, token) assert result["hmac"] == "PASS" assert result["manifest_file_count"] == 2 assert result["package_file_count"] == 4 assert result["unexpected"] == 0 assert result["missing"] == 0