b505ce4ba0
The first draft (5121536f, unpushed) re-issued the claim code and the off-site login on a
reuse whose old box was stale or switched off. A security review flagged it: the caller
holds only the household's retrieval passphrase, and both re-issues are operator
session-only actions (05 section 17.5, 09 decision 252, R-1055/R-1059). Now the reuse
path classifies the old box and re-issues nothing: gone -> host_reinstall_over_old_host
(warning, mailed to the operator: delete the old host and re-run, or re-issue by hand);
live -> host_reenroll_while_live (info). The mint path after the operator's delete is
unchanged and still re-issues. The once-per-silence record and the claim engine comment
edit are reverted. 05 section 8, README and CHANGELOG updated; tests red-proved.
45 lines
2.1 KiB
Go
45 lines
2.1 KiB
Go
package notify
|
|
|
|
import "testing"
|
|
|
|
// R-1150 (v0.159.0) — the refused whole-guest backup pair and the agent's capability pair never reach a
|
|
// household, under the configuration that would break it: the customer HAS the type in enabled_events and
|
|
// HAS an e-mail. The operator still gets the warning. (The api package pins the other register: none of
|
|
// these is in allowedEventTypes, so no box can POST one.)
|
|
//
|
|
// COMPANION RED-PROOF (observed 2026-10-19): with the four lines removed from operatorOnlyEvents this fails
|
|
// with
|
|
//
|
|
// r1150_operator_only_test.go: whole_guest_backup_refused: the household was mailed (customer@example.com) — enabled_events opted it in
|
|
// r1150_operator_only_test.go: agent_capability_degraded: the household was mailed (customer@example.com) — enabled_events opted it in
|
|
//
|
|
// Restored.
|
|
func TestR1150_RefusedBackupAndCapabilityEventsNeverReachTheCustomer(t *testing.T) {
|
|
// R-1154 (v0.159.0): the reinstall-over-the-old-host warning rides the same register and the same proof.
|
|
for _, et := range []string{"whole_guest_backup_refused", "agent_capability_degraded", "host_reinstall_over_old_host"} {
|
|
st := opOnlyStore(t)
|
|
if err := st.SaveNotificationPrefs("c1", "customer@example.com", []string{et}, 6); err != nil {
|
|
t.Fatalf("SaveNotificationPrefs: %v", err)
|
|
}
|
|
rec := &sentTo{}
|
|
d := opOnlyDispatcher(t, st, rec)
|
|
d.ProcessEvent("c1", et, "warning", "Host c1-abc123: test", `{"host_id":"c1-abc123"}`, "hub")
|
|
var customer, operator bool
|
|
for _, to := range rec.to {
|
|
customer = customer || to == "customer@example.com"
|
|
operator = operator || to == "operator@felhom.eu"
|
|
}
|
|
if customer {
|
|
t.Errorf("%s: the household was mailed (customer@example.com) — enabled_events opted it in", et)
|
|
}
|
|
if !operator {
|
|
t.Errorf("%s: the operator was not mailed — the guard must be customer-only", et)
|
|
}
|
|
}
|
|
for _, et := range []string{"whole_guest_backup_refusal_cleared", "agent_capability_recovered", "host_reenroll_while_live"} {
|
|
if !operatorOnlyEvents[et] {
|
|
t.Errorf("%s must be operator-only (the pair's other edge)", et)
|
|
}
|
|
}
|
|
}
|