


























@@ -156,6 +156,20 @@ function describeChatForError(values: {
156156return parts.length === 0 ? "<unknown chat>" : parts.join(", ");
157157}
158158159+function describeMessageIdForError(inputId: string, inputKind: "short" | "uuid"): string {
160+// Don't reflect the raw message id back into an error message that may end
161+// up in agent transcripts / tool results / log streams. Surface only the
162+// shape (numeric short id length range, or a UUID prefix) so callers can
163+// still tell which message id they typed (CWE-117 / CWE-200).
164+if (inputKind === "short") {
165+const len = inputId.length;
166+return `<short:${len}-digit>`;
167+}
168+// For UUID input, expose just an 8-char prefix; consumer can correlate
169+// against full GUID via the trace if needed.
170+return `<uuid:${inputId.slice(0, 8)}…>`;
171+}
172+159173function buildCrossChatError(
160174inputId: string,
161175inputKind: "short" | "uuid",
@@ -167,12 +181,23 @@ function buildCrossChatError(
167181 ? `Retry with the full message GUID to avoid cross-chat reactions/replies landing in the wrong conversation.`
168182 : `Retry with the correct chat target — even the full GUID cannot be reused across chats.`;
169183return new Error(
170-`BlueBubbles message id "${inputId}" belongs to a different chat ` +
184+`BlueBubbles message id ${describeMessageIdForError(inputId, inputKind)} belongs to a different chat ` +
171185`(${describeChatForError(cached)}) than the current call target ` +
172186`(${describeChatForError(ctx)}). ${remediation}`,
173187);
174188}
175189190+function hasChatScope(ctx?: BlueBubblesChatContext): boolean {
191+if (!ctx) {
192+return false;
193+}
194+return Boolean(
195+normalizeOptionalString(ctx.chatGuid) ||
196+normalizeOptionalString(ctx.chatIdentifier) ||
197+typeof ctx.chatId === "number",
198+);
199+}
200+176201/**
177202 * Resolves a short message ID (e.g., "1", "2") to a full BlueBubbles GUID.
178203 * Returns the input unchanged if it's already a GUID or not found in the mapping.
@@ -199,6 +224,17 @@ export function resolveBlueBubblesMessageId(
199224200225// If it looks like a short ID (numeric), try to resolve it
201226if (/^\d+$/.test(trimmed)) {
227+// Privileged callers (requireKnownShortId=true) MUST scope the resolution
228+// to a chat. Without a chat scope the cross-chat guard cannot detect when
229+// the short id belongs to a different chat than the action target — short
230+// ids are allocated from a single global counter across every account and
231+// chat, so an empty `chatContext={}` would otherwise let an action operate
232+// on a message in the wrong conversation (CWE-285).
233+if (opts?.requireKnownShortId && !hasChatScope(opts.chatContext)) {
234+throw new Error(
235+`BlueBubbles short message id "${describeMessageIdForError(trimmed, "short")}" requires a chat scope (chatGuid / chatIdentifier / chatId or a --to target).`,
236+);
237+}
202238const uuid = blueBubblesShortIdToUuid.get(trimmed);
203239if (uuid) {
204240if (opts?.chatContext) {
@@ -211,7 +247,7 @@ export function resolveBlueBubblesMessageId(
211247}
212248if (opts?.requireKnownShortId) {
213249throw new Error(
214-`BlueBubbles short message id "${trimmed}" is no longer available. Use MessageSidFull.`,
250+`BlueBubbles short message id ${describeMessageIdForError(trimmed, "short")} is no longer available. Use MessageSidFull.`,
215251);
216252}
217253return trimmed;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。