mirror of
https://github.com/signalapp/libsignal.git
synced 2024-09-20 03:52:17 +02:00
backup: Validate quoted attachments
This commit is contained in:
parent
f2b9ad59d5
commit
03b71fea23
@ -4,6 +4,7 @@
|
||||
//
|
||||
|
||||
use crate::backup::chat::text::{MessageText, TextError};
|
||||
use crate::backup::file::{MessageAttachment, MessageAttachmentError};
|
||||
use crate::backup::frame::RecipientId;
|
||||
use crate::backup::method::Contains;
|
||||
use crate::backup::time::Timestamp;
|
||||
@ -17,6 +18,7 @@ pub struct Quote {
|
||||
pub author: RecipientId,
|
||||
pub quote_type: QuoteType,
|
||||
pub target_sent_timestamp: Option<Timestamp>,
|
||||
pub attachments: Vec<QuotedAttachment>,
|
||||
pub text: Option<MessageText>,
|
||||
#[serde(skip)]
|
||||
_limit_construction_to_module: (),
|
||||
@ -29,6 +31,16 @@ pub enum QuoteType {
|
||||
GiftBadge,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct QuotedAttachment {
|
||||
pub content_type: Option<String>,
|
||||
pub file_name: Option<String>,
|
||||
pub thumbnail: Option<MessageAttachment>,
|
||||
#[serde(skip)]
|
||||
_limit_construction_to_module: (),
|
||||
}
|
||||
|
||||
#[derive(Debug, displaydoc::Display, thiserror::Error)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum QuoteError {
|
||||
@ -40,6 +52,10 @@ pub enum QuoteError {
|
||||
BodyRangesWithoutText,
|
||||
/// text error: {0}
|
||||
Text(#[from] TextError),
|
||||
/// attachment thumbnail: {0}
|
||||
AttachmentThumbnail(#[from] MessageAttachmentError),
|
||||
/// attachment thumbnail cannot have flag {0:?}
|
||||
AttachmentThumbnailWrongFlag(proto::message_attachment::Flag),
|
||||
}
|
||||
|
||||
impl<R: Contains<RecipientId>> TryFromWith<proto::Quote, R> for Quote {
|
||||
@ -52,8 +68,7 @@ impl<R: Contains<RecipientId>> TryFromWith<proto::Quote, R> for Quote {
|
||||
targetSentTimestamp,
|
||||
text,
|
||||
bodyRanges,
|
||||
// TODO validate these fields
|
||||
attachments: _,
|
||||
attachments,
|
||||
special_fields: _,
|
||||
} = item;
|
||||
|
||||
@ -82,22 +97,112 @@ impl<R: Contains<RecipientId>> TryFromWith<proto::Quote, R> for Quote {
|
||||
.try_into()?,
|
||||
),
|
||||
};
|
||||
|
||||
let attachments = attachments
|
||||
.into_iter()
|
||||
.map(QuotedAttachment::try_from)
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
Ok(Self {
|
||||
author,
|
||||
quote_type,
|
||||
target_sent_timestamp,
|
||||
attachments,
|
||||
text,
|
||||
_limit_construction_to_module: (),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<proto::quote::QuotedAttachment> for QuotedAttachment {
|
||||
type Error = QuoteError;
|
||||
|
||||
fn try_from(value: proto::quote::QuotedAttachment) -> Result<Self, Self::Error> {
|
||||
let proto::quote::QuotedAttachment {
|
||||
contentType,
|
||||
fileName,
|
||||
thumbnail,
|
||||
special_fields: _,
|
||||
} = value;
|
||||
|
||||
let thumbnail = thumbnail
|
||||
.into_option()
|
||||
.map(MessageAttachment::try_from)
|
||||
.transpose()?;
|
||||
|
||||
if let Some(thumbnail) = &thumbnail {
|
||||
match thumbnail.flag {
|
||||
proto::message_attachment::Flag::NONE
|
||||
| proto::message_attachment::Flag::BORDERLESS
|
||||
| proto::message_attachment::Flag::GIF => {}
|
||||
proto::message_attachment::Flag::VOICE_MESSAGE => {
|
||||
return Err(QuoteError::AttachmentThumbnailWrongFlag(thumbnail.flag));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
content_type: contentType,
|
||||
file_name: fileName,
|
||||
thumbnail,
|
||||
_limit_construction_to_module: (),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::backup::time::testutil::MillisecondsSinceEpoch;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl proto::quote::QuotedAttachment {
|
||||
fn test_data() -> Self {
|
||||
Self {
|
||||
contentType: Some("video/mpeg".into()),
|
||||
fileName: Some("test.mpg".into()),
|
||||
thumbnail: Some(proto::MessageAttachment::test_data()).into(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl QuotedAttachment {
|
||||
fn from_proto_test_data() -> Self {
|
||||
Self {
|
||||
content_type: Some("video/mpeg".into()),
|
||||
file_name: Some("test.mpg".into()),
|
||||
thumbnail: Some(MessageAttachment::from_proto_test_data()),
|
||||
_limit_construction_to_module: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test_case(|_| {} => Ok(()); "valid")]
|
||||
#[test_case(|x| x.contentType = None => Ok(()); "no contentType")]
|
||||
#[test_case(|x| x.contentType = Some("".into()) => Ok(()); "empty contentType")]
|
||||
#[test_case(|x| x.fileName = None => Ok(()); "no fileName")]
|
||||
#[test_case(|x| x.fileName = Some("".into()) => Ok(()); "empty fileName")]
|
||||
#[test_case(|x| x.thumbnail = None.into() => Ok(()); "no thumbnail")]
|
||||
#[test_case(|x| x.thumbnail = Some(proto::MessageAttachment::default()).into() => Err(QuoteError::AttachmentThumbnail(MessageAttachmentError::NoFilePointer)); "invalid thumbnail")]
|
||||
#[test_case(|x| x.thumbnail = Some(proto::MessageAttachment {
|
||||
flag: proto::message_attachment::Flag::BORDERLESS.into(),
|
||||
..proto::MessageAttachment::test_data()
|
||||
}).into() => Ok(()); "borderless thumbnail")]
|
||||
#[test_case(|x| x.thumbnail = Some(proto::MessageAttachment {
|
||||
flag: proto::message_attachment::Flag::VOICE_MESSAGE.into(),
|
||||
..proto::MessageAttachment::test_data()
|
||||
}).into() => Err(QuoteError::AttachmentThumbnailWrongFlag(proto::message_attachment::Flag::VOICE_MESSAGE)); "voice message thumbnail")]
|
||||
fn attachment(
|
||||
modifier: impl FnOnce(&mut proto::quote::QuotedAttachment),
|
||||
) -> Result<(), QuoteError> {
|
||||
let mut attachment = proto::quote::QuotedAttachment::test_data();
|
||||
modifier(&mut attachment);
|
||||
QuotedAttachment::try_from(attachment).map(|_| ())
|
||||
}
|
||||
|
||||
impl proto::Quote {
|
||||
pub(crate) fn test_data() -> Self {
|
||||
let proto::Text {
|
||||
@ -110,6 +215,7 @@ mod test {
|
||||
authorId: proto::Recipient::TEST_ID,
|
||||
type_: proto::quote::Type::NORMAL.into(),
|
||||
targetSentTimestamp: Some(MillisecondsSinceEpoch::TEST_VALUE.0),
|
||||
attachments: vec![proto::quote::QuotedAttachment::test_data()],
|
||||
text: Some(body),
|
||||
bodyRanges,
|
||||
..Default::default()
|
||||
@ -124,6 +230,7 @@ mod test {
|
||||
author: RecipientId(proto::Recipient::TEST_ID),
|
||||
quote_type: QuoteType::Normal,
|
||||
target_sent_timestamp: Some(Timestamp::test_value()),
|
||||
attachments: vec![QuotedAttachment::from_proto_test_data()],
|
||||
_limit_construction_to_module: (),
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user