mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
A kind:5 e-tag deletion that resolved to a kind:31234 event was accepted by validate_standard_deletion_event (which only checked authorship, not target kind). After soft-delete the live head row was gone, allowing a second write with a different channel h-tag to bypass the immutable-binding invariant. That path was fixed in the previous commit on this branch. A second bypass existed via kind:9005 (channel admin delete): the 9005 branch in validate_admin_event resolved the e-tag target and authorized the actor (including channel admins who do not own the draft), then returned Ok(()). Post-storage side effects would soft-delete the head row, re-opening the cross-channel rebind window. Fix: in the kind:9005 branch of validate_admin_event (side_effects.rs), immediately after target resolution, reject pre-storage if the target kind is KIND_DRAFT. Authorship and agent-owner actors receive the tombstone-guidance error (mirror of the kind:5 wording). All other actors — including channel admins — receive the generic "target event not found" response, byte-identical to the missing-target branch, so the validator cannot act as a draft-existence oracle. Add two new E2E regressions: - test_nip09_kind9005_deletion_of_draft_is_rejected_and_binding_holds: full bypass sequence (publish draft h=A -> kind:9005 e=draft rejected -> head still live -> h=B rebind rejected), deterministic base/base+1 timestamps. - test_nip09_kind9005_admin_deletion_of_draft_is_masked_as_not_found: channel admin submits kind:9005 targeting a draft; verifies response is exactly "target event not found" (oracle-masking tripwire). Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>