62 lines
1.8 KiB
Python
62 lines
1.8 KiB
Python
#!/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> <source-manifest>')
|
|
|
|
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() |