* docs: clarify WhatsApp allowlist behavior and document WHATSAPP_ALLOW_ALL_USERS - Add WHATSAPP_ALLOW_ALL_USERS and WHATSAPP_DEBUG to env vars reference - Warn that * is not a wildcard and silently blocks all messages - Show WHATSAPP_ALLOWED_USERS as optional, not required - Update troubleshooting with the * trap and debug mode tip - Fix Security section to mention the allow-all alternative Prompted by a user report in Discord where WHATSAPP_ALLOWED_USERS=* caused all incoming messages to be silently dropped at the bridge level. * feat: support * wildcard in platform allowlists Follow the precedent set by SIGNAL_GROUP_ALLOWED_USERS which already supports * as an allow-all wildcard. Bridge (allowlist.js): matchesAllowedUser() now checks for * in the allowedUsers set before iterating sender aliases. Gateway (run.py): _is_authorized() checks for * in allowed_ids after parsing the allowlist. This is generic — works for all platforms, not just WhatsApp. Updated docs to document * as a supported value instead of warning against it. Added WHATSAPP_ALLOW_ALL_USERS and WHATSAPP_DEBUG to the env vars reference. Tests: JS allowlist test + 2 Python gateway tests (WhatsApp + Telegram to verify cross-platform behavior).
85 lines
2.0 KiB
JavaScript
85 lines
2.0 KiB
JavaScript
import path from 'path';
|
|
import { existsSync, readFileSync } from 'fs';
|
|
|
|
export function normalizeWhatsAppIdentifier(value) {
|
|
return String(value || '')
|
|
.trim()
|
|
.replace(/:.*@/, '@')
|
|
.replace(/@.*/, '')
|
|
.replace(/^\+/, '');
|
|
}
|
|
|
|
export function parseAllowedUsers(rawValue) {
|
|
return new Set(
|
|
String(rawValue || '')
|
|
.split(',')
|
|
.map((value) => normalizeWhatsAppIdentifier(value))
|
|
.filter(Boolean)
|
|
);
|
|
}
|
|
|
|
function readMappingFile(sessionDir, identifier, suffix = '') {
|
|
const filePath = path.join(sessionDir, `lid-mapping-${identifier}${suffix}.json`);
|
|
if (!existsSync(filePath)) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
const parsed = JSON.parse(readFileSync(filePath, 'utf8'));
|
|
const normalized = normalizeWhatsAppIdentifier(parsed);
|
|
return normalized || null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function expandWhatsAppIdentifiers(identifier, sessionDir) {
|
|
const normalized = normalizeWhatsAppIdentifier(identifier);
|
|
if (!normalized) {
|
|
return new Set();
|
|
}
|
|
|
|
// Walk both phone->LID and LID->phone mapping files so allowlists can use
|
|
// either form transparently in bot mode.
|
|
const resolved = new Set();
|
|
const queue = [normalized];
|
|
|
|
while (queue.length > 0) {
|
|
const current = queue.shift();
|
|
if (!current || resolved.has(current)) {
|
|
continue;
|
|
}
|
|
|
|
resolved.add(current);
|
|
|
|
for (const suffix of ['', '_reverse']) {
|
|
const mapped = readMappingFile(sessionDir, current, suffix);
|
|
if (mapped && !resolved.has(mapped)) {
|
|
queue.push(mapped);
|
|
}
|
|
}
|
|
}
|
|
|
|
return resolved;
|
|
}
|
|
|
|
export function matchesAllowedUser(senderId, allowedUsers, sessionDir) {
|
|
if (!allowedUsers || allowedUsers.size === 0) {
|
|
return true;
|
|
}
|
|
|
|
// "*" means allow everyone (consistent with SIGNAL_GROUP_ALLOWED_USERS)
|
|
if (allowedUsers.has('*')) {
|
|
return true;
|
|
}
|
|
|
|
const aliases = expandWhatsAppIdentifiers(senderId, sessionDir);
|
|
for (const alias of aliases) {
|
|
if (allowedUsers.has(alias)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|