Plug-ins: an update the store can't take keeps the stored grant

This commit is contained in:
dotCooCoo
2026-11-08 12:24:12 -08:00
parent b33b452539
commit c396ad104a
2 changed files with 37 additions and 3 deletions
+7 -3
View File
@@ -437,13 +437,17 @@ fn install(s: &mut Session, p: &Value) -> Result<Value> {
),
));
}
if m.asks_permissions() {
let g = Grant { id: m.id.clone(), hash: plugin.hash().to_string(), permissions: wanted };
let new_grant = m.asks_permissions().then(|| Grant { id: m.id.clone(), hash: plugin.hash().to_string(), permissions: wanted });
if let Some(g) = &new_grant {
registry::grant(g.clone());
s.store_grant(&m.id, Some(g));
}
// Installed plug-ins stay installed after a restart (#2541).
let stored = s.store_plugin(plugin.id(), &bytes);
// A stored grant names one module: when this one could not replace the stored file, the file
// keeps the grant it has.
if stored && let Some(g) = new_grant {
s.store_grant(&m.id, Some(g));
}
// Installing a disabled plug-in again (an updated version) turns it on for the next launch too,
// also when it is disabled by its stored file alone (it stopped a launch while loading). When
// the new version could not replace the stored file, the old one stays disabled.
+30
View File
@@ -801,6 +801,36 @@ fn a_filter_does_only_what_its_permissions_allow() {
assert_eq!(s.doc().unwrap().doc.plugin_data(stateless), Some(&json!({"pen": 1})));
}
/// When an update can't replace the stored module, the store's grant stays the one for the module
/// it still holds, so that module loads at the next start.
#[test]
fn a_module_the_store_could_not_take_leaves_the_stored_grant() {
let folder = crate::tests_links::Folder::new("plugin-store-failed-update");
let id = "test.engine.permissions.unwritable";
let old = asking(id, r#"["doc.read"]"#, &json!({"objects": []}));
let new = asking(id, r#"["doc.read"]"#, &json!({"objects": [{"name": "x"}]}));
let (old_hash, new_hash) = (vectorcraft_plugins::permissions::sha256_hex(&old), vectorcraft_plugins::permissions::sha256_hex(&new));
let mut s = session();
s.attach_plugin_store(folder.0.clone());
run(&mut s, "plugin.install", json!({"dataBase64": vectorcraft_format::base64_encode(&old), "grant": ["doc.read"]}));
let stored = std::fs::read_dir(&folder.0).unwrap().map(|e| e.unwrap().path()).find(|p| p.extension().is_some_and(|e| e == "wasm")).unwrap();
// A folder under the module's file name can't be replaced by a file.
std::fs::remove_file(&stored).unwrap();
std::fs::create_dir(&stored).unwrap();
run(&mut s, "plugin.install", json!({"dataBase64": vectorcraft_format::base64_encode(&new), "grant": ["doc.read"]}));
let grants = std::fs::read_to_string(folder.0.join("grants.json")).unwrap();
assert!(grants.contains(&old_hash) && !grants.contains(&new_hash), "{grants}");
// With the old module back in that place, the next session loads it.
std::fs::remove_dir(&stored).unwrap();
std::fs::write(&stored, &old).unwrap();
vectorcraft_plugins::registry::remove(id);
vectorcraft_plugins::registry::revoke(id);
let mut next = session();
next.attach_plugin_store(folder.0.clone());
assert_eq!(vectorcraft_plugins::registry::get(id).map(|p| p.hash().to_string()), Some(old_hash));
run(&mut next, "plugin.remove", json!({"id": id}));
}
/// What the user granted is kept in the plug-in store with the module: the next session installs
/// the plug-in again without asking. A stored plug-in that lists permissions nobody granted it is
/// skipped, even when another store granted it, and so is one in the Additional Plug-ins Folder