fix(automation): domain-separate backup fingerprints

This commit is contained in:
ogt
2026-07-14 11:00:20 +08:00
parent a5030e9fe0
commit 745b86af01
3 changed files with 200 additions and 26 deletions

View File

@@ -147,35 +147,57 @@ async def test_backfill_run_inserts_satisfy_no_default_cost_column() -> None:
@pytest.mark.asyncio
async def test_long_occurrence_fingerprint_fits_real_pg_varchar() -> None:
async def test_domain_separated_fingerprints_avoid_cross_domain_pg_collision() -> None:
engine = create_async_engine(_explicit_local_test_database_url(), echo=False)
source_fingerprint = "legacy-backup-occurrence:" + ("a" * 64)
stored_fingerprint = (
deliberate_raw_fingerprint = backfill._sha256(source_fingerprint)
stored_long_fingerprint = (
backup_restore_alertmanager_ingress._approval_storage_fingerprint(
source_fingerprint
)
)
stored_raw_fingerprint = (
backup_restore_alertmanager_ingress._approval_storage_fingerprint(
deliberate_raw_fingerprint
)
)
assert stored_long_fingerprint != stored_raw_fingerprint
try:
async with engine.connect() as connection:
transaction = await connection.begin()
try:
await connection.execute(text("""
CREATE TEMP TABLE approval_records (
fingerprint VARCHAR(64) NOT NULL
source_identity TEXT NOT NULL,
fingerprint VARCHAR(64) NOT NULL UNIQUE
) ON COMMIT DROP
"""))
await connection.execute(
text("""
INSERT INTO approval_records (fingerprint)
VALUES (:fingerprint)
INSERT INTO approval_records (
source_identity, fingerprint
) VALUES
('long', :stored_long_fingerprint),
('deliberate_raw', :stored_raw_fingerprint)
"""),
{"fingerprint": stored_fingerprint},
{
"stored_long_fingerprint": stored_long_fingerprint,
"stored_raw_fingerprint": stored_raw_fingerprint,
},
)
persisted = await connection.scalar(
text("SELECT fingerprint FROM approval_records")
)
assert persisted == stored_fingerprint
assert len(persisted) == 64
persisted = (
await connection.execute(text("""
SELECT source_identity, fingerprint
FROM approval_records
ORDER BY source_identity
"""))
).mappings().all()
assert len(persisted) == 2
assert {row["fingerprint"] for row in persisted} == {
stored_long_fingerprint,
stored_raw_fingerprint,
}
assert all(len(row["fingerprint"]) == 64 for row in persisted)
finally:
await transaction.rollback()
finally:

View File

@@ -85,15 +85,85 @@ def test_backup_ingress_owner_is_stable_per_source_occurrence() -> None:
assert first["read_only"] is True
def test_backup_approval_fingerprint_fits_durable_varchar_boundary() -> None:
def test_backup_approval_fingerprint_is_domain_separated_and_bounded() -> None:
short = "source-fingerprint"
long = "legacy-backup-occurrence:" + ("a" * 64)
legacy_long_digest = hashlib.sha256(long.encode("utf-8")).hexdigest()
assert ingress._approval_storage_fingerprint(short) == short
stored = ingress._approval_storage_fingerprint(long)
assert stored == hashlib.sha256(long.encode("utf-8")).hexdigest()
assert len(stored) == ingress.APPROVAL_FINGERPRINT_MAX_LENGTH
assert ingress._approval_storage_fingerprint(long) == stored
stored_short = ingress._approval_storage_fingerprint(short)
stored_long = ingress._approval_storage_fingerprint(long)
stored_deliberate_raw = ingress._approval_storage_fingerprint(
legacy_long_digest
)
assert stored_short != short
assert stored_long != legacy_long_digest
assert stored_long != stored_deliberate_raw
assert len(stored_short) == ingress.APPROVAL_FINGERPRINT_MAX_LENGTH
assert len(stored_long) == ingress.APPROVAL_FINGERPRINT_MAX_LENGTH
assert ingress._approval_storage_fingerprint(long) == stored_long
assert ingress._approval_fingerprint_lookup_candidates(short) == (
stored_short,
short,
)
assert ingress._approval_fingerprint_lookup_candidates(long) == (
stored_long,
legacy_long_digest,
)
@pytest.mark.asyncio
async def test_backup_approval_legacy_lookup_rejects_deliberate_collision() -> None:
long = "legacy-backup-occurrence:" + ("a" * 64)
deliberate_raw = hashlib.sha256(long.encode("utf-8")).hexdigest()
canonical, legacy = ingress._approval_fingerprint_lookup_candidates(long)
assert legacy == deliberate_raw
wrong_source = SimpleNamespace(
id=UUID("11111111-1111-4111-8111-111111111111"),
incident_id="INC-WRONG-SOURCE",
metadata={"source_fingerprint": deliberate_raw},
)
calls: list[str] = []
async def find_by_fingerprint(*, fingerprint: str, debounce_minutes: int):
calls.append(fingerprint)
assert debounce_minutes == 30
return wrong_source if fingerprint == legacy else None
result = await ingress._find_compatible_source_approval(
SimpleNamespace(find_by_fingerprint=find_by_fingerprint),
source_fingerprint=long,
)
assert result is None
assert calls == [canonical, legacy]
@pytest.mark.asyncio
async def test_backup_approval_legacy_89_char_lookup_remains_compatible() -> None:
source = "legacy-backup-occurrence:" + ("a" * 64)
canonical, legacy = ingress._approval_fingerprint_lookup_candidates(source)
existing = SimpleNamespace(
id=UUID("22222222-2222-4222-8222-222222222222"),
incident_id="INC-LEGACY-89-CHAR",
metadata={"source_fingerprint": source},
)
calls: list[str] = []
async def find_by_fingerprint(*, fingerprint: str, debounce_minutes: int):
calls.append(fingerprint)
assert debounce_minutes == 30
return existing if fingerprint == legacy else None
result = await ingress._find_compatible_source_approval(
SimpleNamespace(find_by_fingerprint=find_by_fingerprint),
source_fingerprint=source,
)
assert result is existing
assert result.incident_id == "INC-LEGACY-89-CHAR"
assert calls == [canonical, legacy]
@pytest.mark.asyncio
@@ -179,10 +249,10 @@ class _FakeApprovalService:
async def find_by_fingerprint(self, **_kwargs):
self.calls.append("legacy_owner_read")
expected = ingress._approval_storage_fingerprint(
expected = ingress._approval_fingerprint_lookup_candidates(
str(_signal_kwargs()["source_fingerprint"])
)
assert _kwargs["fingerprint"] == expected
assert _kwargs["fingerprint"] in expected
return None
async def create_approval_with_fingerprint(self, *, request, fingerprint):
@@ -280,6 +350,14 @@ async def test_backup_ingress_binds_canonical_incident_then_dispatches_and_reads
**_signal_kwargs()
)
assert approval_service.approval is not None
approval_service.approval.fingerprint = hashlib.sha256(
str(_signal_kwargs()["source_fingerprint"]).encode("utf-8")
).hexdigest()
replay = await ingress.process_backup_restore_alertmanager_signal(
**_signal_kwargs()
)
assert calls.index("owner_create") < calls.index("incident_create")
assert calls.index("incident_create") < calls.index("incident_bind")
assert calls.index("incident_bind") < calls.index("incident_canonical_readback")
@@ -297,6 +375,13 @@ async def test_backup_ingress_binds_canonical_incident_then_dispatches_and_reads
assert result["runtime_execution_authorized"] is False
assert result["runtime_closure_verified"] is False
assert result["production_write_performed"] is False
assert replay["owner_created"] is False
assert calls.count("owner_create") == 1
assert calls.count("incident_create") == 1
assert calls.count("owner_recurrence") == 1
assert approval_service.approval.fingerprint == hashlib.sha256(
str(_signal_kwargs()["source_fingerprint"]).encode("utf-8")
).hexdigest()
assert approval_service.created_request.risk_level.value == "low"
assert approval_service.created_request.metadata["telegram_role"] == (
"receipt_projection_only"