fix(share-ai): reject non-text message parts to close size-cap bypass (#63)
MAX_SHARE_MESSAGE_CHARS only counted text parts, so a forged non-text part (tool-result/file/data) bypassed the cap and bloated the model input (token-DoS); convertToModelMessages would also expand a forged tool-result. The anonymous path runs no tools, so a client non-text part is never legitimate — reject any message with a non-text part (isTextUIPart) before the size check. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -31,7 +31,7 @@ import {
|
|||||||
} from './public-share-chat.service';
|
} from './public-share-chat.service';
|
||||||
import { evaluateShareAssistantFunnel } from './public-share-chat.funnel';
|
import { evaluateShareAssistantFunnel } from './public-share-chat.funnel';
|
||||||
import { deriveShareAccess } from './public-share-chat.access';
|
import { deriveShareAccess } from './public-share-chat.access';
|
||||||
import type { UIMessage } from 'ai';
|
import { isTextUIPart, type UIMessage } from 'ai';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Anonymous, read-only AI assistant over a SINGLE public share tree.
|
* Anonymous, read-only AI assistant over a SINGLE public share tree.
|
||||||
@@ -281,6 +281,15 @@ export async function resolveShareAssistantRequest(
|
|||||||
throw new HttpException('Too many messages', 413);
|
throw new HttpException('Too many messages', 413);
|
||||||
}
|
}
|
||||||
for (const m of messages) {
|
for (const m of messages) {
|
||||||
|
const parts = Array.isArray(m?.parts) ? m.parts : [];
|
||||||
|
// The server runs no tools on the anonymous path, so a client tool/non-text
|
||||||
|
// part is never legitimate. Reject before the size check: it keeps the char
|
||||||
|
// cap meaningful (a forged tool-result/file/data part would otherwise bypass
|
||||||
|
// it and bloat the model input) and avoids stringifying an attacker-sized
|
||||||
|
// payload via convertToModelMessages.
|
||||||
|
if (parts.some((p) => !isTextUIPart(p))) {
|
||||||
|
throw new HttpException('Unsupported message content', 400);
|
||||||
|
}
|
||||||
if (uiMessageTextLength(m) > MAX_SHARE_MESSAGE_CHARS) {
|
if (uiMessageTextLength(m) > MAX_SHARE_MESSAGE_CHARS) {
|
||||||
throw new HttpException('Message too long', 413);
|
throw new HttpException('Message too long', 413);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user