From 1ec39821af58ec891d17d993845d73e2b2c5ee62 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Tue, 10 Feb 2026 15:38:55 +0100 Subject: [PATCH] Ensure aspect ratio of images in the timeline is restricted (#6168) * Ensure aspect ratio of images in the timeline is restricted Otherwise, this could cause a crash in Compose since the width and height values could become way too large. --- .../event/TimelineItemAspectRatioBox.kt | 5 ++++- .../event/TimelineItemContentMessageFactory.kt | 18 +++++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAspectRatioBox.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAspectRatioBox.kt index f25957e07f..96f86aecbd 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAspectRatioBox.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAspectRatioBox.kt @@ -20,6 +20,8 @@ import androidx.compose.ui.unit.dp const val MIN_HEIGHT_IN_DP = 100 const val MAX_HEIGHT_IN_DP = 360 const val DEFAULT_ASPECT_RATIO = 1.33f +const val MIN_ASPECT_RATIO = 0.001f +const val MAX_ASPECT_RATIO = 10f @Composable fun TimelineItemAspectRatioBox( @@ -30,7 +32,8 @@ fun TimelineItemAspectRatioBox( maxHeight: Int = MAX_HEIGHT_IN_DP, content: @Composable (BoxScope.() -> Unit), ) { - val safeAspectRatio = aspectRatio ?: DEFAULT_ASPECT_RATIO + // Make sure the aspect ratio is not extremely large, otherwise the resulting size can crash Compose + val safeAspectRatio = aspectRatio?.coerceIn(MIN_ASPECT_RATIO, MAX_ASPECT_RATIO) ?: DEFAULT_ASPECT_RATIO Box( modifier = modifier .heightIn(min = minHeight.dp, max = maxHeight.dp) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt index 3d8467729a..1db6ad304c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt @@ -50,6 +50,11 @@ import kotlinx.collections.immutable.toImmutableList import org.jsoup.nodes.Document import kotlin.time.Duration +private const val MIN_IMAGE_SIZE = 1L +private const val MAX_IMAGE_SIZE = 10_000L +private const val MIN_ASPECT_RATIO = 0.001f +private const val MAX_ASPECT_RATIO = 10f + @Inject class TimelineItemContentMessageFactory( private val fileSizeFormatter: FileSizeFormatter, @@ -83,7 +88,10 @@ class TimelineItemContentMessageFactory( val dom = messageType.formattedCaption?.toHtmlDocument(permalinkParser = permalinkParser) val formattedCaption = dom?.let(::parseHtml) ?: messageType.caption?.withLinks() - val aspectRatio = aspectRatioOf(messageType.info?.width, messageType.info?.height) + // Coerce the image sizes and prevent invalid aspect ratios, which can cause crashes + val width = messageType.info?.width?.coerceIn(MIN_IMAGE_SIZE, MAX_IMAGE_SIZE) + val height = messageType.info?.height?.coerceIn(MIN_IMAGE_SIZE, MAX_IMAGE_SIZE) + val aspectRatio = aspectRatioOf(width, height)?.coerceIn(MIN_ASPECT_RATIO, MAX_ASPECT_RATIO) TimelineItemImageContent( filename = messageType.filename, fileSize = messageType.info?.size ?: 0, @@ -94,10 +102,10 @@ class TimelineItemContentMessageFactory( thumbnailSource = messageType.info?.thumbnailSource, mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream, blurhash = messageType.info?.blurhash, - width = messageType.info?.width?.toInt(), - height = messageType.info?.height?.toInt(), - thumbnailWidth = messageType.info?.thumbnailInfo?.width?.toInt(), - thumbnailHeight = messageType.info?.thumbnailInfo?.height?.toInt(), + width = width?.toInt(), + height = height?.toInt(), + thumbnailWidth = messageType.info?.thumbnailInfo?.width?.coerceIn(MIN_IMAGE_SIZE, MAX_IMAGE_SIZE)?.toInt(), + thumbnailHeight = messageType.info?.thumbnailInfo?.height?.coerceIn(MIN_IMAGE_SIZE, MAX_IMAGE_SIZE)?.toInt(), aspectRatio = aspectRatio, formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0), fileExtension = fileExtensionExtractor.extractFromName(messageType.filename)