Files
archy/tests/apps/qortal/make-cache-fixture.py

52 lines
2.5 KiB
Python

#!/usr/bin/env python3
"""Create an UNPUBLISHED one-class Qortal fixture JAR; never edit original data."""
import argparse
import hashlib
import io
import json
from pathlib import Path
import zipfile
PIN = 'a5fbe763645a53995565c9cb93491e024384431ee29eee38a4146b6a13ed2e87'
CLASS = 'org/qortal/controller/arbitrary/ArbitraryDataCacheManager.class'
MAX_CLASS_BYTES = 1024 * 1024
def sha(data):
return hashlib.sha256(data).hexdigest()
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('original', type=Path)
parser.add_argument('compiled_class', type=Path)
parser.add_argument('output', type=Path)
args = parser.parse_args()
if args.original.is_symlink() or args.compiled_class.is_symlink():
raise ValueError('Symlink inputs refused')
original = args.original.read_bytes()
if sha(original) != PIN:
raise ValueError('Original is not the qualified official JAR')
with args.compiled_class.open('rb') as stream:
replacement = stream.read(MAX_CLASS_BYTES + 1)
if replacement[:4] != b'\xca\xfe\xba\xbe' or len(replacement) > MAX_CLASS_BYTES:
raise ValueError('Invalid or excessive class input')
# Build from the bytes whose pin was checked, never a reopened input path.
with zipfile.ZipFile(io.BytesIO(original)) as src:
names = src.namelist()
if len(set(names)) != len(names) or names.count(CLASS) != 1:
raise ValueError('Ambiguous original entries')
if any(n.upper().startswith('META-INF/') and n.upper().endswith(('.SF', '.RSA', '.DSA', '.EC')) for n in names):
raise ValueError('Refuse to invalidate an existing JAR signature')
with args.output.open('xb') as file, zipfile.ZipFile(file, 'w') as dst:
for item in src.infolist():
dst.writestr(item, replacement if item.filename == CLASS else src.read(item.filename))
with zipfile.ZipFile(io.BytesIO(original)) as src, zipfile.ZipFile(args.output) as dst:
changed = [name for name in names if src.read(name) != dst.read(name)]
if changed != [CLASS]:
raise ValueError('Overlay did not change exactly the expected class')
print(json.dumps({'status':'unpublished synthetic fixture, not released source or installed acceptance',
'original_sha256': PIN, 'class_sha256': sha(replacement),
'fixture_sha256': sha(args.output.read_bytes()), 'changed_entries': changed}, indent=2))
if __name__ == '__main__':
main()