diff --git a/scripts/whatsapp-bridge/allowlist.js b/scripts/whatsapp-bridge/allowlist.js index 4cbd82d0d2..ffc8949a7b 100644 --- a/scripts/whatsapp-bridge/allowlist.js +++ b/scripts/whatsapp-bridge/allowlist.js @@ -64,8 +64,12 @@ export function expandWhatsAppIdentifiers(identifier, sessionDir) { } export function matchesAllowedUser(senderId, allowedUsers, sessionDir) { + // Empty allowlist = NO ONE allowed (secure default, #8389). Operators + // who want an open bot must set ``WHATSAPP_ALLOWED_USERS=*`` explicitly. + // Previous behaviour (empty → return true) let any stranger DM the + // bridge and trigger a Python-side pairing-code reply. if (!allowedUsers || allowedUsers.size === 0) { - return true; + return false; } // "*" means allow everyone (consistent with SIGNAL_GROUP_ALLOWED_USERS) diff --git a/scripts/whatsapp-bridge/allowlist.test.mjs b/scripts/whatsapp-bridge/allowlist.test.mjs index 86e1f1d6bd..c6ca1cb3c4 100644 --- a/scripts/whatsapp-bridge/allowlist.test.mjs +++ b/scripts/whatsapp-bridge/allowlist.test.mjs @@ -57,3 +57,24 @@ test('matchesAllowedUser treats * as allow-all wildcard', () => { rmSync(sessionDir, { recursive: true, force: true }); } }); + +test('matchesAllowedUser rejects everyone when allowlist is empty (#8389)', () => { + // Regression guard: empty allowlist used to return true (allow-everyone), + // which let any stranger DM the bridge and trigger a Python-side + // pairing-code reply. Secure default is now "reject unless explicitly + // configured"; operators who want an open bot must set `*`. + const sessionDir = mkdtempSync(path.join(os.tmpdir(), 'hermes-wa-allowlist-')); + + try { + const empty = parseAllowedUsers(''); + assert.equal(empty.size, 0); + assert.equal(matchesAllowedUser('19175395595@s.whatsapp.net', empty, sessionDir), false); + assert.equal(matchesAllowedUser('267383306489914@lid', empty, sessionDir), false); + + // Null/undefined allowlist (defensive) also rejects. + assert.equal(matchesAllowedUser('19175395595@s.whatsapp.net', null, sessionDir), false); + assert.equal(matchesAllowedUser('19175395595@s.whatsapp.net', undefined, sessionDir), false); + } finally { + rmSync(sessionDir, { recursive: true, force: true }); + } +}); diff --git a/scripts/whatsapp-bridge/bridge.js b/scripts/whatsapp-bridge/bridge.js index 162acdaca1..9ab6118da1 100644 --- a/scripts/whatsapp-bridge/bridge.js +++ b/scripts/whatsapp-bridge/bridge.js @@ -267,17 +267,34 @@ async function startSocket() { if (!isSelfChat) continue; } - // Check allowlist for messages from others (resolve LID ↔ phone aliases) - if (!msg.key.fromMe && !matchesAllowedUser(senderId, ALLOWED_USERS, SESSION_DIR)) { - try { - console.log(JSON.stringify({ - event: 'ignored', - reason: 'allowlist_mismatch', - chatId, - senderId, - })); - } catch {} - continue; + // Handle !fromMe messages (from other people) based on mode. + // Self-chat mode only responds to the user's own messages to + // themselves — stranger DMs / group pings must never reach the + // Python gateway, otherwise a pairing-code reply fires in response + // to arbitrary incoming messages (#8389). + if (!msg.key.fromMe) { + if (WHATSAPP_MODE === 'self-chat') { + try { + console.log(JSON.stringify({ + event: 'ignored', + reason: 'self_chat_mode_rejects_non_self', + chatId, + senderId, + })); + } catch {} + continue; + } + if (!matchesAllowedUser(senderId, ALLOWED_USERS, SESSION_DIR)) { + try { + console.log(JSON.stringify({ + event: 'ignored', + reason: 'allowlist_mismatch', + chatId, + senderId, + })); + } catch {} + continue; + } } const messageContent = getMessageContent(msg); @@ -676,8 +693,12 @@ if (PAIR_ONLY) { console.log(`📁 Session stored in: ${SESSION_DIR}`); if (ALLOWED_USERS.size > 0) { console.log(`🔒 Allowed users: ${Array.from(ALLOWED_USERS).join(', ')}`); + } else if (WHATSAPP_MODE === 'self-chat') { + console.log(`🔒 Self-chat mode — only your own messages to yourself are processed.`); } else { - console.log(`⚠️ No WHATSAPP_ALLOWED_USERS set — all messages will be processed`); + console.log(`🔒 No WHATSAPP_ALLOWED_USERS set — incoming messages are rejected.`); + console.log(` Set WHATSAPP_ALLOWED_USERS= to authorize specific users,`); + console.log(` or WHATSAPP_ALLOWED_USERS=* for an explicit open bot.`); } console.log(); startSocket();