mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
Add actor_can_reference_target helper to mask author-only event ids
on all five write-path target-resolution sites (reaction channel
derivation, NIP-10 thread parent, stream-edit, forum-vote, kind:5
e-tag deletion), preventing non-authors from distinguishing a real
draft/reminder id from a random id via differential error responses.
Q2: DbError::DraftChannelRequired + channel_id=None DB-layer rejection
Q3: participates_in_thread_metadata split; outer e-tag rejection on drafts
Q4: reader_can_receive_event canonical read gate in buzz-core/filter.rs;
wire into req.rs / count.rs / bridge.rs / search path
Q5: CI step for buzz-search FTS integration tests (ignored suite now runs)
Q6: HTTP catchall kindless privacy e2e test_draft_not_returned_in_kindless_channel_http_query
Q7: draft_kind_is_excluded_from_workflow_dispatch_by_author_only_guard
Q8: a-tag defensive-guard comment rewrite in side_effects.rs
Q9: e2e smoke for outer-e-tag rejection (test_draft_rejected_outer_e_tag)
Q10: fan-out author-side positive control (test_draft_live_fanout_reaches_author_own_subscription)
Q11: mixed-kinds /count test (test_draft_attacker_mixed_kinds_count_excludes_drafts)
Q12: derive exclusion list from AUTHOR_ONLY_KINDS in thread.rs
Q13: DM privacy recipient-side test (test_dm_draft_not_readable_by_dm_recipient)
Q14: migration 0007 deploy-window note
Q15: actor_can_reference_target post-lookup helper applied at reaction channel
derivation (all actors rejected), NIP-10 thread parent (all actors
rejected), stream-edit / forum-vote (non-author masked, author falls
through), kind:5 e-tag deletion (non-author masked before authz,
false comment corrected); generalizes over AUTHOR_ONLY_KINDS so
kind:30300 reminders are also protected; unit tests confirm membership
Q16: channel-window cursor-boundary e2e (test_channel_window_cursor_boundary_excludes_draft)
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
44 lines
2.4 KiB
SQL
44 lines
2.4 KiB
SQL
-- Exclude kind 31234 (NIP-37 draft wraps) from full-text search.
|
|
--
|
|
-- NIP-37 draft wraps carry NIP-44-v2 ciphertext in `content` (or empty string
|
|
-- for deletion tombstones). Indexing ciphertext would waste storage and violate
|
|
-- the "channel/DM context lives only inside the encrypted payload" invariant
|
|
-- that makes draft wraps author-private. The relay also must not index
|
|
-- plaintext compose context through any search surface.
|
|
--
|
|
-- Additive migration: previously applied files must not change checksum.
|
|
-- We must DROP the generated column and re-ADD it with the extended exclusion
|
|
-- list; ALTER COLUMN cannot change a GENERATED expression in Postgres.
|
|
--
|
|
-- DEPLOY NOTE: the DROP + re-ADD below acquires ACCESS EXCLUSIVE on `events`
|
|
-- for the duration of the migration, blocking all reads and writes to the
|
|
-- table. The GIN index rebuild that follows is a full table scan. On large
|
|
-- deployments this migration should run during a scheduled maintenance window.
|
|
-- This is the same shape as migration 0005 and was accepted as precedent.
|
|
--
|
|
-- Final kind exclusion list after this migration:
|
|
-- 1059 = KIND_GIFT_WRAP (NIP-17 ciphertext)
|
|
-- 30300 = KIND_EVENT_REMINDER (AUTHOR_ONLY_KINDS — defense in depth)
|
|
-- 30622 = KIND_DM_VISIBILITY (per-viewer private hide state)
|
|
-- 31234 = KIND_DRAFT (NIP-37: AUTHOR_ONLY_KINDS — ciphertext or tombstone)
|
|
-- 44100 = KIND_MEMBER_ADDED_NOTIFICATION (p-gated membership notice)
|
|
-- 44101 = KIND_MEMBER_REMOVED_NOTIFICATION (p-gated membership notice)
|
|
-- 44200 = KIND_AGENT_TURN_METRIC (NIP-AM: p-gated encrypted turn metrics)
|
|
-- Constants kept in `buzz_core::kind`; inlined here because a sqlx migration
|
|
-- is frozen SQL and cannot import the Rust constant. If a new privacy-sensitive
|
|
-- kind is added there, add a new additive migration following this pattern and
|
|
-- add a regression test in `buzz-search/tests/fts_integration.rs`.
|
|
--
|
|
-- NULL tsvector never matches `@@`, so excluded rows are storage-level
|
|
-- unsearchable.
|
|
|
|
ALTER TABLE events DROP COLUMN search_tsv;
|
|
ALTER TABLE events ADD COLUMN search_tsv TSVECTOR GENERATED ALWAYS AS (
|
|
CASE WHEN kind IN (1059, 30300, 30622, 31234, 44100, 44101, 44200) THEN NULL::tsvector
|
|
ELSE to_tsvector('simple', content)
|
|
END
|
|
) STORED;
|
|
|
|
-- Recreate the GIN index dropped with the column.
|
|
CREATE INDEX idx_events_search_tsv ON events USING GIN (search_tsv);
|