audit-core/tests/test_cadence_registration.py

99 lines
5.6 KiB
Python
Raw Normal View History

"""Guarded registration never changes custody or unrelated registry state."""
import copy
import hashlib
import json
from pathlib import Path
import pytest
from audit_core.cadence_registration import prepare, strict_json
def fixture():
frame = {"frame_seconds": 3600}
entries = [{"source_id": n, "event_class": n, "form": "heartbeat-or-reconciliation",
"heartbeat": {"event_class": "audit-core.heartbeat", "interval_seconds": 3600,
"assertion": "nothing-to-report", "missing": "finding",
"extensions": {"approval-engine": {"asserted_class": n,
"requested_observer_max_gap_seconds": 7200,
"effective_deadline_seconds": 10800}}}}
for n in ("issuance", "use", "supersession", "revocation")]
entries.append({"source_id": "heartbeat", "event_class": "heartbeat",
"form": "heartbeat-or-reconciliation", "reconciliation": {
"compare_local": "local", "compare_observed": "receiver", "divergence": "finding"}})
doc = {"schema_version": "0.1", "declaration_id": "test", "source": "approval-engine",
"sources": entries, "extensions": {"netkingdom": {"coordination_frame": frame}}}
raw = json.dumps(doc).encode()
proposal = {"sender": "approval-engine", "source": "approval-engine", "tenant": "tenant:platform",
"may_read": False, "preserve_existing_credentials_and_other_registry_fields": True,
"coordination_frame": frame, "emission_cadence_sha256": hashlib.sha256(raw).hexdigest(),
"heartbeat_classes": dict.fromkeys(("issuance", "use", "supersession", "revocation"),7200),
"receiver_grace_factor": 1.5, "effective_deadline_seconds": 10800}
rows = [{"name": "sibling", "sources": ["sibling"], "tokens": ["sibling-fixture"]},
{"name": "approval-engine", "sources": ["approval-engine"], "tenants": ["tenant:platform"],
"tokens": ["current-fixture", "overlap-fixture"], "may_read": False,
"evidence_kind": "load-bearing", "secret_policy": "redact", "owner_property": {"keep": True}}]
return {"senders.json": json.dumps(rows), "unrelated": "preserve"}, raw, proposal
def test_preserves_every_non_metadata_field_and_inputs_and_is_idempotent():
body, raw, proposal = fixture()
original = copy.deepcopy(body)
updated, receipt = prepare(body, raw, proposal)
before, after = json.loads(body["senders.json"]), json.loads(updated["senders.json"])
assert body == original and updated["unrelated"] == "preserve" and before[0] == after[0]
assert {k:v for k,v in after[1].items() if k not in ("heartbeat_classes", "emission_cadence")} == before[1]
assert "fixture" not in json.dumps(receipt)
assert prepare(updated, raw, proposal)[0] == updated
assert prepare(updated, raw, proposal)[1]["changed"] is False
@pytest.mark.parametrize("mutation", ["digest", "read", "tenant", "class", "credentials", "duplicate", "absent"])
def test_fail_closed(mutation):
body, raw, proposal = fixture()
rows = json.loads(body["senders.json"])
if mutation == "digest": proposal["emission_cadence_sha256"] = "bad"
if mutation == "read": rows[1]["may_read"] = True
if mutation == "tenant": rows[1]["tenants"] = ["*"]
if mutation == "class": rows[1]["heartbeat_classes"] = {"must-not-drop": 3600}
if mutation == "credentials": rows[1]["tokens"] = []
if mutation == "duplicate": rows.append(copy.deepcopy(rows[1]))
if mutation == "absent": rows.pop()
body["senders.json"] = json.dumps(rows)
with pytest.raises(ValueError): prepare(body, raw, proposal)
def test_duplicate_json_refused():
with pytest.raises(ValueError): strict_json('{"name":1,"name":2}')
def test_real_owner_proposal_when_checkout_available():
root = Path(__file__).resolve().parents[2]
proposal = root / "net-kingdom/generated/coordination-frame/approval-engine-receiver.json"
declaration = root / "approval-engine/cadence.yaml"
if not proposal.exists() or not declaration.exists(): pytest.skip("owner checkouts unavailable")
body, _, _ = fixture()
_, receipt = prepare(body, declaration.read_bytes(), strict_json(proposal.read_bytes()))
assert receipt["effective_deadline_seconds"] == 10800
def test_qonto_proposal_retains_mixed_classification():
from audit_core.cadence_registration import qonto_metadata
body, raw, proposal = fixture()
doc = strict_json(raw)
doc["source"] = "qonto-assistant"
doc["stream_id"] = "qonto-assistant.audit"
kinds = {"audit.allow": "attributive", "audit.deny": "load-bearing", "audit.heartbeat": "load-bearing"}
doc["sources"] = [{"source_id": name, "event_class": name, "form": "heartbeat-or-reconciliation",
"extensions": {"netkingdom": {"evidence_class": kind}},
"heartbeat": {"event_class": "audit.heartbeat", "interval": "PT3600S",
"assertion": "nothing-to-report", "missing": "finding",
"extensions": {"qonto-assistant": {"assertion_scope": "no-deny-transitions-in-window"}}}}
for name, kind in kinds.items()]
entry = qonto_metadata(doc, "a" * 64)
assert "tokens" not in entry and entry["may_read"] is False
assert entry["heartbeat_classes"] == {"audit.deny": 7200}
assert entry["emission_cadence"] == doc
doc["sources"][0]["extensions"]["netkingdom"]["evidence_class"] = "load-bearing"
with pytest.raises(ValueError, match="classification"): qonto_metadata(doc, "a" * 64)