fix (backend-rs): limit isahc responses to a reasonable length in all places

This commit is contained in:
naskya 2024-07-29 07:30:57 +09:00
parent 354208b49f
commit 9356569f8d
No known key found for this signature in database
GPG key ID: 712D413B3A9FED5C
7 changed files with 34 additions and 9 deletions

1
Cargo.lock generated
View file

@ -213,6 +213,7 @@ dependencies = [
"chrono", "chrono",
"cuid2", "cuid2",
"emojis", "emojis",
"futures-util",
"idna 1.0.2", "idna 1.0.2",
"image", "image",
"isahc", "isahc",

View file

@ -23,6 +23,7 @@ chrono = { version = "0.4.38", default-features = false }
convert_case = { version = "0.6.0", default-features = false } convert_case = { version = "0.6.0", default-features = false }
cuid2 = { version = "0.1.2", default-features = false } cuid2 = { version = "0.1.2", default-features = false }
emojis = { version = "0.6.3", 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 } idna = { version = "1.0.2", default-features = false }
image = { version = "0.25.2", default-features = false } image = { version = "0.25.2", default-features = false }
isahc = { version = "1.7.2", default-features = false } isahc = { version = "1.7.2", default-features = false }

View file

@ -27,6 +27,7 @@ bcrypt = { workspace = true, features = ["std"] }
chrono = { workspace = true } chrono = { workspace = true }
cuid2 = { workspace = true } cuid2 = { workspace = true }
emojis = { workspace = true } emojis = { workspace = true }
futures-util = { workspace = true, features = ["io"] }
idna = { workspace = true, features = ["std", "compiled_data"] } idna = { workspace = true, features = ["std", "compiled_data"] }
image = { workspace = true, features = ["avif", "bmp", "gif", "ico", "jpeg", "png", "tiff", "webp"] } image = { workspace = true, features = ["avif", "bmp", "gif", "ico", "jpeg", "png", "tiff", "webp"] }
isahc = { workspace = true, features = ["http2", "text-decoding", "json"] } isahc = { workspace = true, features = ["http2", "text-decoding", "json"] }

View file

@ -3,6 +3,7 @@
//! ref: <https://nodeinfo.diaspora.software/protocol.html> //! ref: <https://nodeinfo.diaspora.software/protocol.html>
use crate::{federation::nodeinfo::schema::*, util::http_client}; use crate::{federation::nodeinfo::schema::*, util::http_client};
use futures_util::io::AsyncReadExt;
use isahc::AsyncReadResponseExt; use isahc::AsyncReadResponseExt;
use serde::Deserialize; use serde::Deserialize;
@ -41,7 +42,7 @@ pub struct NodeinfoLink {
async fn fetch_nodeinfo_links(host: &str) -> Result<NodeinfoLinks, Error> { async fn fetch_nodeinfo_links(host: &str) -> Result<NodeinfoLinks, Error> {
let client = http_client::client()?; let client = http_client::client()?;
let wellknown_url = format!("https://{}/.well-known/nodeinfo", host); 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() { if !wellknown_response.status().is_success() {
tracing::debug!("{:#?}", wellknown_response.body()); tracing::debug!("{:#?}", wellknown_response.body());
@ -52,7 +53,12 @@ async fn fetch_nodeinfo_links(host: &str) -> Result<NodeinfoLinks, Error> {
))); )));
} }
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]. /// Check if any of the following relations is present in the given [NodeinfoLinks].

View file

@ -1,6 +1,7 @@
use crate::{database::cache, util::http_client}; use crate::{database::cache, util::http_client};
use futures_util::AsyncReadExt;
use image::{ImageError, ImageFormat, ImageReader}; use image::{ImageError, ImageFormat, ImageReader};
use isahc::AsyncReadResponseExt; use isahc::prelude::*;
use nom_exif::{parse_jpeg_exif, EntryValue, ExifTag}; use nom_exif::{parse_jpeg_exif, EntryValue, ExifTag};
use std::io::Cursor; use std::io::Cursor;
use tokio::sync::Mutex; use tokio::sync::Mutex;
@ -73,11 +74,10 @@ pub async fn get_image_size_from_url(url: &str) -> Result<ImageSize, Error> {
tracing::info!("retrieving image from {}", url); 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() { if !response.status().is_success() {
tracing::info!("status: {}", response.status()); tracing::info!("status: {}", response.status());
tracing::debug!("response body: {:#?}", response.body());
return Err(Error::BadStatus(format!( return Err(Error::BadStatus(format!(
"{} returned {}", "{} returned {}",
url, url,
@ -85,7 +85,11 @@ pub async fn get_image_size_from_url(url: &str) -> Result<ImageSize, Error> {
))); )));
} }
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()?; let reader = ImageReader::new(Cursor::new(&image_bytes)).with_guessed_format()?;

View file

@ -1,6 +1,7 @@
//! Fetch latest Firefish version from the Firefish repository //! Fetch latest Firefish version from the Firefish repository
use crate::{database::cache, util::http_client}; use crate::{database::cache, util::http_client};
use futures_util::AsyncReadExt;
use isahc::AsyncReadResponseExt; use isahc::AsyncReadResponseExt;
use serde::Deserialize; use serde::Deserialize;
@ -30,13 +31,14 @@ async fn get_latest_version() -> Result<String, Error> {
version: String, version: String,
} }
// Read up to 1 MiB of the response body
let mut response = http_client::client()? let mut response = http_client::client()?
.get_async(UPSTREAM_PACKAGE_JSON_URL) .get_async(UPSTREAM_PACKAGE_JSON_URL)
.await?; .await?
.map(|body| body.take(1024 * 1024));
if !response.status().is_success() { if !response.status().is_success() {
tracing::info!("status: {}", response.status()); tracing::info!("status: {}", response.status());
tracing::debug!("response body: {:#?}", response.body());
return Err(Error::BadStatus(response.status().to_string())); return Err(Error::BadStatus(response.status().to_string()));
} }

View file

@ -96,6 +96,7 @@ pub async fn translate(
mod deepl_translate { mod deepl_translate {
use crate::util::http_client; use crate::util::http_client;
use futures_util::AsyncReadExt;
use isahc::{AsyncReadResponseExt, Request}; use isahc::{AsyncReadResponseExt, Request};
use serde::Deserialize; use serde::Deserialize;
use serde_json::json; use serde_json::json;
@ -156,7 +157,13 @@ mod deepl_translate {
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.body(serde_json::to_string(&body)?)?; .body(serde_json::to_string(&body)?)?;
let response = client.send_async(request).await?.json::<Response>().await?; // Read up to 1 MiB of the response body
let response = client
.send_async(request)
.await?
.map(|body| body.take(1024 * 1024))
.json::<Response>()
.await?;
let result = response let result = response
.translations .translations
@ -191,6 +198,7 @@ mod deepl_translate {
mod libre_translate { mod libre_translate {
use crate::util::http_client; use crate::util::http_client;
use futures_util::AsyncReadExt;
use isahc::{AsyncReadResponseExt, Request}; use isahc::{AsyncReadResponseExt, Request};
use serde::Deserialize; use serde::Deserialize;
use serde_json::json; use serde_json::json;
@ -248,9 +256,11 @@ mod libre_translate {
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.body(serde_json::to_string(&body)?)?; .body(serde_json::to_string(&body)?)?;
// Read up to 1 MiB of the response body
let result = client let result = client
.send_async(request) .send_async(request)
.await? .await?
.map(|body| body.take(1024 * 1024))
.json::<Translation>() .json::<Translation>()
.await?; .await?;