Files
hermes-agent/scripts/whatsapp-bridge/allowlist.js
Teknium 3e2c8c529b fix(whatsapp): resolve LID↔phone aliases in allowlist matching (#3830)
WhatsApp DMs can arrive with LID sender IDs even when
WHATSAPP_ALLOWED_USERS is configured with phone numbers. The allowlist
check now reads bridge session mapping files (lid-mapping-*.json) to
resolve phone↔LID aliases, matching users regardless of which
identifier format the message uses.

Both the Python gateway (_is_user_authorized) and the Node bridge
(allowlist.js) now share the same mapping-file-based resolution logic.

Co-authored-by: Frederico Ribeiro <fr@tecompanytea.com>
2026-03-29 18:21:50 -07:00

80 lines
1.9 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;
}
const aliases = expandWhatsAppIdentifiers(senderId, sessionDir);
for (const alias of aliases) {
if (allowedUsers.has(alias)) {
return true;
}
}
return false;
}