diff --git a/crates/engine/tests/plugin_safe_mode.rs b/crates/engine/tests/plugin_safe_mode.rs index 7fe54fb53..1f49fea15 100644 --- a/crates/engine/tests/plugin_safe_mode.rs +++ b/crates/engine/tests/plugin_safe_mode.rs @@ -217,6 +217,37 @@ fn a_disabled_plug_in_whose_new_grant_cant_be_stored_stays_disabled() { let _ = std::fs::remove_dir_all(&dir); } +/// A stored module that lists no permissions and isn't read, because it is disabled or Safe Mode is +/// on, leaves the grant for its id in the store. The load that reads it forgets the grant. +#[test] +fn a_stored_module_that_is_not_read_keeps_the_grant_for_its_id() { + let _f = fresh(); + let dir = tmp_dir("unread-grant"); + let id = "test.sm.unread"; + let mut s = launch(&dir); + s.execute("plugin.install", &json!({"dataBase64": vectorcraft_format::base64_encode(&filter(id))})).unwrap(); + let grants = dir.join("grants.json"); + let entry = serde_json::to_vec(&json!([{"id": id, "hash": "0".repeat(64), "permissions": ["doc.read"]}])).unwrap(); + std::fs::write(&grants, &entry).unwrap(); + + s.execute("plugin.setEnabled", &json!({"id": id, "enabled": false})).unwrap(); + let mut s = launch(&dir); + assert!(!installed(id), "the disabled module is skipped"); + assert_eq!(std::fs::read(&grants).unwrap(), entry, "a disabled module keeps the grant"); + + s.execute("plugin.setEnabled", &json!({"id": id, "enabled": true})).unwrap(); + vectorcraft_engine::cmd::plugin::start_in_safe_mode(); + let mut s = launch(&dir); + assert!(!installed(id), "Safe Mode loads nothing"); + assert_eq!(std::fs::read(&grants).unwrap(), entry, "Safe Mode keeps the grant"); + + safe_mode::set_active(None); + vectorcraft_engine::cmd::plugin::load_prefs_folder(&mut s); + assert!(installed(id)); + assert!(!grants.exists(), "the load that reads the module forgets the grant"); + let _ = std::fs::remove_dir_all(&dir); +} + /// A launch where the stored plug-in `id` stopped while loading: it is installed, then disabled by /// its stored file alone (as Disable and Continue saves it), and the next launch skips it. fn stored_plug_in_disabled_by_file(dir: &std::path::Path, id: &str) -> Session { diff --git a/docs/plugins.md b/docs/plugins.md index 29ab56311..45ef94e16 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -156,8 +156,9 @@ capability model proposed for PhotoCraft plug-ins hexadecimal digits covers nothing. `plugin.remove` forgets the grant, and so does a successful `plugin.install` of a module that lists no permissions under that id, so the earlier bytes need a new approval. A refused install leaves the grant as it is. When such an install can't write the module to the `Plug-ins` folder, the module already stored - there stays and `grants.json` keeps its grant. A stored module that lists no permissions has any grant for - its id forgotten when the `Plug-ins` folder is attached. The Additional Plug-ins Folder + there stays and `grants.json` keeps its grant. When VectorCraft loads the `Plug-ins` folder, it forgets any grant + for an id whose stored module lists no permissions. In Safe Mode, and for a stored module that the user disabled, + VectorCraft does not read the module, and `grants.json` keeps the grant for its id. The Additional Plug-ins Folder and `plugin.reload` do not change the `Plug-ins` folder: when they leave a module that lists no permissions under an id, the process forgets its own grant for that id, and a grant kept in `grants.json` stays with the stored module it covers. An entry of `grants.json` that this version can't use (a permission it doesn't know, an invalid id) is