DM audit: stop group messages leaking into 1:1 threads/list (1.2.42)

NIP-17 group messages (rumor with >1 recipient p-tag) were bleeding into the
1:1 surfaces:

- useConversation (thread view): a group message from `otherPubkey` where I was
  also a recipient satisfied isFromOther && isToMe, so group chatter appeared
  inside the 1:1 thread.
- useConversations (list): group messages were collapsed to conversationWith =
  sender (or an arbitrary recipient for my own sends), spawning phantom 1:1
  entries and double-listing every group (once here, once in
  useGroupConversations).

Both 1:1 hooks now skip any rumor with more than one p-tag. Groups remain the
sole responsibility of the useGroupConversation(s) hooks, which already require
pTags.length > 1. Genuine 1:1 rumors carry exactly one recipient p-tag
(createRumor doesn't tag the sender), so no legitimate message is dropped.

Verified solid (no changes): the seal(13)/rumor(14) kind checks, the
rumor.pubkey === seal.pubkey impersonation guard, and the amberSafeQueryOptions
decrypt-storm protection for interactive signers are all correct.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
shakespeare.diy 2026-09-08 18:37:39 +07:00
parent c376f37164
commit a724dadad1
2 changed files with 14 additions and 1 deletions

View file

@ -1,7 +1,7 @@
{
"name": "skate-spots",
"private": true,
"version": "1.2.41",
"version": "1.2.42",
"type": "module",
"homepage": "https://skate-spots.com",
"scripts": {

View file

@ -322,6 +322,12 @@ export function useConversation(otherPubkey: string) {
// Check if this message is part of the conversation with otherPubkey
const pTags = rumor.tags.filter(t => t[0] === 'p').map(t => t[1]);
// 1:1 only. A rumor with more than one recipient is a GROUP message
// (handled by useGroupConversation). Without this guard a group chat
// leaks into the 1:1 thread with each of its participants.
if (pTags.length > 1) continue;
const isFromOther = rumor.pubkey === otherPubkey;
const isToOther = pTags.includes(otherPubkey);
const isFromMe = rumor.pubkey === user.pubkey;
@ -428,6 +434,13 @@ export function useConversations() {
// Determine conversation partner
const pTags = rumor.tags.filter(t => t[0] === 'p').map(t => t[1]);
// 1:1 list only. A rumor addressed to more than one recipient is a
// GROUP message (surfaced by useGroupConversations). Without this
// guard each group would masquerade as a phantom 1:1 with whichever
// participant happened to speak, and would appear twice in the UI.
if (pTags.length > 1) continue;
let conversationWith: string;
if (rumor.pubkey === user.pubkey) {