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:
link2xt
2026-09-03 10:12:24 +00:00
committed by l
parent a516829171
commit 506a78b5d4
5 changed files with 81 additions and 1 deletions

View File

@@ -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.

View File

@@ -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;