Implement AUDIT-WP-0007 hash-chain integrity.

Accept now extends a single-schema chain. Verify walks it; a rewritten
payload_hash is a break. Tamper evidence is that detector plus an
external chain-head attestation, not WORM.
This commit is contained in:
tegwick 2026-08-16 01:18:30 +02:00
parent 5faede18fc
commit 5fd04e2095
17 changed files with 696 additions and 29 deletions

View file

@ -201,11 +201,57 @@ def test_health_passes_on_a_live_backend(backend):
backend.health()
def test_chain_links_and_verify_is_clean(backend):
from audit_core.integrity import GENESIS
first = make_event("chain-1")
second = make_event("chain-2")
backend.accept(first, digest(first))
backend.accept(second, digest(second))
report = backend.verify_chain()
assert report.intact is True
assert report.events >= 2
assert report.first_break is None
assert report.head != GENESIS
replay = backend.accept(first, digest(first))
assert replay.duplicate is True
assert backend.verify_chain().events == report.events
# --- postgres-specific guarantees -------------------------------------------
pg_only = pytest.mark.skipif(not HAVE_PG, reason="needs PostgreSQL")
@pg_only
def test_rewritten_payload_fails_verify_postgres():
"""Superuser rewrite is the evidence the trigger never gave us."""
from audit_core.postgres_backend import PostgresAuditBackend
schema = f"conf_{uuid.uuid4().hex[:12]}"
backend = PostgresAuditBackend(PG_URL, schema=schema)
try:
event = make_event("break-1")
backend.accept(event, digest(event))
other = make_event("break-2")
backend.accept(other, digest(other))
assert backend.verify_chain().intact is True
with backend.pool.connection() as conn:
conn.execute(f'ALTER TABLE "{schema}".events DISABLE TRIGGER events_append_only')
conn.execute(
f'UPDATE "{schema}".events SET payload_hash = %s WHERE event_id = %s',
("deadbeef" * 8, "break-2"),
)
conn.execute(f'ALTER TABLE "{schema}".events ENABLE TRIGGER events_append_only')
report = backend.verify_chain()
assert report.intact is False
assert report.first_break == "break-2"
finally:
with backend.pool.connection() as conn:
conn.execute(f'DROP SCHEMA IF EXISTS "{schema}" CASCADE')
backend.close()
@pg_only
def test_replay_reconciles_rather_than_duplicating():
"""Replay must never mint a second custody record for one source event."""