#!/usr/bin/env python3 """Validate an unsigned LinkLog XPI produced by the Makefile.""" import json import sys import zipfile from pathlib import Path REQUIRED_FILES = { 'manifest.json', 'logo.svg', 'icon-16.png', 'icon-32.png', 'icon-48.png', 'icon-96.png', 'options.css', 'options.html', 'options.js', 'popup.css', 'popup.html', 'popup.js', } def fail(message: str) -> None: raise SystemExit(f'XPI validation failed: {message}') def main() -> None: if len(sys.argv) != 3: fail('usage: validate_xpi.py ') xpi_path = Path(sys.argv[1]) source_manifest_path = Path(sys.argv[2]) try: source_manifest = json.loads(source_manifest_path.read_text()) with zipfile.ZipFile(xpi_path) as archive: names = set(archive.namelist()) corrupt_member = archive.testzip() if corrupt_member is not None: fail(f'corrupt archive member: {corrupt_member}') if 'manifest.json' not in names: fail('manifest.json is missing') packaged_manifest = json.loads(archive.read('manifest.json')) except (OSError, zipfile.BadZipFile, json.JSONDecodeError) as error: fail(str(error)) missing_files = REQUIRED_FILES - names if missing_files: fail(f'missing required files: {", ".join(sorted(missing_files))}') unexpected_files = names - REQUIRED_FILES if any(name.startswith('__MACOSX/') or name == '.DS_Store' for name in unexpected_files): fail('archive contains macOS metadata') if packaged_manifest != source_manifest: fail('packaged manifest does not match webextension/manifest.json') print(f'validated unsigned XPI {xpi_path}') if __name__ == '__main__': main()