mirror of
https://github.com/TheFunny/TelegramTwitterMediaBot.git
synced 2026-10-16 03:02:16 +00:00
fix: admit adapter temp media in the local-path guard
This commit is contained in:
@@ -7,3 +7,23 @@ pub mod site;
|
||||
/// destructors), and without a marker the only safe assumption about the OS
|
||||
/// temp directory is "not mine".
|
||||
pub const TEMP_FILE_PREFIX: &str = "tgxmb-";
|
||||
|
||||
/// True when `path` is a file inside a temp directory this project created: the
|
||||
/// directory's name carries [`TEMP_FILE_PREFIX`] and it sits directly under the
|
||||
/// OS temp directory (`tempfile`'s default location). The prefix lives on the
|
||||
/// directory, not the file, so checking the file name is not enough; requiring
|
||||
/// the parent to be the temp root stops an upstream-supplied bare path that
|
||||
/// merely contains a `tgxmb-` component from passing.
|
||||
pub fn is_own_temp_file(path: &std::path::Path) -> bool {
|
||||
let Some(dir) = path.parent().and_then(|dir| dir.canonicalize().ok()) else {
|
||||
return false;
|
||||
};
|
||||
let Ok(root) = std::env::temp_dir().canonicalize() else {
|
||||
return false;
|
||||
};
|
||||
dir.parent() == Some(root.as_path())
|
||||
&& dir
|
||||
.file_name()
|
||||
.and_then(|name| name.to_str())
|
||||
.is_some_and(|name| name.starts_with(TEMP_FILE_PREFIX))
|
||||
}
|
||||
|
||||
@@ -186,10 +186,7 @@ pub(super) fn media_to_payload(media: &Media, sensitive: bool) -> Option<MediaIt
|
||||
let url = media.url();
|
||||
// Only adapter-produced temp files may be non-HTTP. A bare path from an
|
||||
// upstream JSON field must never reach InputFile::file().
|
||||
let is_local_temp = std::path::Path::new(url)
|
||||
.file_name()
|
||||
.and_then(|name| name.to_str())
|
||||
.is_some_and(|name| name.starts_with(x_media::TEMP_FILE_PREFIX));
|
||||
let is_local_temp = x_media::is_own_temp_file(std::path::Path::new(url));
|
||||
if !url.starts_with("http://") && !url.starts_with("https://") && !is_local_temp {
|
||||
log::warn!("dropping media with a non-URL upstream path");
|
||||
return None;
|
||||
@@ -832,6 +829,46 @@ mod tests {
|
||||
assert!(media_to_payload(&media, false).is_none());
|
||||
}
|
||||
|
||||
/// Adapter output (bsky remux, pixiv ugoira) lives at `tgxmb-XXXX/video.mp4`
|
||||
/// under the OS temp dir: the prefix is on the directory, so it must pass.
|
||||
#[test]
|
||||
fn media_to_payload_accepts_an_adapter_temp_video() {
|
||||
let dir = tempfile::Builder::new()
|
||||
.prefix(x_media::TEMP_FILE_PREFIX)
|
||||
.tempdir()
|
||||
.unwrap();
|
||||
let path = dir.path().join("video.mp4");
|
||||
std::fs::write(&path, b"mp4").unwrap();
|
||||
let media = Media::Video {
|
||||
url: path.to_string_lossy().into_owned(),
|
||||
thumbnail_url: String::new(),
|
||||
};
|
||||
assert!(matches!(
|
||||
media_to_payload(&media, false),
|
||||
Some(MediaItemPayload::Video {
|
||||
media: MediaRef::Source(_),
|
||||
..
|
||||
})
|
||||
));
|
||||
}
|
||||
|
||||
/// A `tgxmb-` directory outside the OS temp dir is an upstream-chosen path,
|
||||
/// not one of ours, and must still be dropped.
|
||||
#[test]
|
||||
fn media_to_payload_rejects_a_tgxmb_dir_outside_the_temp_root() {
|
||||
let dir = tempfile::Builder::new()
|
||||
.prefix(x_media::TEMP_FILE_PREFIX)
|
||||
.tempdir_in(std::env::current_dir().unwrap())
|
||||
.unwrap();
|
||||
let path = dir.path().join("video.mp4");
|
||||
std::fs::write(&path, b"mp4").unwrap();
|
||||
let media = Media::Video {
|
||||
url: path.to_string_lossy().into_owned(),
|
||||
thumbnail_url: String::new(),
|
||||
};
|
||||
assert!(media_to_payload(&media, false).is_none());
|
||||
}
|
||||
|
||||
/// The caption-quote threshold matches the post's text inside the caption,
|
||||
/// so a long-text cache hit is quoted and a short-text one is not.
|
||||
#[tokio::test]
|
||||
|
||||
Reference in New Issue
Block a user