Files
buzz/crates
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7andWill Pfleger 1e82a573d7 fix(relay): block all e-tag deletion routes targeting kind:31234 drafts
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>
2026-07-14 00:21:25 -04:00
..