Files
2026-05-05 21:22:33 +02:00

85 lines
3.5 KiB
Python

from __future__ import annotations
import os
import stat
import sys
from pathlib import Path
import pytest
PACKAGING_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(PACKAGING_ROOT))
import validate_release # noqa: E402
def _write_file(path: Path, content: str = "x", *, executable: bool = False) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(content, encoding="utf-8")
if executable and os.name != "nt":
path.chmod(path.stat().st_mode | stat.S_IXUSR)
def _seed_base_release(root: Path) -> None:
for filename in validate_release.EXPECTED_TEAMSERVER_FILES:
_write_file(root / "TeamServer" / filename, executable=filename == "TeamServer")
(root / "TeamServer" / "logs").mkdir(parents=True)
for filename in validate_release.EXPECTED_TEAMSERVER_MODULES:
_write_file(root / "TeamServerModules" / filename)
for filename in validate_release.EXPECTED_COMMAND_SPECS_COMMON:
_write_file(root / "CommandSpecs" / "common" / filename, "{}")
for filename in validate_release.EXPECTED_COMMAND_SPECS_MODULES:
_write_file(root / "CommandSpecs" / "modules" / filename, "{}")
_write_file(root / "Client" / "README.md")
_write_file(root / "Client" / "pyproject.toml")
_write_file(root / "Client" / "requirements.txt")
_write_file(root / "Client" / "run-client.sh", executable=True)
_write_file(root / "Client" / "run-client.ps1")
_write_file(root / "Client" / "c2client_protocol" / "__init__.py")
_write_file(root / "Client" / "c2client_protocol" / "TeamServerApi_pb2.py")
_write_file(root / "Client" / "c2client_protocol" / "TeamServerApi_pb2_grpc.py")
def test_validate_base_release_requires_command_specs(tmp_path):
release_root = tmp_path / "Release"
_seed_base_release(release_root)
validate_release.validate_base_release(release_root)
(release_root / "CommandSpecs" / "modules" / "taskScheduler.json").unlink()
with pytest.raises(validate_release.ValidationError, match="taskScheduler.json"):
validate_release.validate_base_release(release_root)
def test_validate_base_release_rejects_runtime_data_roots(tmp_path):
release_root = tmp_path / "Release"
_seed_base_release(release_root)
(release_root / "data" / "Tools").mkdir(parents=True)
with pytest.raises(validate_release.ValidationError, match="runtime/operator data"):
validate_release.validate_base_release(release_root)
def test_validate_implants_requires_linux_arch_layout(tmp_path):
release_root = tmp_path / "Release"
for arch in validate_release.EXPECTED_WINDOWS_ARCHES:
for filename in validate_release.EXPECTED_WINDOWS_BEACONS:
_write_file(release_root / "WindowsBeacons" / arch / filename)
for filename in validate_release.EXPECTED_WINDOWS_MODULES:
_write_file(release_root / "WindowsModules" / arch / filename)
for arch in validate_release.EXPECTED_LINUX_ARCHES:
for filename in validate_release.EXPECTED_LINUX_BEACONS:
_write_file(release_root / "LinuxBeacons" / arch / filename)
for filename in validate_release.EXPECTED_LINUX_MODULES:
_write_file(release_root / "LinuxModules" / arch / filename)
validate_release.validate_implants(release_root)
flat_beacon = release_root / "LinuxBeacons" / "BeaconHttp"
_write_file(flat_beacon)
with pytest.raises(validate_release.ValidationError, match="unexpected file"):
validate_release.validate_implants(release_root)