argand-site-registry/scripts/check_consumers.py

58 lines
2.8 KiB
Python

#!/usr/bin/env python3
"""Compare shipped native CLI, Rust library and Python example on authored fixtures."""
import argparse
import hashlib
import json
import subprocess
import sys
from pathlib import Path
def run(command):
result = subprocess.run(command, check=True, capture_output=True, text=True, timeout=60)
return json.loads(result.stdout)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--binary", type=Path, required=True)
parser.add_argument("--rust-example", type=Path, required=True)
parser.add_argument("--fixture", type=Path, required=True)
args = parser.parse_args()
example = Path(__file__).resolve().parents[1] / "examples" / "lookup.py"
generation = args.fixture / "candidate"
# This local test generated the fixture itself. Production pins come from
# an authenticated publisher, as explained in the consumer documentation.
pin = hashlib.sha256((generation / "COMPLETE.json").read_bytes()).hexdigest()
for query in ("FB", "Atlas", "Café Atlas", "$(echo unsafe); `id`"):
shared = ["--generation", str(generation), "--pin", pin, "--query", query]
cli = run([str(args.binary), "lookup", *shared, "--limit", "100"])
rust = run([str(args.rust_example), *shared])
python = run([sys.executable, str(example), "--binary", str(args.binary), *shared])
if cli != rust or cli != python:
raise ValueError(f"consumer output differs for {query!r}")
if not cli["attribution"]:
raise ValueError("consumer lost attribution")
if query == "FB":
candidate = cli["candidates"][0]
if candidate["canonical_name"] != "Facebook" or not candidate["provenance"]:
raise ValueError("name/provenance contract mismatch")
if candidate["web_property"]["domain"]["registrable_domain"] != "facebook.com":
raise ValueError("domain contract mismatch")
if query == "Atlas":
urls = {candidate["url"] for candidate in cli["candidates"]}
if urls != {"https://atlas.example.com/", "https://atlas.example.co.uk/",
"https://atlas.example.de/"}:
raise ValueError("regional properties were lost")
bad = ["--generation", str(generation), "--pin", "0" * 64, "--query", "FB"]
for command in ([str(args.binary), "lookup", *bad], [str(args.rust_example), *bad],
[sys.executable, str(example), "--binary", str(args.binary), *bad]):
result = subprocess.run(command, capture_output=True, timeout=60)
if result.returncode == 0:
raise ValueError("consumer accepted an untrusted generation")
print("Native CLI, Rust and Python consumer parity passed, including trust failures.")
if __name__ == "__main__":
main()