69 lines
3.0 KiB
Python
69 lines
3.0 KiB
Python
"""Bounded ephemeral write capabilities; validated numeric receipts go to stdout.
|
|
|
|
These are unverified browser diagnostics, never release-gate attestations.
|
|
No public read API, database, cookies, media, device identities or credentials.
|
|
"""
|
|
from collections import deque
|
|
import hashlib
|
|
import json
|
|
import secrets
|
|
import threading
|
|
import time
|
|
|
|
from validation import validate_report
|
|
|
|
|
|
class Refused(Exception):
|
|
def __init__(self, status):
|
|
self.status = status
|
|
|
|
|
|
class ReceiptStore:
|
|
def __init__(self, emit, clock=time.monotonic, wall=time.time):
|
|
self.emit, self.clock, self.wall = emit, clock, wall
|
|
self.runs, self.starts = {}, deque()
|
|
self.lock = threading.Lock()
|
|
|
|
def start(self):
|
|
with self.lock:
|
|
now = self.clock()
|
|
self.runs = {key: value for key, value in self.runs.items() if value["expires"] > now}
|
|
while self.starts and now-self.starts[0] >= 60:
|
|
self.starts.popleft()
|
|
if len(self.starts) >= 12 or len(self.runs) >= 64:
|
|
raise Refused(429)
|
|
run_id, token = secrets.token_hex(16), secrets.token_urlsafe(32)
|
|
self.runs[run_id] = {"token": hashlib.sha256(token.encode()).digest(), "expires": now+600,
|
|
"sequence": 0, "digest": None, "terminal": False, "last_progress": None}
|
|
self.starts.append(now)
|
|
self.log({"run_id": run_id, "sequence": 0, "state": "starting", "metrics": None})
|
|
return {"run_id": run_id, "write_token": token}
|
|
|
|
def report(self, body, token):
|
|
data = validate_report(body)
|
|
if type(token) is not str or len(token) != 43:
|
|
raise Refused(401)
|
|
with self.lock:
|
|
now = self.clock()
|
|
run = self.runs.get(data["run_id"])
|
|
if not run or run["expires"] <= now or not secrets.compare_digest(
|
|
run["token"], hashlib.sha256(token.encode()).digest()):
|
|
raise Refused(401)
|
|
digest = hashlib.sha256(json.dumps(data, sort_keys=True, allow_nan=False).encode()).digest()
|
|
if data["sequence"] == run["sequence"] and run["digest"] == digest:
|
|
return # Idempotent network retry, no duplicate log entry.
|
|
if run["terminal"] or data["sequence"] <= run["sequence"]:
|
|
raise Refused(409)
|
|
if data["state"] == "running" and run["last_progress"] is not None and now-run["last_progress"] < 8:
|
|
raise Refused(429)
|
|
self.log(data)
|
|
run.update(sequence=data["sequence"], digest=digest, terminal=data["state"] != "running")
|
|
if data["state"] == "running":
|
|
run["last_progress"] = now
|
|
else:
|
|
run["expires"] = now+60 # Retain briefly for idempotent final retry.
|
|
|
|
def log(self, data):
|
|
self.emit({"schema": "av-test.receipt.v1", "kind": "unverified-browser-diagnostic",
|
|
"received_unix_ms": int(self.wall()*1000), "release_eligible": False, **data})
|