package reconcile import "testing" // The storage-authorization redesign: a USER-DATA data-bearing wipe is customer-confirmable // (durable-id-bound, no operator signature); SYSTEM and BACKUP stay operator-signature ONLY, and a // `confirmed:true` claim on them is REFUSED by role (a compromised controller can't relabel a // protected device to walk the gate). These assert the gate's tiering — the non-hollow checks the // spec calls for: "the gate refuses a `confirmed` wipe on system/backup (assert no exec); a user-data // confirmed wipe binds to the durable id (a mismatched id is refused)". const durA = "byid:wwn-0x5000c500a" const durB = "byuuid:1111-2222" // user-data + confirmed + matching durable id → allowed (customer-confirmable), audited with the id. func TestStorageWipe_UserDataConfirmed_Allowed(t *testing.T) { aud := &captureAudit{} g := NewGate(nil, testHost, aud, nil) // NO verifier pinned — proves no operator signature is needed d := g.AuthorizeStorageWipe(StorageWipeAuthz{ HostID: testHost, Role: StorageRoleUserData, DeviceDurableID: durA, Confirmed: true, ConfirmDurableID: durA, }, nil) if !d.Allowed || d.Disposition != CustomerConfirmable || d.Reason != ReasonCustomerConfirmed { t.Fatalf("user-data confirmed+matching: got allowed=%v disp=%s reason=%s", d.Allowed, d.Disposition, d.Reason) } if len(aud.recs) != 1 || !aud.recs[0].Allowed || aud.recs[0].DurableID != durA { t.Fatalf("customer-confirmed wipe must be audited with the durable id: %+v", aud.recs) } } // user-data + confirmed but the confirmation binds to a DIFFERENT disk's durable id → refused // (binding_mismatch). A confirmation for one disk can't wipe another. func TestStorageWipe_UserDataDurableMismatch_Refused(t *testing.T) { g := NewGate(nil, testHost, &captureAudit{}, nil) d := g.AuthorizeStorageWipe(StorageWipeAuthz{ HostID: testHost, Role: StorageRoleUserData, DeviceDurableID: durA, Confirmed: true, ConfirmDurableID: durB, // confirms B, device is A }, nil) if d.Allowed || d.Reason != ReasonBindingMismatch { t.Fatalf("durable-id mismatch: got allowed=%v reason=%s, want refused binding_mismatch", d.Allowed, d.Reason) } } // user-data + an UNRESOLVABLE device durable id fails safe (refused) even when confirmed. func TestStorageWipe_UserDataNoDurable_Refused(t *testing.T) { g := NewGate(nil, testHost, &captureAudit{}, nil) d := g.AuthorizeStorageWipe(StorageWipeAuthz{ HostID: testHost, Role: StorageRoleUserData, DeviceDurableID: "", Confirmed: true, ConfirmDurableID: "", }, nil) if d.Allowed { t.Fatal("a wipe with no resolvable durable id must not be allowed even when confirmed") } } // user-data, NOT confirmed → pending_confirmation (ask the customer; not a signature). func TestStorageWipe_UserDataUnconfirmed_PendingConfirmation(t *testing.T) { g := NewGate(nil, testHost, &captureAudit{}, nil) d := g.AuthorizeStorageWipe(StorageWipeAuthz{ HostID: testHost, Role: StorageRoleUserData, DeviceDurableID: durA, }, nil) if d.Allowed || d.Disposition != CustomerConfirmable || d.Reason != ReasonPendingConfirmation { t.Fatalf("user-data unconfirmed: got allowed=%v disp=%s reason=%s", d.Allowed, d.Disposition, d.Reason) } } // THE HEADLINE: a `confirmed:true` wipe on a SYSTEM device is REFUSED — it falls to the // operator-signature path (no signer pinned → pending_signature), NOT customer-confirmable. The // customer's confirmation is ignored BY ROLE. Same for BACKUP. func TestStorageWipe_SystemConfirmedTrue_RefusedBySignature(t *testing.T) { for _, role := range []string{StorageRoleSystem, StorageRoleBackup} { g := NewGate(nil, testHost, &captureAudit{}, nil) d := g.AuthorizeStorageWipe(StorageWipeAuthz{ HostID: testHost, Role: role, DeviceDurableID: durA, Confirmed: true, ConfirmDurableID: durA, // a matching confirmation — must NOT help }, nil) if d.Allowed { t.Fatalf("role=%s: a confirmed wipe of a protected device was ALLOWED — invariant violated", role) } if d.Disposition != Destructive || d.Reason != ReasonPendingSignature { t.Fatalf("role=%s: got disp=%s reason=%s, want destructive/pending_signature", role, d.Disposition, d.Reason) } } } // An UNKNOWN/empty role fails safe to the protected (operator-signature) path, never user-data. func TestStorageWipe_UnknownRole_FailsSafeDestructive(t *testing.T) { g := NewGate(nil, testHost, &captureAudit{}, nil) d := g.AuthorizeStorageWipe(StorageWipeAuthz{ HostID: testHost, Role: "", Confirmed: true, DeviceDurableID: durA, ConfirmDurableID: durA, }, nil) if d.Allowed || d.Disposition != Destructive { t.Fatalf("unknown role must fail safe to destructive: got allowed=%v disp=%s", d.Allowed, d.Disposition) } }