#!/usr/bin/env python3 """Create a sanitized BitBrowser database while preserving extensions and kernels.""" from __future__ import annotations import argparse import json import os import sqlite3 from pathlib import Path def quote_identifier(value: str) -> str: return '"' + value.replace('"', '""') + '"' def main() -> int: parser = argparse.ArgumentParser() parser.add_argument("--source", required=True) parser.add_argument("--output", required=True) args = parser.parse_args() source_path = Path(args.source).resolve() output_path = Path(args.output).resolve() if source_path == output_path: raise RuntimeError("The output database must differ from the source database") if not source_path.is_file(): raise FileNotFoundError(source_path) output_path.parent.mkdir(parents=True, exist_ok=True) if output_path.exists(): output_path.unlink() source = sqlite3.connect(f"file:{source_path.as_posix()}?mode=ro", uri=True) target = sqlite3.connect(str(output_path)) try: source.backup(target) target.execute("PRAGMA foreign_keys = OFF") tables = [ row[0] for row in target.execute( "SELECT name FROM sqlite_master " "WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name" ) ] extension_rows = [] if "local_extensions" in tables: extension_rows = target.execute( "SELECT name, desc, extension_id, icon, version, is_use " "FROM local_extensions ORDER BY id" ).fetchall() chrome_rows = [] if "chrome_versions" in tables: chrome_rows = target.execute( "SELECT chrome, version FROM chrome_versions ORDER BY id" ).fetchall() firefox_rows = [] if "firefox_versions" in tables: firefox_rows = target.execute( "SELECT firefox, version FROM firefox_versions ORDER BY id" ).fetchall() for table in tables: target.execute(f"DELETE FROM {quote_identifier(table)}") target.execute("DELETE FROM sqlite_sequence") if extension_rows: target.executemany( "INSERT INTO local_extensions " "(name, desc, extension_id, icon, version, is_use, cloud_id, main_user_id) " "VALUES (?, ?, ?, ?, ?, ?, NULL, NULL)", extension_rows, ) if chrome_rows: target.executemany( "INSERT INTO chrome_versions (chrome, version) VALUES (?, ?)", chrome_rows, ) if firefox_rows: target.executemany( "INSERT INTO firefox_versions (firefox, version) VALUES (?, ?)", firefox_rows, ) target.commit() target.execute("VACUUM") target.execute("PRAGMA optimize") counts = { table: target.execute( f"SELECT COUNT(*) FROM {quote_identifier(table)}" ).fetchone()[0] for table in tables } allowed_nonempty = {"local_extensions", "chrome_versions", "firefox_versions"} unexpected = { table: count for table, count in counts.items() if count and table not in allowed_nonempty } if unexpected: raise RuntimeError(f"Sanitization failed; unexpected rows remain: {unexpected}") integrity = target.execute("PRAGMA integrity_check").fetchone()[0] if integrity != "ok": raise RuntimeError(f"SQLite integrity check failed: {integrity}") report = { "source": str(source_path), "output": str(output_path), "extensions": [ { "name": row[0], "extension_id": row[2], "version": row[4], "enabled": bool(row[5]), } for row in extension_rows ], "chrome_versions": [ {"chrome": row[0], "version": row[1]} for row in chrome_rows ], "firefox_versions": [ {"firefox": row[0], "version": row[1]} for row in firefox_rows ], "table_counts": counts, "integrity_check": integrity, "size_bytes": os.path.getsize(output_path), } print(json.dumps(report, ensure_ascii=False, indent=2)) finally: target.close() source.close() return 0 if __name__ == "__main__": raise SystemExit(main())