mirror of
https://github.com/chatmail/core.git
synced 2026-09-22 04:58:47 +03:00
fix: do not load webxdc icon if it has too large dimensions
BREAKING CHANGE: get_webxdc_blob() may fail to load icon.png or icon.jpg if image dimensions are too large. The issue is discovered by https://github.com/Sergei768
This commit is contained in:
@@ -2227,6 +2227,9 @@ impl CommandApi {
|
||||
/// Get blob encoded as base64 from a webxdc message
|
||||
///
|
||||
/// path is the path of the file within webxdc archive
|
||||
///
|
||||
/// If the file is `icon.png` or `icon.jpg`,
|
||||
/// loading it may fail if dimensions are unexpectedly large.
|
||||
async fn get_webxdc_blob(
|
||||
&self,
|
||||
account_id: u32,
|
||||
|
||||
@@ -20,6 +20,7 @@ mod maps_integration;
|
||||
|
||||
use std::cmp::max;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Cursor;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::{Context as _, Result, anyhow, bail, ensure, format_err};
|
||||
@@ -27,6 +28,7 @@ use anyhow::{Context as _, Result, anyhow, bail, ensure, format_err};
|
||||
use async_zip::tokio::read::seek::ZipFileReader as SeekZipFileReader;
|
||||
use deltachat_contact_tools::sanitize_bidi_characters;
|
||||
use deltachat_derive::FromSql;
|
||||
use image::{ImageFormat, ImageReader};
|
||||
use mail_builder::mime::MimePart;
|
||||
use rusqlite::OptionalExtension;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -875,6 +877,9 @@ impl Message {
|
||||
/// Currently, this works only if the message is an webxdc instance.
|
||||
///
|
||||
/// `name` is the filename within the archive, e.g. `index.html`.
|
||||
///
|
||||
/// If the file is `icon.png` or `icon.jpg`,
|
||||
/// loading it may fail if dimensions are unexpectedly large.
|
||||
pub async fn get_webxdc_blob(&self, context: &Context, name: &str) -> Result<Vec<u8>> {
|
||||
ensure!(self.viewtype == Viewtype::Webxdc, "No webxdc instance.");
|
||||
|
||||
@@ -903,7 +908,25 @@ impl Message {
|
||||
));
|
||||
}
|
||||
|
||||
get_blob(&mut archive, name).await
|
||||
let blob = get_blob(&mut archive, name).await?;
|
||||
if name == "icon.png" || name == "icon.jpg" {
|
||||
let image_reader = ImageReader::new(Cursor::new(&blob))
|
||||
.with_guessed_format()
|
||||
.context("Reading from Cursor must never fail")?;
|
||||
match image_reader.format() {
|
||||
None => bail!("Unable to determine image format"),
|
||||
Some(ImageFormat::Png) | Some(ImageFormat::Jpeg) => {
|
||||
// We accept PNG named icon.jpg and JPEG named icon.png
|
||||
// to avoid incompatibilities, but no unexpected formats like GIF.
|
||||
}
|
||||
Some(format) => bail!("Unexpected icon format {format:?}"),
|
||||
}
|
||||
let (width, height) = image_reader
|
||||
.into_dimensions()
|
||||
.context("Failed to determine icon dimensions")?;
|
||||
ensure!(width <= 4096 && height <= 4096, "Icon is too large");
|
||||
}
|
||||
Ok(blob)
|
||||
}
|
||||
|
||||
/// Return info from manifest.toml or from fallbacks.
|
||||
|
||||
@@ -1080,6 +1080,60 @@ async fn test_get_webxdc_blob() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tests that valid webxdc icon can be loaded.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_webxdc_blob_icon() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let chat_id = create_group(alice, "chat").await?;
|
||||
|
||||
{
|
||||
let mut instance = create_webxdc_instance(
|
||||
alice,
|
||||
"with-png-icon.xdc",
|
||||
include_bytes!("../../test-data/webxdc/with-png-icon.xdc"),
|
||||
)?;
|
||||
send_msg(alice, chat_id, &mut instance).await?;
|
||||
let buf = instance.get_webxdc_blob(alice, "icon.png").await?;
|
||||
assert_eq!(buf.len(), 103);
|
||||
}
|
||||
|
||||
{
|
||||
let mut instance = create_webxdc_instance(
|
||||
alice,
|
||||
"with-jpg-icon.xdc",
|
||||
include_bytes!("../../test-data/webxdc/with-jpg-icon.xdc"),
|
||||
)?;
|
||||
send_msg(alice, chat_id, &mut instance).await?;
|
||||
let buf = instance.get_webxdc_blob(alice, "icon.jpg").await?;
|
||||
assert_eq!(buf.len(), 286);
|
||||
}
|
||||
|
||||
{
|
||||
// Webxdc with icon.png than is in fact a text file.
|
||||
let mut instance = create_webxdc_instance(
|
||||
alice,
|
||||
"with-broken-png-icon.xdc",
|
||||
include_bytes!("../../test-data/webxdc/with-broken-png-icon.xdc"),
|
||||
)?;
|
||||
send_msg(alice, chat_id, &mut instance).await?;
|
||||
assert!(instance.get_webxdc_blob(alice, "icon.png").await.is_err());
|
||||
}
|
||||
|
||||
{
|
||||
// Webxdc with icon.png than is a 9999x9999 PNG image.
|
||||
let mut instance = create_webxdc_instance(
|
||||
alice,
|
||||
"with-too-large-png-icon.xdc",
|
||||
include_bytes!("../../test-data/webxdc/with-too-large-png-icon.xdc"),
|
||||
)?;
|
||||
send_msg(alice, chat_id, &mut instance).await?;
|
||||
assert!(instance.get_webxdc_blob(alice, "icon.png").await.is_err());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_webxdc_blob_default_icon() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
BIN
test-data/webxdc/with-broken-png-icon.xdc
Normal file
BIN
test-data/webxdc/with-broken-png-icon.xdc
Normal file
Binary file not shown.
BIN
test-data/webxdc/with-too-large-png-icon.xdc
Normal file
BIN
test-data/webxdc/with-too-large-png-icon.xdc
Normal file
Binary file not shown.
Reference in New Issue
Block a user