Merge pull request #6449 from dotCooCoo/fix/script-run-keeps-file-scope

Scripts: a framed run keeps the file-access roots it started under
This commit is contained in:
BFlat
2026-11-11 02:18:21 +07:00
committed by GitHub
2 changed files with 53 additions and 8 deletions
+15 -8
View File
@@ -502,6 +502,8 @@ struct Job {
/// The documents open when the run began, by [`DocState::uid`].
start_uids: Vec<u64>,
name: String,
/// The file-access scope `script.run` came in, which every slice runs in.
scope: crate::file_access::ScopeHandle,
}
/// The `Data` for a JSON value, within `limits`. When it does not fit, the error is the place in
@@ -986,7 +988,8 @@ impl Session {
self.script_report = Some(report);
return Err(EngineError::Other(message));
}
let job = Job { realm, host, world: World::working(self), start_uids: open, name: request.name };
let scope = crate::file_access::ScopeHandle::capture();
let job = Job { realm, host, world: World::working(self), start_uids: open, name: request.name, scope };
let id = NEXT_JOB.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
JOBS.with(|jobs| jobs.borrow_mut().insert(id, job));
self.script_key = Some(JobKey(id));
@@ -1170,20 +1173,24 @@ fn failure_text(report: &Value) -> String {
}
/// A run's slice: the working copy in, the real session lent to the host functions, one measured
/// slice run, and everything back. A panic in the interpreter is the outer `Err`.
/// slice run in the file-access scope the run began in, and everything back. A panic in the
/// interpreter is the outer `Err`.
fn run_slice(s: &mut Session, job: &mut Job) -> std::result::Result<std::result::Result<Progress, RunError>, String> {
let depth = std::mem::replace(&mut s.depth, 0);
job.world.swap(s);
s.script_in_run = true;
let cell = job.host.session.clone();
std::mem::swap(s, &mut *cell.borrow_mut());
let scope = job.scope.clone();
let realm = &mut job.realm;
let r = guard::catch_panic(|| {
#[cfg(test)]
if PANIC_IN_SLICE.with(Cell::get) {
panic!("a slice failed");
}
realm.advance(SLICE_FUEL)
let r = scope.run(|| {
guard::catch_panic(|| {
#[cfg(test)]
if PANIC_IN_SLICE.with(Cell::get) {
panic!("a slice failed");
}
realm.advance(SLICE_FUEL)
})
});
std::mem::swap(s, &mut *cell.borrow_mut());
s.script_in_run = false;
+38
View File
@@ -862,6 +862,44 @@ fn a_file_saved_before_a_failure_stays_written_and_is_named() {
let _ = std::fs::remove_file(&path);
}
/// A run in the app's frames keeps the file-access scope its `script.run` came in, whatever scope
/// the frames that advance it run in.
#[test]
fn a_framed_run_keeps_the_file_access_scope_it_started_in() {
let dir = std::env::temp_dir().join(format!("vc-script-scope-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
let (allowed, outside) = (dir.join("allowed"), dir.join("outside"));
std::fs::create_dir_all(&allowed).unwrap();
std::fs::create_dir_all(&outside).unwrap();
let roots = crate::file_access::AutomationRoots::new(&[&allowed], &[&allowed]).unwrap().unwrap();
let export = |target: &std::path::Path| {
let p = target.to_string_lossy().into_owned();
format!("try {{ vc.run('document.export', {{path: {p:?}, format: 'svg'}}); vc.log('ok'); }} catch (e) {{ vc.log(e.name); }}")
};
let finish = |s: &mut Session| {
loop {
if let Some(r) = s.script_pump() {
return r;
}
}
};
let mut s = two_docs();
s.script_frames = true;
let confined_target = outside.join("confined.svg");
let started = crate::file_access::confine(Some(&roots), || run(&mut s, &export(&confined_target))).unwrap();
assert_eq!(started["running"], true);
let r = crate::file_access::unconfined(|| finish(&mut s)).unwrap();
assert_eq!(r["log"], json!(["Error"]), "a confined caller's run stays confined: {r}");
assert!(!confined_target.exists());
let user_target = outside.join("user.svg");
let started = crate::file_access::unconfined(|| run(&mut s, &export(&user_target))).unwrap();
assert_eq!(started["running"], true);
let r = crate::file_access::confine(Some(&roots), || finish(&mut s)).unwrap();
assert_eq!(r["log"], json!(["ok"]), "a run the person started is not confined by a later caller: {r}");
assert!(user_target.exists());
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn a_committed_run_exports_what_it_drew() {
let path = std::env::temp_dir().join(format!("vc-script-export-{}.svg", std::process::id()));