From 974e2efad519d9a1e80c6b4909ad7692c8b9dd6a Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Fri, 10 Jul 2026 04:07:35 +0000 Subject: [PATCH] Improve story body range validations. --- .../api/messages/EnvelopeContentValidator.kt | 8 ++ .../messages/EnvelopeContentValidatorTest.kt | 77 +++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidator.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidator.kt index d5144afbee..6ee8a637f7 100644 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidator.kt +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidator.kt @@ -343,6 +343,14 @@ object EnvelopeContentValidator { validateGroupContextV2(storyMessage.group, "[StoryMessage]")?.let { return it } } + if (storyMessage.bodyRanges.any { it.isStyleRangeMissingOffsets() }) { + return Result.Invalid("[StoryMessage] Style body range is missing a start or length!") + } + + if (storyMessage.bodyRanges.hasInvalidBounds(storyMessage.textAttachment?.text)) { + return Result.Invalid("[StoryMessage] Body range with out-of-bounds start/length!") + } + return Result.Valid } diff --git a/lib/libsignal-service/src/test/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidatorTest.kt b/lib/libsignal-service/src/test/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidatorTest.kt index f6e958d85f..ac16a214e9 100644 --- a/lib/libsignal-service/src/test/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidatorTest.kt +++ b/lib/libsignal-service/src/test/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidatorTest.kt @@ -15,7 +15,9 @@ import org.whispersystems.signalservice.internal.push.BodyRange import org.whispersystems.signalservice.internal.push.Content import org.whispersystems.signalservice.internal.push.DataMessage import org.whispersystems.signalservice.internal.push.Envelope +import org.whispersystems.signalservice.internal.push.StoryMessage import org.whispersystems.signalservice.internal.push.SyncMessage +import org.whispersystems.signalservice.internal.push.TextAttachment class EnvelopeContentValidatorTest { @@ -579,4 +581,79 @@ class EnvelopeContentValidatorTest { val result = EnvelopeContentValidator.validate(envelope, content, SELF_ACI, CiphertextMessage.WHISPER_TYPE) assert(result is EnvelopeContentValidator.Result.Valid) } + + @Test + fun `validate - ensure story body range with negative length is marked invalid`() { + val content = Content( + storyMessage = StoryMessage( + textAttachment = TextAttachment(text = "abc"), + bodyRanges = listOf( + BodyRange(start = 2, length = -3, style = BodyRange.Style.BOLD) + ) + ) + ) + + val result = EnvelopeContentValidator.validate(Envelope(clientTimestamp = 1234), content, SELF_ACI, CiphertextMessage.WHISPER_TYPE) + assert(result is EnvelopeContentValidator.Result.Invalid) + } + + @Test + fun `validate - ensure story body range with negative start is marked invalid`() { + val content = Content( + storyMessage = StoryMessage( + textAttachment = TextAttachment(text = "abc"), + bodyRanges = listOf( + BodyRange(start = -1, length = 1, style = BodyRange.Style.BOLD) + ) + ) + ) + + val result = EnvelopeContentValidator.validate(Envelope(clientTimestamp = 1234), content, SELF_ACI, CiphertextMessage.WHISPER_TYPE) + assert(result is EnvelopeContentValidator.Result.Invalid) + } + + @Test + fun `validate - ensure story body range extending past the end of the text is marked invalid`() { + val content = Content( + storyMessage = StoryMessage( + textAttachment = TextAttachment(text = "abc"), + bodyRanges = listOf( + BodyRange(start = 2, length = 10, style = BodyRange.Style.BOLD) + ) + ) + ) + + val result = EnvelopeContentValidator.validate(Envelope(clientTimestamp = 1234), content, SELF_ACI, CiphertextMessage.WHISPER_TYPE) + assert(result is EnvelopeContentValidator.Result.Invalid) + } + + @Test + fun `validate - ensure story style body range missing length is marked invalid`() { + val content = Content( + storyMessage = StoryMessage( + textAttachment = TextAttachment(text = "abc"), + bodyRanges = listOf( + BodyRange(start = 0, style = BodyRange.Style.BOLD) + ) + ) + ) + + val result = EnvelopeContentValidator.validate(Envelope(clientTimestamp = 1234), content, SELF_ACI, CiphertextMessage.WHISPER_TYPE) + assert(result is EnvelopeContentValidator.Result.Invalid) + } + + @Test + fun `validate - ensure story body range that exactly covers the text is marked valid`() { + val content = Content( + storyMessage = StoryMessage( + textAttachment = TextAttachment(text = "abc"), + bodyRanges = listOf( + BodyRange(start = 0, length = 3, style = BodyRange.Style.BOLD) + ) + ) + ) + + val result = EnvelopeContentValidator.validate(Envelope(clientTimestamp = 1234), content, SELF_ACI, CiphertextMessage.WHISPER_TYPE) + assert(result is EnvelopeContentValidator.Result.Valid) + } }