CVE-2026-29178
Description
Lemmy, a link aggregator and forum for the fediverse, is vulnerable to server-side request forgery via a dependency on activitypub_federation, a framework for ActivityPub federation in Rust. Prior to version 0.19.16, the GET /api/v4/image/{filename} endpoint is vulnerable to unauthenticated SSRF through parameter injection in the file_type query parameter. An attacker can inject arbitrary query parameters into the internal request to pict-rs, including the proxy parameter which causes pict-rs to fetch arbitrary URLs. This issue has been patched in version 0.19.16.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
lemmy_routescrates.io | < 0.19.16 | 0.19.16 |
Affected products
1Patches
1f47a03f56d17[v0.19] Adding tests for image file_type (#6368)
1 file changed · +54 −6
crates/routes/src/images.rs+54 −6 modified@@ -24,7 +24,7 @@ use lemmy_utils::{ }; use reqwest::Body; use reqwest_middleware::{ClientWithMiddleware, RequestBuilder}; -use serde::{Deserialize, Serialize}; +use serde::Deserialize; use std::{str::FromStr, time::Duration}; use strum::{Display, EnumString}; use url::Url; @@ -61,7 +61,7 @@ impl ProcessUrl for PictrsGetParams { format!("{}image/original/{}", pictrs_url, src) } else { // Take file type from name, or jpg if nothing is given - let format = file_type(self.format.clone(), src).unwrap_or(PictrsFileType::Jpg); + let format = file_type(self.format.clone(), src).unwrap_or_default(); let mut url = format!("{}image/process.{}?src={}", pictrs_url, format, src); @@ -73,13 +73,13 @@ impl ProcessUrl for PictrsGetParams { } } -#[derive(EnumString, Display, Debug, Serialize, PartialEq)] -#[serde(rename_all = "snake_case")] -#[strum(ascii_case_insensitive)] +#[derive(EnumString, Display, PartialEq, Debug, Default)] +#[strum(ascii_case_insensitive, serialize_all = "snake_case")] enum PictrsFileType { Apng, Avif, Gif, + #[default] Jpg, Jxl, Png, @@ -108,7 +108,7 @@ impl ProcessUrl for ImageProxyParams { format!("{}image/original?proxy={}", pictrs_url, proxy_url) } else { // Take file type from name, or jpg if nothing is given - let format = file_type(self.format.clone(), proxy_url).unwrap_or(PictrsFileType::Jpg); + let format = file_type(self.format.clone(), proxy_url).unwrap_or_default(); let mut url = format!("{}image/process.{}?proxy={}", pictrs_url, format, proxy_url); @@ -345,3 +345,51 @@ where std::pin::Pin::new(&mut self.rx).poll_recv(cx) } } + +#[cfg(test)] +mod tests { + use crate::images::{file_type, PictrsFileType}; + use lemmy_utils::error::LemmyResult; + + #[tokio::test] + async fn image_file_type_tests() -> LemmyResult<()> { + // Make sure files type outputs are getting lower-cased + assert_eq!(PictrsFileType::Jpg.to_string(), "jpg".to_string()); + + let file_url = "a8a7f07f-3ef2-40fa-849c-ae952f68f3ec.jpg"; + + // Make sure wrong-cased file type requests are okay + assert_eq!( + PictrsFileType::Jpg, + file_type(Some("JPg".to_string()), file_url)? + ); + + // Make sure wrong file type requests are okay with unwrap_or_default + assert_eq!( + PictrsFileType::Jpg, + file_type(Some("jpeg".to_string()), file_url).unwrap_or_default() + ); + assert_eq!( + PictrsFileType::Jpg, + file_type(Some("nonsense".to_string()), file_url).unwrap_or_default() + ); + + // Make sure missing file type requests are okay + assert_eq!(PictrsFileType::Jpg, file_type(None, file_url)?); + + // jpeg + let file_url = "a8a7f07f-3ef2-40fa-849c-ae952f68f3ec.jpeg"; + + // Make sure jpeg one is okay + assert_eq!( + PictrsFileType::Jpg, + file_type(None, file_url).unwrap_or_default() + ); + + // Make sure proxy ones are okay + let proxy_url = "https://test.tld/pictrs/image/6d3b2f3f-7b29-4d9a-868e-b269423f4d6c.WEbP"; + assert_eq!(PictrsFileType::Webp, file_type(None, proxy_url)?); + + Ok(()) + } +}
Vulnerability mechanics
Synthesis attempt was rejected by the grounding validator. Re-run pending.
References
4News mentions
0No linked articles in our index yet.