#3692: font names are debug-quoted in log warnings

Finding 46: install_font logged the caller-supplied file name raw when the
font couldn't be stored, and the desktop log writes line breaks as they
come, so a name could forge extra log records. The name is now logged
with {:?} (escaped and quoted), and so are the font-derived family names in
the two glyph-outline warnings, which come from a document's own fonts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Martin Ball
2026-10-25 21:02:01 -04:00
committed by Dylan M. Taylor
co-authored by Claude Opus 5.5
parent 5155d2c18c
commit 6c6326486f
2 changed files with 42 additions and 3 deletions
+2 -2
View File
@@ -194,13 +194,13 @@ impl FontFace {
self.location(),
HintingOptions { engine: Engine::Interpreter, target: Target::Mono },
)
.map_err(|e| log::warn!("Cannot initialize outline interpreter for {}: {e}", self.family))
.map_err(|e| log::warn!("Cannot initialize outline interpreter for {:?}: {e}", self.family))
.ok()
});
let settings =
hinter.as_ref().map_or_else(|| DrawSettings::unhinted(Size::unscaled(), self.location()), |h| DrawSettings::hinted(h, false));
if let Err(e) = g.draw(settings, &mut pen) {
log::warn!("Cannot draw glyph {gid} in {}: {e}", self.family);
log::warn!("Cannot draw glyph {gid} in {:?}: {e}", self.family);
return BezPath::new();
}
}
+40 -1
View File
@@ -366,7 +366,8 @@ impl FontDb {
None
}
Err(e) => {
log::warn!("couldn't store the font {name} in {}: {e}", dir.display());
// Debug-quoted: a line break in the supplied name must not start a forged log record.
log::warn!("couldn't store the font {name:?} in {}: {e}", dir.display());
Some(format!("added for this session only: couldn't store it in {}: {e}", dir.display()))
}
},
@@ -903,6 +904,44 @@ pub(crate) mod tests {
let _ = std::fs::remove_dir_all(&dir);
}
/// The messages logged while this test binary runs.
fn logged() -> &'static std::sync::Mutex<Vec<String>> {
static LOG: std::sync::OnceLock<std::sync::Mutex<Vec<String>>> = std::sync::OnceLock::new();
struct Capture;
impl log::Log for Capture {
fn enabled(&self, _: &log::Metadata) -> bool {
true
}
fn log(&self, r: &log::Record) {
if let Some(l) = LOG.get() {
l.lock().unwrap().push(r.args().to_string());
}
}
fn flush(&self) {}
}
LOG.get_or_init(|| {
let _ = log::set_logger(&Capture);
log::set_max_level(log::LevelFilter::Warn);
std::sync::Mutex::new(Vec::new())
})
}
#[test]
fn a_supplied_name_is_logged_escaped() {
// #3692: a line break in the name must not start a forged record in the log file.
let log = logged();
let not_a_folder = temp_dir("not-a-folder");
std::fs::write(&not_a_folder, b"x").unwrap();
let db = db();
db.load_user_fonts(&not_a_folder);
let r = db.install_font("a\nERROR forged record.ttf", &renamed("SourceSans3-Regular.ttf", FAMILY)).unwrap();
assert!(r.warning.is_some(), "the font can't be stored");
let lines = log.lock().unwrap();
let line = lines.iter().find(|l| l.contains("forged record")).unwrap();
assert!(!line.contains('\n'), "{line}");
let _ = std::fs::remove_file(&not_a_folder);
}
#[test]
fn stored_names_are_safe_and_unique() {
let ttf = renamed("SourceSans3-Regular.ttf", FAMILY);