fix: gate reset auth and infer whatsapp sender

This commit is contained in:
Peter Steinberger
2026-01-06 02:23:50 +01:00
parent b85248bd07
commit b6ae376076
3 changed files with 28 additions and 5 deletions

View File

@@ -41,6 +41,7 @@
- Docs: clarify Slack manifest scopes (current vs optional) with references. Thanks @jarvis-medmatic for PR #235.
- Control UI: avoid Slack config ReferenceError by reading slack config snapshots. Thanks @sreekaransrinath for PR #249.
- Telegram: honor routing.groupChat.mentionPatterns for group mention gating. Thanks @regenrek for PR #242.
- Auto-reply: block unauthorized `/reset` and infer WhatsApp senders from E.164 inputs.
### Maintenance
- Deps: bump pi-* stack, Slack SDK, discord-api-types, file-type, zod, and Biome.

View File

@@ -18,16 +18,23 @@ export function resolveCommandAuthorization(params: {
}): CommandAuthorization {
const { ctx, cfg, commandAuthorized } = params;
const surface = (ctx.Surface ?? "").trim().toLowerCase();
const isWhatsAppSurface =
surface === "whatsapp" ||
const from = (ctx.From ?? "").replace(/^whatsapp:/, "");
const to = (ctx.To ?? "").replace(/^whatsapp:/, "");
const hasWhatsappPrefix =
(ctx.From ?? "").startsWith("whatsapp:") ||
(ctx.To ?? "").startsWith("whatsapp:");
const looksLikeE164 = (value: string) =>
Boolean(value && /^\+?\d{3,}$/.test(value.replace(/[^\d+]/g, "")));
const inferWhatsApp =
!surface &&
Boolean(cfg.whatsapp?.allowFrom?.length) &&
(looksLikeE164(from) || looksLikeE164(to));
const isWhatsAppSurface =
surface === "whatsapp" || hasWhatsappPrefix || inferWhatsApp;
const configuredAllowFrom = isWhatsAppSurface
? cfg.whatsapp?.allowFrom
: undefined;
const from = (ctx.From ?? "").replace(/^whatsapp:/, "");
const to = (ctx.To ?? "").replace(/^whatsapp:/, "");
const allowFromList =
configuredAllowFrom?.filter((entry) => entry?.trim()) ?? [];
const allowAll =
@@ -35,7 +42,9 @@ export function resolveCommandAuthorization(params: {
allowFromList.length === 0 ||
allowFromList.some((entry) => entry.trim() === "*");
const senderE164 = normalizeE164(ctx.SenderE164 ?? "");
const senderE164 = normalizeE164(
ctx.SenderE164 ?? (isWhatsAppSurface ? from : ""),
);
const ownerCandidates =
isWhatsAppSurface && !allowAll
? allowFromList.filter((entry) => entry !== "*")

View File

@@ -173,6 +173,7 @@ export async function handleCommands(params: {
shouldContinue: boolean;
}> {
const {
ctx,
cfg,
command,
directives,
@@ -193,6 +194,18 @@ export async function handleCommands(params: {
isGroup,
} = params;
const resetRequested =
command.commandBodyNormalized === "/reset" ||
command.commandBodyNormalized === "reset" ||
command.commandBodyNormalized === "/new" ||
command.commandBodyNormalized === "new";
if (resetRequested && !command.isAuthorizedSender) {
logVerbose(
`Ignoring /reset from unauthorized sender: ${command.senderE164 || "<unknown>"}`,
);
return { shouldContinue: false };
}
const activationCommand = parseActivationCommand(
command.commandBodyNormalized,
);