diff --git a/client/src/components/reservation/ReservationV4SourceMessageCard.vue b/client/src/components/reservation/ReservationV4SourceMessageCard.vue index 9c2bc4e..bb44d7c 100644 --- a/client/src/components/reservation/ReservationV4SourceMessageCard.vue +++ b/client/src/components/reservation/ReservationV4SourceMessageCard.vue @@ -175,8 +175,8 @@ function normalizeAttachments(value: unknown): AttachmentSummary[] { function toAttachmentSummary(value: unknown, index: number): AttachmentSummary { const record = readRecord(value) const name = record - ? readFirstString(record, ['name', 'file_name', 'filename', 'display_name', 'title']) ?? t('conversation.unnamedAttachment') - : String(value) + ? safeAttachmentName(readFirstString(record, ['name', 'file_name', 'filename', 'display_name', 'title'])) + : safeAttachmentName(String(value)) const type = record ? readFirstString(record, ['content_type', 'mime_type', 'media_type', 'file_type']) : null const size = record ? readNumber(record, 'size_bytes') ?? readNumber(record, 'size') : null return { @@ -249,6 +249,18 @@ function formatFileSize(size: number | null): string { } return `${(size / 1024 / 1024).toFixed(1)} MB` } + +function safeAttachmentName(value: string | null): string { + const trimmedValue = value?.trim() + if (!trimmedValue || isUnsafeAttachmentText(trimmedValue)) { + return t('conversation.unnamedAttachment') + } + return trimmedValue +} + +function isUnsafeAttachmentText(value: string): boolean { + return /^(https?:\/\/|oss:\/\/|s3:\/\/)/i.test(value.trim()) +}