fix: trim sender ids before auth fallback

This commit is contained in:
Peter Steinberger
2026-01-12 06:39:14 +00:00
parent 5a2688c7b5
commit c2e37c78ff
5 changed files with 98 additions and 2 deletions

View File

@@ -143,6 +143,7 @@
- Auto-reply: align `/think` default display with model reasoning defaults. (#751) — thanks @gabriel-trigo.
- Auto-reply: flush block reply buffers on tool boundaries. (#750) — thanks @sebslight.
- Auto-reply: allow sender fallback for command authorization when `SenderId` is empty (WhatsApp self-chat). (#755) — thanks @juanpablodlc.
- Auto-reply: treat whitespace-only sender ids as missing for command authorization (WhatsApp self-chat).
- Heartbeat: refresh prompt text for updated defaults.
- Agents/Tools: use PowerShell on Windows to capture system utility output. (#748) — thanks @myfunc.
- Docker: tolerate unset optional env vars in docker-setup.sh under strict mode. (#725) — thanks @petradonka.

View File

@@ -27,4 +27,50 @@ describe("resolveCommandAuthorization", () => {
expect(auth.senderId).toBe("+123");
expect(auth.isAuthorizedSender).toBe(true);
});
it("falls back from whitespace SenderId to SenderE164", () => {
const cfg = {
whatsapp: { allowFrom: ["+123"] },
} as ClawdbotConfig;
const ctx = {
Provider: "whatsapp",
Surface: "whatsapp",
From: "whatsapp:+999",
SenderId: " ",
SenderE164: "+123",
} as MsgContext;
const auth = resolveCommandAuthorization({
ctx,
cfg,
commandAuthorized: true,
});
expect(auth.senderId).toBe("+123");
expect(auth.isAuthorizedSender).toBe(true);
});
it("falls back to From when SenderId and SenderE164 are whitespace", () => {
const cfg = {
whatsapp: { allowFrom: ["+999"] },
} as ClawdbotConfig;
const ctx = {
Provider: "whatsapp",
Surface: "whatsapp",
From: "whatsapp:+999",
SenderId: " ",
SenderE164: " ",
} as MsgContext;
const auth = resolveCommandAuthorization({
ctx,
cfg,
commandAuthorized: true,
});
expect(auth.senderId).toBe("+999");
expect(auth.isAuthorizedSender).toBe(true);
});
});

View File

@@ -96,7 +96,9 @@ export function resolveCommandAuthorization(params: {
}
const ownerList = ownerCandidates;
const senderRaw = ctx.SenderId || ctx.SenderE164 || from;
const senderIdCandidate = ctx.SenderId?.trim() ?? "";
const senderE164Candidate = ctx.SenderE164?.trim() ?? "";
const senderRaw = senderIdCandidate || senderE164Candidate || from;
const senderId = senderRaw
? formatAllowFromList({
dock,

View File

@@ -153,6 +153,53 @@ describe("partial reply gating", () => {
expect(reply).toHaveBeenCalledWith("final reply");
});
it("falls back from empty senderJid to senderE164 for SenderId", async () => {
const reply = vi.fn().mockResolvedValue(undefined);
const sendComposing = vi.fn().mockResolvedValue(undefined);
const sendMedia = vi.fn().mockResolvedValue(undefined);
const replyResolver = vi.fn().mockResolvedValue({ text: "final reply" });
const mockConfig: ClawdbotConfig = {
whatsapp: {
allowFrom: ["*"],
},
};
setLoadConfigMock(mockConfig);
await monitorWebProvider(
false,
async ({ onMessage }) => {
await onMessage({
id: "m1",
from: "+1000",
conversationId: "+1000",
to: "+2000",
body: "hello",
timestamp: Date.now(),
chatType: "direct",
chatId: "direct:+1000",
senderJid: "",
senderE164: "+1000",
sendComposing,
reply,
sendMedia,
});
return { close: vi.fn().mockResolvedValue(undefined) };
},
false,
replyResolver,
);
resetLoadConfigMock();
expect(replyResolver).toHaveBeenCalledTimes(1);
const ctx = replyResolver.mock.calls[0]?.[0] ?? {};
expect(ctx.SenderE164).toBe("+1000");
expect(ctx.SenderId).toBe("+1000");
});
it("updates last-route for direct chats without senderE164", async () => {
const now = Date.now();
const mainSessionKey = "agent:main:main";

View File

@@ -1291,7 +1291,7 @@ export async function monitorWebProvider(
msg.senderE164,
),
SenderName: msg.senderName,
SenderId: msg.senderJid ?? msg.senderE164,
SenderId: msg.senderJid?.trim() || msg.senderE164,
SenderE164: msg.senderE164,
WasMentioned: msg.wasMentioned,
...(msg.location ? toLocationContext(msg.location) : {}),