MCP: an agent's folders hold for file loads through the app and for a design's font files (#1112)
doc.open and the STEP/mesh imports run on the file loader's worker with the request's path scope, and a font named by file path in a design is checked against the scope before it is read.
This commit is contained in:
1 parent
6de8f6db95
commit
c469adf456
8 files changed
+122
-30
No files matched your search
@@ -186,11 +186,8 @@ fn text_source(p: &Value, cmd: &str, old: Option<(String, Vec2, f64, f64, Option
|
||||
Some(_) => return Err(bad(cmd, "`font` must be a font family name or a font file path")),
|
||||
};
|
||||
let style = text_style(p, cmd, s0)?;
|
||||
// A font file an agent names must lie in its folders (installed families are found by name).
|
||||
if let Some(path) = font.as_deref().and_then(solidcraft_sketch::font_file_path) {
|
||||
solidcraft_io::vfs::check(&path.to_string_lossy()).map_err(|e| bad(cmd, e.to_string()))?;
|
||||
}
|
||||
// An unknown font, an unreadable file or a style the font lacks is an error now, not a lost link later.
|
||||
// An unknown font, an unreadable file or a style the font lacks is an error now, not a lost
|
||||
// link later; so is a font file outside an agent's folders (the font reader asks `vfs`).
|
||||
solidcraft_sketch::font_data_styled(font.as_deref(), style.bold, style.italic).map_err(|e| bad(cmd, e.to_string()))?;
|
||||
Ok(LinkSource::Text { text, at, height, angle, font, style })
|
||||
}
|
||||
|
||||
@@ -64,3 +64,32 @@ fn file_commands_stay_inside_the_agents_folders() {
|
||||
s.execute("file.export", &json!({"path": at(&out, "x.stl")})).unwrap();
|
||||
let _ = std::fs::remove_dir_all(&base);
|
||||
}
|
||||
|
||||
/// A design names the font file of its text, and opening it reads that file: for an agent, a
|
||||
/// font outside its folders stays unread, even one already read (and cached) for the user.
|
||||
#[test]
|
||||
fn a_designs_font_file_stays_inside_the_agents_folders() {
|
||||
let base = solidcraft_io::vfs::temp_dir().join(format!("solidcraft-font-scope-{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&base);
|
||||
let (root, out) = (base.join("root"), base.join("out"));
|
||||
std::fs::create_dir_all(&root).unwrap();
|
||||
std::fs::create_dir_all(&out).unwrap();
|
||||
let font = out.join("f.ttf");
|
||||
std::fs::write(&font, solidcraft_sketch::font_data(None).unwrap().bytes()).unwrap();
|
||||
let mut s = Session::default();
|
||||
s.execute("sketch.create", &json!({"plane": "XY"})).unwrap();
|
||||
s.execute("sketch.text", &json!({"text": "A", "at": [0, 0], "font": font.to_string_lossy()})).unwrap();
|
||||
s.execute("sketch.finish", &json!({})).unwrap();
|
||||
let design = root.join("text.solidcraft");
|
||||
std::fs::write(&design, solidcraft_io::write_design(&s.doc)).unwrap();
|
||||
let open = json!({"path": design.to_string_lossy()});
|
||||
// The sketch's warning says what its text couldn't read.
|
||||
let warnings = |s: &Session| s.model.results.iter().filter_map(|r| r.warning.clone()).collect::<Vec<_>>().join("; ");
|
||||
s.execute("doc.open", &open).unwrap();
|
||||
assert!(warnings(&s).is_empty(), "the user's own open reads it: {}", warnings(&s));
|
||||
scoped(Some(PathScope::new([root.clone()])), || {
|
||||
s.execute("doc.open", &open).unwrap();
|
||||
assert!(warnings(&s).contains("outside the folders this agent may use"), "{}", warnings(&s));
|
||||
});
|
||||
let _ = std::fs::remove_dir_all(&base);
|
||||
}
|
||||
Reference in new issue
Block a user