Files
ITWorx_MCP_Hub-Public/tests/test_release_scanner_asset.py
T

129 lines
3.8 KiB
Python

import hashlib
import io
import tarfile
import pytest
from scripts import build_mcp_hub_release_images as producer
def _fixture(
monkeypatch,
*,
declared_size=None,
duplicate=False,
kind=tarfile.REGTYPE,
machine=62,
bad_archive=False,
bad_binary=False,
member_size=None,
mode=0o755,
extra_stream=False,
):
header = bytearray(64)
header[:7] = b"\x7fELF\x02\x01\x01"
header[16:18] = (2).to_bytes(2, "little")
header[18:20] = machine.to_bytes(2, "little")
binary = bytes(header) + b"synthetic public scanner fixture"
downloaded = b"synthetic compressed archive"
monkeypatch.setattr(producer.platform, "system", lambda: "Linux")
monkeypatch.setattr(producer.platform, "machine", lambda: "x86_64")
monkeypatch.setattr(
producer,
"TRIVY_ARCHIVE_SHA256",
"0" * 64 if bad_archive else hashlib.sha256(downloaded).hexdigest(),
raising=False,
)
monkeypatch.setattr(
producer,
"TRIVY_BINARY_SHA256",
"0" * 64 if bad_binary else hashlib.sha256(binary).hexdigest(),
raising=False,
)
monkeypatch.setattr(
producer,
"TRIVY_BINARY_BYTES",
len(binary) if declared_size is None else declared_size,
raising=False,
)
class Opener:
def open(self, request, timeout):
assert request.full_url == producer.TRIVY_URL
return io.BytesIO(downloaded)
member = tarfile.TarInfo("trivy")
member.size = (
(len(binary) if declared_size is None else declared_size)
if member_size is None
else member_size
)
member.mode = mode
member.type = kind
class Bundle:
def __enter__(self):
return self
def __exit__(self, *_args):
return False
def getmembers(self):
return [member, member] if duplicate else [member]
def extractfile(self, _member):
return io.BytesIO(binary + b"unexpected" if extra_stream else binary)
monkeypatch.setattr(producer.urllib.request, "build_opener", lambda *_args: Opener())
monkeypatch.setattr(producer.tarfile, "open", lambda *_args: Bundle())
return binary
def test_verified_official_asset_contract_accepts_exact_binary(monkeypatch, tmp_path):
binary = _fixture(monkeypatch)
output = producer._download_trivy(tmp_path)
assert output.read_bytes() == binary
def test_actual_public_member_size_advances_to_strict_length_check(monkeypatch, tmp_path):
_fixture(monkeypatch, declared_size=166486178)
with pytest.raises(producer.BuildRejected, match="executable length differs"):
producer._download_trivy(tmp_path)
def test_compressed_download_bound_remains_independent_and_enforced(monkeypatch, tmp_path):
assert producer.MAX_TRIVY_ARCHIVE_BYTES == 128 * 1024**2
_fixture(monkeypatch)
monkeypatch.setattr(producer, "MAX_TRIVY_ARCHIVE_BYTES", 1)
with pytest.raises(producer.BuildRejected, match="download exceeded its size bound"):
producer._download_trivy(tmp_path)
@pytest.mark.parametrize(
"case",
[
"archive",
"binary",
"duplicate",
"symlink",
"architecture",
"size",
"mode",
"oversized_stream",
],
)
def test_scanner_trust_guards_remain_closed(monkeypatch, tmp_path, case):
_fixture(
monkeypatch,
bad_archive=case == "archive",
bad_binary=case == "binary",
duplicate=case == "duplicate",
kind=tarfile.SYMTYPE if case == "symlink" else tarfile.REGTYPE,
machine=183 if case == "architecture" else 62,
member_size=999 if case == "size" else None,
mode=0o4755 if case == "mode" else 0o755,
extra_stream=case == "oversized_stream",
)
with pytest.raises(producer.BuildRejected):
producer._download_trivy(tmp_path)