utils/crdt-sync/tool/e2e_audit.py
Krzysztof kuhy Rudnicki 9102c4b6ff crdt-sync: tool/e2e_audit plaintext check
Decodes an RTDB dump and lists plaintext leaves; the E2E done check.
A grep for "title" misses escaped blobs.

Todo: fda71ed1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 09:54:00 +01:00

64 lines
2.0 KiB
Python

# Copyright (c) 2026 Krzysztof Rudnicki
"""Read-only E2E done check: list plaintext leaves in an RTDB JSON dump.
Feed it ``firebase database:get /<root>`` on stdin::
firebase database:get /todo-sync | python3 -m tool.e2e_audit todo-sync
Exit 0 when every leaf is sealed (or is the clear Argon2id header or a keyed
revision), 1 when any leaf is plaintext. A plain ``grep '"title"'`` misses
plaintext because RTDB serves each blob as an escaped JSON string; this
decodes the tree instead.
"""
from __future__ import annotations
import json
import re
import sys
from crdt_sync import is_e2e_envelope
_HEX64 = re.compile(r"[0-9a-f]{64}")
def _decode_key(key: str) -> str:
return re.sub(r"~([0-9A-F]{2})", lambda m: chr(int(m.group(1), 16)), key)
def _is_clear_leaf(value: str) -> bool:
# Envelopes and the Argon2id header both carry an integer "v"; keyed
# revisions are 64 hex chars; a bare E2eCipher blob is base64, not JSON.
if is_e2e_envelope(value) or _HEX64.fullmatch(value):
return True
return not value.lstrip().startswith(("{", "["))
def plaintext_paths(tree: object, prefix: str = "") -> list[str]:
"""Return the decoded paths of every plaintext leaf under ``tree``."""
found: list[str] = []
if isinstance(tree, dict):
for key, value in tree.items():
path = f"{prefix}/{_decode_key(key)}" if prefix else _decode_key(key)
found.extend(plaintext_paths(value, path))
elif isinstance(tree, str):
if not _is_clear_leaf(tree):
found.append(prefix)
elif tree is not None:
found.append(prefix)
return found
def main(argv: list[str]) -> int:
"""Audit the dump on stdin; ``argv[0]`` names the root for the report."""
root = argv[0] if argv else ""
paths = plaintext_paths(json.load(sys.stdin), root)
sys.stdout.write(f"plaintext leaves under /{root}: {len(paths)}\n")
for path in paths:
sys.stdout.write(f" {path}\n")
return 1 if paths else 0
if __name__ == "__main__":
raise SystemExit(main(sys.argv[1:]))