From 9171774ae0eda7ca2aad149bddf6a6f42819b282 Mon Sep 17 00:00:00 2001 From: JohnBraham Date: Fri, 30 Oct 2026 11:26:27 +0000 Subject: [PATCH] Verify generated native SDK archive contents --- tools/migration_ci.py | 4 +-- tools/native117_generated_sdk.json | 14 +++++++++ tools/native_runtime.py | 31 ++++++++++++++++---- tools/native_sdk_contents.py | 37 ++++++++++++++++++++++++ tools/test_native_sdk_contents.py | 46 ++++++++++++++++++++++++++++++ 5 files changed, 124 insertions(+), 8 deletions(-) create mode 100644 tools/native117_generated_sdk.json create mode 100644 tools/native_sdk_contents.py create mode 100644 tools/test_native_sdk_contents.py diff --git a/tools/migration_ci.py b/tools/migration_ci.py index 043889d..464cc22 100644 --- a/tools/migration_ci.py +++ b/tools/migration_ci.py @@ -19,9 +19,9 @@ def digest(path): def native_input_paths(project, expected=None): - from native_runtime import SERVER_INPUTS + from native_runtime import verify_server_inputs native=project/'build/native-server-distribution' - expected=SERVER_INPUTS if expected is None else expected + expected=verify_server_inputs(native) if expected is None else expected if not expected:raise ValueError('Missing native SDK manifest') paths=[] for name,sha in sorted(expected.items()): diff --git a/tools/native117_generated_sdk.json b/tools/native117_generated_sdk.json new file mode 100644 index 0000000..c9a3b0c --- /dev/null +++ b/tools/native117_generated_sdk.json @@ -0,0 +1,14 @@ +{ + "libraries/net/minecraft/server/1.17.1-20210706.113038/server-1.17.1-20210706.113038-srg.jar": { + "entries": 4677, + "content_and_metadata_sha256": "38885B270B4072A6F4C61764FEF61C2839C00505E58C7A193F5C896EB3834F7D", + "timestamp_policy": "installer-times", + "canonical_archive_sha256": "A00E5004F190A0F221D8001B2E04609CE744A129F7764181AD10FBEFB1237EAA" + }, + "libraries/net/minecraftforge/forge/1.17.1-37.1.1/forge-1.17.1-37.1.1-server.jar": { + "entries": 1252, + "content_and_metadata_sha256": "AF6A206D4398A373937D85C240CD16ACC2833A9A8493F483FA31E95870DFF3F0", + "timestamp_policy": "exact", + "canonical_archive_sha256": "35DB91BCD011CAD8B812BA165CD7F5AD934CEEEE9050621423F9E5D227D7F895" + } +} diff --git a/tools/native_runtime.py b/tools/native_runtime.py index b463df5..c827cba 100644 --- a/tools/native_runtime.py +++ b/tools/native_runtime.py @@ -39,6 +39,29 @@ def fingerprint(directory): for p in sorted(directory.rglob('*')) if p.is_file()} +def verify_server_inputs(distribution): + """Pin raw inputs and seal both installer-generated archive contents. + + Only installer directory timestamps and ZIP entry order may vary. The + returned hashes bind the actual SDK bytes prepared for every process. + """ + from native_sdk_contents import verify_generated_archive + generated=json.loads(Path(__file__).with_name('native117_generated_sdk.json').read_text()) + actual={} + for name,expected in SERVER_INPUTS.items(): + artifact=Path(distribution)/name + if artifact.is_symlink() or not artifact.is_file():raise FileNotFoundError('Missing native37 SDK input: '+name) + value=digest(artifact) + if value!=expected: + seal=generated.get(name) + if not seal or seal['canonical_archive_sha256']!=expected: + raise RuntimeError('Incorrect official Forge37 server input: '+name) + verify_generated_archive(artifact,seal) + if digest(artifact)!=value:raise RuntimeError('Native SDK changed during verification: '+name) + actual[name]=value + return actual + + def seed_loader_defaults(home, deterministic=False, include_default_path=True): """Seed an owned home; disable optional HTTP checks only for probes.""" config=home/'config/fml.toml' @@ -57,10 +80,7 @@ def prepare_server(home, distribution, candidate=None, probe=None): libraries = distribution / 'libraries' if not libraries.is_dir(): raise FileNotFoundError('Incomplete official Forge 37 server installation') - for name, expected in SERVER_INPUTS.items(): - artifact = distribution / name - if not artifact.is_file() or digest(artifact) != expected: - raise RuntimeError('Incorrect official Forge 37 server input: ' + name) + verify_server_inputs(distribution) home.mkdir(parents=True, exist_ok=False) (home / 'mods').mkdir() seed_loader_defaults(home, deterministic=probe is not None) @@ -279,8 +299,7 @@ def install(java, home, client=False): from native117_client import prepare inputs=prepare(home,home/'prepared-inputs')['files'] else: - inputs={name:digest(home/name) for name in SERVER_INPUTS} - if inputs!=SERVER_INPUTS:raise RuntimeError('Official Forge37 server installation checksum mismatch') + inputs=verify_server_inputs(home) (home/'runtime-inputs.json').write_text(json.dumps({'installer_sha256':INSTALLER_SHA256,'client':client,'files':inputs},indent=2)+'\n') return home diff --git a/tools/native_sdk_contents.py b/tools/native_sdk_contents.py new file mode 100644 index 0000000..ea0f2c7 --- /dev/null +++ b/tools/native_sdk_contents.py @@ -0,0 +1,37 @@ +"""Verify generated native SDK archives despite installer container metadata.""" +import hashlib,json,struct,zipfile +from pathlib import PurePosixPath + +def timestamp_extra(extra): + # Java's directory-backed installer emits NTFS times on Windows and + # extended Unix times on Linux. No other extra field is admitted here. + if len(extra)<4:raise ValueError('Missing installer timestamp metadata') + kind,size=struct.unpack('