mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-14 19:26:56 -04:00
Fix #832: --automation-read-root and --automation-write-root confine the MCP server's files
vectorcraft-cli mcp and vectorcraft --control take the flags PhotoCraft takes. While roots are in force, every path a command reads (open, place, relink, SVG image links, library and profile loads, plug-ins, preset imports, font searches) must lie inside the read root and every path it writes (save, export, package, Export for Screens and for Web, print to file, captures, library saves) inside the write root. Paths are made absolute, links are followed (a missing file's nearest folder's), and the result is compared by whole components, ignoring case on Windows; `..` below a missing folder, dangling links and device names are refused. A root left out grants none of its access; without the flags nothing changes. The checks live in one place, vectorcraft_engine::file_access, consulted by the engine's file helpers and the app's file services. The headless server confines its whole process; the app confines control requests, the frames carrying input they inject and the background saves they start, leaving the person at the keyboard and the app's own folders alone. The screenshot tool now has the backend write its file. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
ab69087d68
commit
9f875050ef
32 files changed
+1353
-136
No files matched your search
@@ -95,6 +95,13 @@ pub struct FolderReport {
|
||||
/// reported and skipped; the others still load.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn load_folder(dir: &std::path::Path) -> Result<FolderReport> {
|
||||
load_folder_where(dir, |_| Ok(()))
|
||||
}
|
||||
|
||||
/// [`load_folder`], loading only the files `allow` accepts; the others are reported as failed
|
||||
/// with its reason.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn load_folder_where(dir: &std::path::Path, allow: impl Fn(&std::path::Path) -> std::result::Result<(), String>) -> Result<FolderReport> {
|
||||
let rd = std::fs::read_dir(dir).map_err(|e| Error::Io(format!("{}: {e}", dir.display())))?;
|
||||
let mut files: Vec<std::path::PathBuf> =
|
||||
rd.filter_map(|e| e.ok().map(|e| e.path())).filter(|p| p.extension().is_some_and(|x| x.eq_ignore_ascii_case("wasm"))).collect();
|
||||
@@ -102,6 +109,10 @@ pub fn load_folder(dir: &std::path::Path) -> Result<FolderReport> {
|
||||
files.truncate(1000);
|
||||
let mut report = FolderReport::default();
|
||||
for f in files {
|
||||
if let Err(why) = allow(&f) {
|
||||
report.failed.push((f.display().to_string(), why));
|
||||
continue;
|
||||
}
|
||||
match load_file(&f, Limits::default()) {
|
||||
Ok(p) => report.loaded.push(install(p).id().to_string()),
|
||||
Err(e) => report.failed.push((f.display().to_string(), e.to_string())),
|
||||
|
||||
Reference in new issue
Block a user