mirror of
https://github.com/storytold/filmcraft.git
synced 2026-10-24 01:58:19 -06:00
security(text): keep font scans within configured folders (#2876)
* security(text): keep font scans within configured folders * fonts: still find symlinked font files; skip only symlinked folders Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fonts: rustfmt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Brandon Thomas <brandon@storyteller.ai> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
Brandon Thomas
parent
d3ec344691
commit
68cfb0b52d
Generated
+1
@@ -2287,6 +2287,7 @@ dependencies = [
|
||||
"filmcraft-color",
|
||||
"harfrust",
|
||||
"skrifa",
|
||||
"tempfile",
|
||||
"unicode-bidi",
|
||||
"unicode-linebreak",
|
||||
"unicode-segmentation",
|
||||
|
||||
@@ -21,3 +21,6 @@ unicode-bidi = "0.3.18"
|
||||
unicode-linebreak = "0.1.5"
|
||||
# UAX #29 extended grapheme clusters (MIT OR Apache-2.0).
|
||||
unicode-segmentation = "1.13"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.27"
|
||||
|
||||
@@ -114,16 +114,46 @@ fn walk(dir: &std::path::Path, depth: u32, out: &mut Vec<PathBuf>) {
|
||||
let Ok(rd) = std::fs::read_dir(dir) else { return };
|
||||
for e in rd.flatten() {
|
||||
let p = e.path();
|
||||
if p.is_dir() {
|
||||
let Ok(file_type) = e.file_type() else { continue };
|
||||
// Font discovery is bounded to the configured folders: never follow a symlinked folder
|
||||
// into a loop or an unrelated tree. Symlinked font files (NixOS, home-manager, Debian
|
||||
// alternatives) still count.
|
||||
let is_file = if file_type.is_symlink() { std::fs::metadata(&p).is_ok_and(|m| m.is_file()) } else { file_type.is_file() };
|
||||
if file_type.is_dir() {
|
||||
if depth < 4 {
|
||||
walk(&p, depth + 1, out);
|
||||
}
|
||||
} else if p.extension().and_then(|x| x.to_str()).is_some_and(|x| matches!(x.to_ascii_lowercase().as_str(), "ttf" | "otf" | "ttc" | "otc")) {
|
||||
} else if is_file && p.extension().and_then(|x| x.to_str()).is_some_and(|x| matches!(x.to_ascii_lowercase().as_str(), "ttf" | "otf" | "ttc" | "otc")) {
|
||||
out.push(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, unix))]
|
||||
mod directory_tests {
|
||||
use super::walk;
|
||||
|
||||
#[test]
|
||||
fn directory_source_does_not_follow_symlinked_font_trees() {
|
||||
use std::os::unix::fs::symlink;
|
||||
|
||||
let root = tempfile::tempdir().unwrap();
|
||||
let configured = root.path().join("configured");
|
||||
let foreign = root.path().join("foreign");
|
||||
std::fs::create_dir_all(&configured).unwrap();
|
||||
std::fs::create_dir_all(&foreign).unwrap();
|
||||
std::fs::write(configured.join("inside.ttf"), b"").unwrap();
|
||||
std::fs::write(foreign.join("outside.ttf"), b"").unwrap();
|
||||
symlink(&foreign, configured.join("loop")).unwrap();
|
||||
symlink(foreign.join("outside.ttf"), configured.join("linked.ttf")).unwrap();
|
||||
|
||||
let mut found = Vec::new();
|
||||
walk(&configured, 0, &mut found);
|
||||
found.sort();
|
||||
assert_eq!(found, vec![configured.join("inside.ttf"), configured.join("linked.ttf")]);
|
||||
}
|
||||
}
|
||||
|
||||
impl FontSource for DirectorySource {
|
||||
fn faces(&self) -> Vec<FaceInfo> {
|
||||
let mut files = Vec::new();
|
||||
|
||||
Reference in New Issue
Block a user