e247dbc1be
- host_escrow_superseded table + SaveHostEscrow retains a different-sha old blob before overwrite (tx); same-sha idempotent (no supersede row); returns superseded bool. ACK/restore read the current row unchanged. CountSuperseded/ListSuperseded; DeleteHost drops retained rows. - escrow_superseded audit event + operator retained-count on host detail; register offbox_repo_orphaned/reset. Red-proof TestSaveHostEscrow_RetainsSuperseded.
59 lines
2.1 KiB
Go
59 lines
2.1 KiB
Go
package store
|
|
|
|
import "testing"
|
|
|
|
// Part B (v0.60.0) — the escrow retention red-proof (the 2026-07-17 incident class). A new escrow blob
|
|
// with a DIFFERENT sealed-passphrase sha must RETAIN the old blob (recoverable) instead of destroying
|
|
// it; a same-sha re-upload is idempotent (no supersede row). Pre-fix (destructive ON CONFLICT
|
|
// overwrite) the old blob is gone → the retrieval assertion FAILS.
|
|
func TestSaveHostEscrow_RetainsSuperseded(t *testing.T) {
|
|
st := newTestStore(t)
|
|
const h = "h1"
|
|
|
|
// 1st upload (P_old) — nothing to supersede.
|
|
sup, err := st.SaveHostEscrow(h, []byte("blob-old"), "fp-old", "zk", "2026-07-09T00:00:00Z", "SHA_OLD")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if sup {
|
|
t.Fatal("first upload must not supersede")
|
|
}
|
|
|
|
// 2nd upload (P_new, DIFFERENT sha) — must supersede + retain the old.
|
|
sup, err = st.SaveHostEscrow(h, []byte("blob-new"), "fp-new", "zk", "2026-07-16T00:00:00Z", "SHA_NEW")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !sup {
|
|
t.Fatal("a different-passphrase upload must supersede (retain the old blob)")
|
|
}
|
|
// Current row is the NEW blob (ACK/restore-serving read this — unchanged behavior).
|
|
cur, _ := st.GetHostEscrow(h)
|
|
if cur == nil || string(cur.Blob) != "blob-new" || cur.ResticPwSHA256 != "SHA_NEW" {
|
|
t.Fatalf("current row not the new blob: %+v", cur)
|
|
}
|
|
// RED-PROOF: the OLD blob is RETAINED and retrievable.
|
|
old, err := st.ListSupersededEscrow(h)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(old) != 1 || string(old[0].Blob) != "blob-old" || old[0].ResticPwSHA256 != "SHA_OLD" {
|
|
t.Fatalf("old blob NOT retained after overwrite (incident): %+v", old)
|
|
}
|
|
if n, _ := st.CountSupersededEscrow(h); n != 1 {
|
|
t.Fatalf("superseded count = %d, want 1", n)
|
|
}
|
|
|
|
// 3rd upload, SAME sha as current — idempotent (re-ceremony of the same password): NO supersede row.
|
|
sup, err = st.SaveHostEscrow(h, []byte("blob-new-2"), "fp-new", "zk", "2026-07-16T01:00:00Z", "SHA_NEW")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if sup {
|
|
t.Fatal("a same-sha re-upload must NOT supersede (idempotent)")
|
|
}
|
|
if n, _ := st.CountSupersededEscrow(h); n != 1 {
|
|
t.Fatalf("idempotent re-upload created a superseded row: count=%d", n)
|
|
}
|
|
}
|