From 50852ceff110c250ae450631837a78150fce3e99 Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Tue, 3 Nov 2026 15:48:03 +0000 Subject: [PATCH] ui-egui: embed craft-fonts files only from inside the checkout The build hook canonicalised each manifest row's file and embedded it with include_bytes! wherever it led, so an absolute path, a .. path or a symlink in a craft-fonts checkout could put any readable file of the build machine into the binary. Manifests and font files must now resolve inside the canonical checkout directory. --- crates/ui-egui/build.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/crates/ui-egui/build.rs b/crates/ui-egui/build.rs index 0c5dbf3e8..039b8b064 100644 --- a/crates/ui-egui/build.rs +++ b/crates/ui-egui/build.rs @@ -76,7 +76,14 @@ fn emit_craft_fonts() { /// One `CraftFont { .. }` initialiser per manifest row GridCraft may embed. fn craft_fonts(dir: &std::path::Path, wasm: bool, required: bool) -> Result { - let manifest = dir.join("fonts/manifest.txt"); + // Every file embedded comes from inside the checkout: a manifest row (or a symlink) naming a + // path outside it would put that file of the build machine into the binary. + let root = dir.canonicalize().map_err(|e| format!("{}: {e}", dir.display()))?; + let inside = |path: std::path::PathBuf| -> Result { + let real = path.canonicalize().map_err(|e| format!("{}: {e}", path.display()))?; + if real.starts_with(&root) { Ok(real) } else { Err(format!("{} is outside the craft-fonts checkout {}", path.display(), root.display())) } + }; + let manifest = inside(dir.join("fonts/manifest.txt"))?; println!("cargo::rerun-if-changed={}", manifest.display()); let text = std::fs::read_to_string(&manifest).map_err(|e| format!("{}: {e}", manifest.display()))?; let rows = craft_fonts_policy::parse_manifest(&text)?; @@ -84,7 +91,7 @@ fn craft_fonts(dir: &std::path::Path, wasm: bool, required: bool) -> Result Result = row.scripts.iter().map(|s| format!("{s:?}")).collect(); let family = &row.family; @@ -112,6 +119,10 @@ fn craft_fonts(dir: &std::path::Path, wasm: bool, required: bool) -> Result Result, String> { let manifest = dir.join("fonts/simplified-chinese-manifest.txt"); println!("cargo::rerun-if-changed={}", manifest.display()); + // `dir` is the canonical checkout: the manifest itself must not lead out of it either. + if manifest.canonicalize().is_ok_and(|real| !real.starts_with(dir)) { + return Err(format!("{} is outside the craft-fonts checkout", manifest.display())); + } let optional = |e: String| -> Result, String> { if required { Err(format!("{}: {e}", manifest.display()))