Compare commits

...
Author SHA1 Message Date
Dylan M. Taylor a2bdea55ce address review: register the manifest path before validation and keep the Chinese manifest fallback optional 2026-11-04 01:44:41 +00:00
Dylan M. Taylor 50852ceff1 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.
2026-11-03 15:48:03 +00:00
+21 -3
View File
@@ -76,7 +76,21 @@ 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<String, String> {
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<std::path::PathBuf, String> {
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())) }
};
// Registered before validation, by the path as given (and its folder, to see the manifest appear
// or a symlink replaced): a build that skipped the fonts must rebuild once they are there.
let manifest_path = dir.join("fonts/manifest.txt");
println!("cargo::rerun-if-changed={}", manifest_path.display());
if let Some(parent) = manifest_path.parent() {
println!("cargo::rerun-if-changed={}", parent.display());
}
let manifest = inside(manifest_path)?;
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 +98,7 @@ fn craft_fonts(dir: &std::path::Path, wasm: bool, required: bool) -> Result<Stri
if required {
craft_fonts_policy::check_required(&rows)?;
}
let chinese = if wasm { Vec::new() } else { chinese_font_rows(dir, required)? };
let chinese = if wasm { Vec::new() } else { chinese_font_rows(&root, required)? };
for row in rows.iter().chain(&chinese) {
if craft_fonts_policy::barred(&row.family) {
println!("cargo::warning=craft-fonts: not embedding {} {} (repository asset policy)", row.family, row.style);
@@ -92,7 +106,7 @@ fn craft_fonts(dir: &std::path::Path, wasm: bool, required: bool) -> Result<Stri
}
let mut out = String::new();
for row in craft_fonts_policy::embeddable(&rows, wasm).into_iter().chain(craft_fonts_policy::embeddable(&chinese, wasm)) {
let path = dir.join(&row.file).canonicalize().map_err(|e| format!("{}: {e}", row.file))?;
let path = inside(root.join(&row.file)).map_err(|e| format!("{}: {e}", row.file))?;
println!("cargo::rerun-if-changed={}", path.display());
let scripts: Vec<String> = row.scripts.iter().map(|s| format!("{s:?}")).collect();
let family = &row.family;
@@ -120,6 +134,10 @@ fn chinese_font_rows(dir: &std::path::Path, required: bool) -> Result<Vec<craft_
Ok(Vec::new())
}
};
// `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 optional(format!("{} is outside the craft-fonts checkout", manifest.display()));
}
match std::fs::read_to_string(&manifest) {
Ok(text) => match craft_fonts_policy::parse_manifest(&text).and_then(|rows| {
craft_fonts_policy::check_declared_scripts(&rows)?;