From ec2a8d37d6429d97ba39ca60a4ad3585b67be1b9 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 15 Jul 2026 16:40:42 +0000 Subject: [PATCH] Improve edit message validations. --- .../api/messages/EnvelopeContentValidator.kt | 14 +++++-- .../messages/EnvelopeContentValidatorTest.kt | 40 +++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) 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 6ee8a637f7..59505135f4 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 @@ -67,7 +67,7 @@ object EnvelopeContentValidator { content.typingMessage != null -> validateTypingMessage(envelope, content.typingMessage) content.decryptionErrorMessage != null -> validateDecryptionErrorMessage(content.decryptionErrorMessage.toByteArray()) content.storyMessage != null -> validateStoryMessage(content.storyMessage) - content.editMessage != null -> validateEditMessage(content.editMessage) + content.editMessage != null -> validateEditMessage(envelope, content.editMessage) content.pniSignatureMessage != null -> Result.Valid content.senderKeyDistributionMessage != null -> Result.Valid else -> Result.Invalid("Content is empty!") @@ -247,7 +247,7 @@ object EnvelopeContentValidator { } else if (syncMessage.sent.storyMessageRecipients.isNotEmpty()) { Result.Valid } else if (syncMessage.sent.editMessage != null) { - validateEditMessage(syncMessage.sent.editMessage) + validateEditMessage(envelope, syncMessage.sent.editMessage) } else { Result.Invalid("[SyncMessage] Empty SyncMessage.sent!") } @@ -354,7 +354,7 @@ object EnvelopeContentValidator { return Result.Valid } - private fun validateEditMessage(editMessage: EditMessage): Result { + private fun validateEditMessage(envelope: Envelope, editMessage: EditMessage): Result { if (editMessage.dataMessage == null) { return Result.Invalid("[EditMessage] No data message present") } @@ -365,6 +365,14 @@ object EnvelopeContentValidator { val dataMessage: DataMessage = editMessage.dataMessage + if (dataMessage.timestamp == null) { + return Result.Invalid("[EditMessage] Missing timestamp!") + } + + if (dataMessage.timestamp != envelope.clientTimestamp) { + return Result.Invalid("[EditMessage] Timestamps don't match! envelope: ${envelope.clientTimestamp}, content: ${dataMessage.timestamp}") + } + if (dataMessage.requiredProtocolVersion != null && dataMessage.requiredProtocolVersion > DataMessage.ProtocolVersion.CURRENT.value) { return Result.UnsupportedDataMessage( ourVersion = DataMessage.ProtocolVersion.CURRENT.value, 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 ac16a214e9..658c311c19 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 @@ -14,6 +14,7 @@ import org.whispersystems.signalservice.internal.push.AttachmentPointer 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.EditMessage import org.whispersystems.signalservice.internal.push.Envelope import org.whispersystems.signalservice.internal.push.StoryMessage import org.whispersystems.signalservice.internal.push.SyncMessage @@ -656,4 +657,43 @@ class EnvelopeContentValidatorTest { val result = EnvelopeContentValidator.validate(Envelope(clientTimestamp = 1234), content, SELF_ACI, CiphertextMessage.WHISPER_TYPE) assert(result is EnvelopeContentValidator.Result.Valid) } + + @Test + fun `validate - ensure edit message with mismatched nested timestamp is marked invalid`() { + val content = Content( + editMessage = EditMessage( + targetSentTimestamp = 1000, + dataMessage = DataMessage(timestamp = 5678) + ) + ) + + val result = EnvelopeContentValidator.validate(Envelope(clientTimestamp = 1234), content, SELF_ACI, CiphertextMessage.WHISPER_TYPE) + assert(result is EnvelopeContentValidator.Result.Invalid) + } + + @Test + fun `validate - ensure edit message with missing nested timestamp is marked invalid`() { + val content = Content( + editMessage = EditMessage( + targetSentTimestamp = 1000, + dataMessage = DataMessage() + ) + ) + + val result = EnvelopeContentValidator.validate(Envelope(clientTimestamp = 1234), content, SELF_ACI, CiphertextMessage.WHISPER_TYPE) + assert(result is EnvelopeContentValidator.Result.Invalid) + } + + @Test + fun `validate - ensure edit message with nested timestamp matching the envelope is marked valid`() { + val content = Content( + editMessage = EditMessage( + targetSentTimestamp = 1000, + dataMessage = DataMessage(timestamp = 1234) + ) + ) + + val result = EnvelopeContentValidator.validate(Envelope(clientTimestamp = 1234), content, SELF_ACI, CiphertextMessage.WHISPER_TYPE) + assert(result is EnvelopeContentValidator.Result.Valid) + } }