diff --git a/crates/x-media/src/site/bsky/interface.rs b/crates/x-media/src/site/bsky/interface.rs index c8ed335..606d753 100644 --- a/crates/x-media/src/site/bsky/interface.rs +++ b/crates/x-media/src/site/bsky/interface.rs @@ -1,7 +1,7 @@ use super::model; use crate::media::Media; use crate::site::{FetchError, Fetched}; -use html_escape::encode_text; +use html_escape::{encode_double_quoted_attribute, encode_text}; use regex::Regex; use std::sync::LazyLock; @@ -224,8 +224,8 @@ impl Post { pub fn caption(&self) -> String { format!( "{url}\n{author}: {text}", - url = self.url(), - author_url = self.author_url(), + url = encode_double_quoted_attribute(&self.url()), + author_url = encode_double_quoted_attribute(&self.author_url()), author = encode_text(&self.author), text = encode_text(&self.text), ) diff --git a/crates/x-media/src/site/pixiv/interface.rs b/crates/x-media/src/site/pixiv/interface.rs index 506bced..6a68680 100644 --- a/crates/x-media/src/site/pixiv/interface.rs +++ b/crates/x-media/src/site/pixiv/interface.rs @@ -1,7 +1,7 @@ use super::model::{IllustrationModel, TypeModel}; use crate::media::Media; use crate::site::{FetchError, Fetched}; -use html_escape::encode_text; +use html_escape::{encode_double_quoted_attribute, encode_text}; use regex::Regex; use std::sync::LazyLock; @@ -48,9 +48,9 @@ impl Illustration { pub fn caption(&self) -> String { format!( "{title} / {author}\n{tags}", - url = self.url(), + url = encode_double_quoted_attribute(&self.url()), title = encode_text(&self.title), - author_url = self.author_url(), + author_url = encode_double_quoted_attribute(&self.author_url()), author = encode_text(&self.author), tags = encode_text( &self diff --git a/crates/x-media/src/site/twitter/interface.rs b/crates/x-media/src/site/twitter/interface.rs index 70baf87..90b2829 100644 --- a/crates/x-media/src/site/twitter/interface.rs +++ b/crates/x-media/src/site/twitter/interface.rs @@ -1,7 +1,7 @@ use super::model; use crate::media::Media; use crate::site::{FetchError, Fetched}; -use html_escape::encode_text; +use html_escape::{encode_double_quoted_attribute, encode_text}; use regex::Regex; use std::sync::LazyLock; @@ -48,7 +48,9 @@ pub async fn fetch_from_url(url: &str) -> Result { fn empty_fetched(url: &str) -> Fetched { Fetched { source_url: url.to_string(), - caption: url.to_string(), + // The raw user-supplied URL goes into an HTML caption; escape it so + // crafted links cannot break the parse (Telegram 400). + caption: encode_text(url).into_owned(), title: String::new(), media: vec![], sensitive: true, @@ -151,8 +153,8 @@ impl Tweet { pub fn caption(&self) -> String { format!( "{url}\n{author}: {text}", - url = self.url(), - author_url = self.author_url(), + url = encode_double_quoted_attribute(&self.url()), + author_url = encode_double_quoted_attribute(&self.author_url()), author = encode_text(&self.author), text = encode_text(&self.text), ) diff --git a/crates/xmedia-bot/src/handlers.rs b/crates/xmedia-bot/src/handlers.rs index e695dee..3bdf887 100644 --- a/crates/xmedia-bot/src/handlers.rs +++ b/crates/xmedia-bot/src/handlers.rs @@ -162,7 +162,7 @@ async fn edit_message_handler(bot: &Bot, message: &Message) -> bool { }; let link = format!( "{1}", - edit.url, + html_escape::encode_double_quoted_attribute(&edit.url), html_escape::encode_text(text) ); let new_text = if edit.template.is_empty() {