58 lines
3.1 KiB
Python
58 lines
3.1 KiB
Python
"""Actual inspector rejects missing serialized clips and changed PCM in QA-only APK copies."""
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
import subprocess
|
|
import sys
|
|
import zipfile
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('--apk', required=True)
|
|
parser.add_argument('--project', required=True)
|
|
parser.add_argument('--evidence', required=True)
|
|
args = parser.parse_args()
|
|
apk, evidence = Path(args.apk), Path(args.evidence)
|
|
evidence.mkdir(parents=True, exist_ok=True)
|
|
inspector = Path(__file__).with_name('inspect-voice-payload.py')
|
|
checks = []
|
|
for kind in ('positive', 'missing-clips', 'altered-pcm'):
|
|
case = evidence / kind
|
|
case.mkdir(exist_ok=True)
|
|
target = apk if kind == 'positive' else case / 'qa-mutated.apk'
|
|
mutation = None
|
|
if kind != 'positive':
|
|
with zipfile.ZipFile(apk) as source, zipfile.ZipFile(target, 'w') as output:
|
|
for entry in source.infolist():
|
|
if not entry.filename.startswith('assets/bin/Data/'):
|
|
continue
|
|
if kind == 'missing-clips' and entry.filename.startswith('assets/bin/Data/sharedassets0.assets'):
|
|
continue
|
|
data = source.read(entry)
|
|
if kind == 'altered-pcm' and entry.filename in ('assets/bin/Data/sharedassets0.resource.split0', 'assets/bin/Data/sharedassets0.resource'):
|
|
data = bytearray(data)
|
|
offset = 194000 # Inside first known PCM16 AudioClip resource, outside its FSB header.
|
|
data[offset] ^= 32
|
|
data = bytes(data)
|
|
mutation = {'entry': entry.filename, 'offset': offset, 'xor': 32}
|
|
output.writestr(entry, data)
|
|
if kind == 'altered-pcm' and mutation is None:
|
|
raise RuntimeError('Expected actual PCM split resource is absent')
|
|
command = [sys.executable, '-I', str(inspector), '--apk', str(target), '--project', args.project, '--evidence', str(case)]
|
|
process = subprocess.run(command, capture_output=True, text=True)
|
|
(case / 'stdout.log').write_text(process.stdout, encoding='utf-8')
|
|
(case / 'stderr.log').write_text(process.stderr, encoding='utf-8')
|
|
record = json.loads((case / 'apk-voice-payload.json').read_text(encoding='utf-8'))
|
|
expected = kind == 'positive'
|
|
passed = process.returncode == (0 if expected else 1) and (record['status'] == 'passed') == expected
|
|
if kind == 'missing-clips':
|
|
passed = passed and record.get('actualClipCount') == 0
|
|
if kind == 'altered-pcm':
|
|
passed = passed and any(not check['passed'] for check in record['checks']) and 'PCM mismatch' in record.get('error', '')
|
|
checks.append({'case': kind, 'command': command, 'exitCode': process.returncode, 'passed': passed,
|
|
'mutation': mutation, 'inspectorRecord': record})
|
|
result = {'originalApkSha256': hashlib.sha256(apk.read_bytes()).hexdigest(), 'scope': 'QA synthetic negative copies only; never installed',
|
|
'checks': checks, 'passed': all(check['passed'] for check in checks)}
|
|
(evidence / 'regression.json').write_text(json.dumps(result, indent=2), encoding='utf-8')
|
|
sys.exit(0 if result['passed'] else 1)
|