From 9356569f8d6dd431207a7ad69512983e6ebc38c7 Mon Sep 17 00:00:00 2001 From: naskya Date: Mon, 29 Jul 2024 07:30:57 +0900 Subject: [PATCH] fix (backend-rs): limit isahc responses to a reasonable length in all places --- Cargo.lock | 1 + Cargo.toml | 1 + packages/backend-rs/Cargo.toml | 1 + packages/backend-rs/src/federation/nodeinfo/fetch.rs | 10 ++++++++-- packages/backend-rs/src/misc/get_image_size.rs | 12 ++++++++---- packages/backend-rs/src/misc/latest_version.rs | 6 ++++-- packages/backend-rs/src/misc/translate.rs | 12 +++++++++++- 7 files changed, 34 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 25eacb6a2b..974715ab54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -213,6 +213,7 @@ dependencies = [ "chrono", "cuid2", "emojis", + "futures-util", "idna 1.0.2", "image", "isahc", diff --git a/Cargo.toml b/Cargo.toml index b1d594a12f..421ae8b674 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ chrono = { version = "0.4.38", default-features = false } convert_case = { version = "0.6.0", default-features = false } cuid2 = { version = "0.1.2", default-features = false } emojis = { version = "0.6.3", default-features = false } +futures-util = { version = "0.3.30", default-features = false } idna = { version = "1.0.2", default-features = false } image = { version = "0.25.2", default-features = false } isahc = { version = "1.7.2", default-features = false } diff --git a/packages/backend-rs/Cargo.toml b/packages/backend-rs/Cargo.toml index f6176ec0a4..54efddecbe 100644 --- a/packages/backend-rs/Cargo.toml +++ b/packages/backend-rs/Cargo.toml @@ -27,6 +27,7 @@ bcrypt = { workspace = true, features = ["std"] } chrono = { workspace = true } cuid2 = { workspace = true } emojis = { workspace = true } +futures-util = { workspace = true, features = ["io"] } idna = { workspace = true, features = ["std", "compiled_data"] } image = { workspace = true, features = ["avif", "bmp", "gif", "ico", "jpeg", "png", "tiff", "webp"] } isahc = { workspace = true, features = ["http2", "text-decoding", "json"] } diff --git a/packages/backend-rs/src/federation/nodeinfo/fetch.rs b/packages/backend-rs/src/federation/nodeinfo/fetch.rs index e7bdb0b071..307d86d273 100644 --- a/packages/backend-rs/src/federation/nodeinfo/fetch.rs +++ b/packages/backend-rs/src/federation/nodeinfo/fetch.rs @@ -3,6 +3,7 @@ //! ref: use crate::{federation::nodeinfo::schema::*, util::http_client}; +use futures_util::io::AsyncReadExt; use isahc::AsyncReadResponseExt; use serde::Deserialize; @@ -41,7 +42,7 @@ pub struct NodeinfoLink { async fn fetch_nodeinfo_links(host: &str) -> Result { let client = http_client::client()?; let wellknown_url = format!("https://{}/.well-known/nodeinfo", host); - let mut wellknown_response = client.get_async(&wellknown_url).await?; + let wellknown_response = client.get_async(&wellknown_url).await?; if !wellknown_response.status().is_success() { tracing::debug!("{:#?}", wellknown_response.body()); @@ -52,7 +53,12 @@ async fn fetch_nodeinfo_links(host: &str) -> Result { ))); } - Ok(serde_json::from_str(&wellknown_response.text().await?)?) + // Read up to 1 MiB of the response body + let text = wellknown_response + .map(|body| body.take(1024 * 1024)) + .text() + .await?; + Ok(serde_json::from_str(&text)?) } /// Check if any of the following relations is present in the given [NodeinfoLinks]. diff --git a/packages/backend-rs/src/misc/get_image_size.rs b/packages/backend-rs/src/misc/get_image_size.rs index 939f17f755..d10151483a 100644 --- a/packages/backend-rs/src/misc/get_image_size.rs +++ b/packages/backend-rs/src/misc/get_image_size.rs @@ -1,6 +1,7 @@ use crate::{database::cache, util::http_client}; +use futures_util::AsyncReadExt; use image::{ImageError, ImageFormat, ImageReader}; -use isahc::AsyncReadResponseExt; +use isahc::prelude::*; use nom_exif::{parse_jpeg_exif, EntryValue, ExifTag}; use std::io::Cursor; use tokio::sync::Mutex; @@ -73,11 +74,10 @@ pub async fn get_image_size_from_url(url: &str) -> Result { tracing::info!("retrieving image from {}", url); - let mut response = http_client::client()?.get_async(url).await?; + let response = http_client::client()?.get_async(url).await?; if !response.status().is_success() { tracing::info!("status: {}", response.status()); - tracing::debug!("response body: {:#?}", response.body()); return Err(Error::BadStatus(format!( "{} returned {}", url, @@ -85,7 +85,11 @@ pub async fn get_image_size_from_url(url: &str) -> Result { ))); } - let image_bytes = response.bytes().await?; + // Read up to 8 MiB of the response body + let image_bytes = response + .map(|body| body.take(8 * 1024 * 1024)) + .bytes() + .await?; let reader = ImageReader::new(Cursor::new(&image_bytes)).with_guessed_format()?; diff --git a/packages/backend-rs/src/misc/latest_version.rs b/packages/backend-rs/src/misc/latest_version.rs index f49ac81099..a7e5568427 100644 --- a/packages/backend-rs/src/misc/latest_version.rs +++ b/packages/backend-rs/src/misc/latest_version.rs @@ -1,6 +1,7 @@ //! Fetch latest Firefish version from the Firefish repository use crate::{database::cache, util::http_client}; +use futures_util::AsyncReadExt; use isahc::AsyncReadResponseExt; use serde::Deserialize; @@ -30,13 +31,14 @@ async fn get_latest_version() -> Result { version: String, } + // Read up to 1 MiB of the response body let mut response = http_client::client()? .get_async(UPSTREAM_PACKAGE_JSON_URL) - .await?; + .await? + .map(|body| body.take(1024 * 1024)); if !response.status().is_success() { tracing::info!("status: {}", response.status()); - tracing::debug!("response body: {:#?}", response.body()); return Err(Error::BadStatus(response.status().to_string())); } diff --git a/packages/backend-rs/src/misc/translate.rs b/packages/backend-rs/src/misc/translate.rs index 7e9d5a005f..d783b86952 100644 --- a/packages/backend-rs/src/misc/translate.rs +++ b/packages/backend-rs/src/misc/translate.rs @@ -96,6 +96,7 @@ pub async fn translate( mod deepl_translate { use crate::util::http_client; + use futures_util::AsyncReadExt; use isahc::{AsyncReadResponseExt, Request}; use serde::Deserialize; use serde_json::json; @@ -156,7 +157,13 @@ mod deepl_translate { .header("Content-Type", "application/json") .body(serde_json::to_string(&body)?)?; - let response = client.send_async(request).await?.json::().await?; + // Read up to 1 MiB of the response body + let response = client + .send_async(request) + .await? + .map(|body| body.take(1024 * 1024)) + .json::() + .await?; let result = response .translations @@ -191,6 +198,7 @@ mod deepl_translate { mod libre_translate { use crate::util::http_client; + use futures_util::AsyncReadExt; use isahc::{AsyncReadResponseExt, Request}; use serde::Deserialize; use serde_json::json; @@ -248,9 +256,11 @@ mod libre_translate { .header("Content-Type", "application/json") .body(serde_json::to_string(&body)?)?; + // Read up to 1 MiB of the response body let result = client .send_async(request) .await? + .map(|body| body.take(1024 * 1024)) .json::() .await?;