From faa53707b99daa62352d689d8cea22276c1cef1f Mon Sep 17 00:00:00 2001 From: Public Source Publisher Date: Wed, 14 Oct 2026 07:54:12 +0000 Subject: [PATCH] Publish curated ITWorx_MCP_Hub source from 535009426b1f754478673ad9789b16e2c047a32e --- PUBLIC_SOURCE_EXPORT.md | 2 +- PUBLIC_SOURCE_MANIFEST.json | 7 +- tests/test_release_scanner_asset.py | 128 ++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 tests/test_release_scanner_asset.py diff --git a/PUBLIC_SOURCE_EXPORT.md b/PUBLIC_SOURCE_EXPORT.md index d0b8ee7..7904ad4 100644 --- a/PUBLIC_SOURCE_EXPORT.md +++ b/PUBLIC_SOURCE_EXPORT.md @@ -1,3 +1,3 @@ # Public source export -This source snapshot was generated from the private canonical repository at revision `55011066f84f073ceabb35c99ddd5b65745e34f6`. It contains no private Git history or operational evidence. Changes are published from the canonical repository. +This source snapshot was generated from the private canonical repository at revision `535009426b1f754478673ad9789b16e2c047a32e`. It contains no private Git history or operational evidence. Changes are published from the canonical repository. diff --git a/PUBLIC_SOURCE_MANIFEST.json b/PUBLIC_SOURCE_MANIFEST.json index 5003d6f..3990f25 100644 --- a/PUBLIC_SOURCE_MANIFEST.json +++ b/PUBLIC_SOURCE_MANIFEST.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "sourceRevision": "55011066f84f073ceabb35c99ddd5b65745e34f6", + "sourceRevision": "535009426b1f754478673ad9789b16e2c047a32e", "files": [ { "path": ".dockerignore", @@ -3457,6 +3457,11 @@ "sha256": "055e9adb93816464381e4de32b0c11e86d54555606a17041ba1ee52f36d73487", "bytes": 3063 }, + { + "path": "tests/test_release_scanner_asset.py", + "sha256": "3a3a6674c9e3146827cc34aa493e0445505067b6e90d5618c3552c7dc2861118", + "bytes": 3902 + }, { "path": "tests/test_stitch_connector.py", "sha256": "c34cd2dc20721052dc63a15dc763ecc9b4f1adaef1d6ec2d5189adff9161c269", diff --git a/tests/test_release_scanner_asset.py b/tests/test_release_scanner_asset.py new file mode 100644 index 0000000..f650313 --- /dev/null +++ b/tests/test_release_scanner_asset.py @@ -0,0 +1,128 @@ +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)