diff --git a/pumpkin-plugin-api/src/permissions.rs b/pumpkin-plugin-api/src/permissions.rs index e9b64ab4d..84c1b82e4 100644 --- a/pumpkin-plugin-api/src/permissions.rs +++ b/pumpkin-plugin-api/src/permissions.rs @@ -34,16 +34,14 @@ pub const NETWORK_OUTBOUND: &str = "network.outbound"; /// This is separate from `network.outbound`. This allows the use of `wasi:http`; the other allows the more powerful `wasi:sockets`. pub const HTTP_OUTBOUND: &str = "http.outbound"; -/// Allows the plugin to read files from the server's file system outside of its data folder. -pub const FS_READ: &str = "fs.read"; - -/// Allows the plugin to write files to the server's file system outside of its data folder. -pub const FS_WRITE: &str = "fs.write"; - -/// Allows the plugin to read files within its own data folder (`plugins/`). +/// Allows the plugin to read files within its own data folder (`plugins/data/`). pub const FS_READ_DATA: &str = "fs.read.data"; -/// Allows the plugin to write files within its own data folder (`plugins/`). +/// Allows the plugin to write files within its own data folder (`plugins/data/`). +/// +/// Note that even without `FS_READ_DATA`, this will allow the plugin to +/// inspect (e.g. list) the contents of the directory. But it will block +/// reading any file's contents. pub const FS_WRITE_DATA: &str = "fs.write.data"; /// Allows the plugin to read all environment variables. diff --git a/pumpkin/src/plugin/api/context.rs b/pumpkin/src/plugin/api/context.rs index 3a21b2fc6..96ef6268f 100644 --- a/pumpkin/src/plugin/api/context.rs +++ b/pumpkin/src/plugin/api/context.rs @@ -80,7 +80,7 @@ impl Context { /// A string representing the path to the data folder. #[must_use] pub fn get_data_folder(&self) -> PathBuf { - let path = Path::new("plugins").join(&self.metadata.name); + let path = Path::new("plugins").join("data").join(&self.metadata.name); if !path.exists() { fs::create_dir_all(&path).unwrap(); } diff --git a/pumpkin/src/plugin/loader/wasm/wasm_host/mod.rs b/pumpkin/src/plugin/loader/wasm/wasm_host/mod.rs index f846666e3..3f6fc81f4 100644 --- a/pumpkin/src/plugin/loader/wasm/wasm_host/mod.rs +++ b/pumpkin/src/plugin/loader/wasm/wasm_host/mod.rs @@ -2,7 +2,7 @@ use std::{fs, path::Path, sync::Arc}; use thiserror::Error; use tokio::sync::Mutex; use wasmtime::{Cache, CacheConfig, Engine, Store, component::Component, component::Linker}; -use wasmtime_wasi::{WasiCtxBuilder, sockets::SocketAddrUse}; +use wasmtime_wasi::{DirPerms, FilePerms, WasiCtxBuilder, sockets::SocketAddrUse}; use crate::plugin::{ Context, PluginMetadata, loader::wasm::wasm_host::state::PluginHostState, permissions, @@ -143,7 +143,6 @@ fn load_component( } impl WasmPlugin { - #[expect(clippy::too_many_lines)] pub async fn on_load( &self, context: Arc, @@ -221,48 +220,29 @@ impl WasmPlugin { } } - let data_folder = context.get_data_folder(); - let preopen_path = - if has_permission(permissions::FS_READ) || has_permission(permissions::FS_WRITE) { - Path::new(".") - } else { - data_folder.as_path() - }; - - // Determine permissions for the preopened directory - let (dir_perms, file_perms) = if has_permission(permissions::FS_WRITE) { - ( - wasmtime_wasi::DirPerms::all(), - wasmtime_wasi::FilePerms::all(), - ) - } else if has_permission(permissions::FS_READ) { - ( - wasmtime_wasi::DirPerms::READ, - wasmtime_wasi::FilePerms::READ, - ) - } else { - // Scoped to data folder - let can_write = has_permission(permissions::FS_WRITE_DATA); - if can_write { - ( - wasmtime_wasi::DirPerms::all(), - wasmtime_wasi::FilePerms::all(), - ) - } else { - // Default to READ if no write permission is given for data folder - // (Plugins should at least be able to read their own config) - ( - wasmtime_wasi::DirPerms::READ, - wasmtime_wasi::FilePerms::READ, - ) - } - }; - builder.preopened_dir( - preopen_path, - preopen_path.to_string_lossy(), - dir_perms, - file_perms, + context.get_data_folder(), + "data", + if has_permission(permissions::FS_READ_DATA) + || has_permission(permissions::FS_WRITE_DATA) + { + DirPerms::READ + } else { + DirPerms::empty() + } | if has_permission(permissions::FS_WRITE_DATA) { + DirPerms::MUTATE + } else { + DirPerms::empty() + }, + if has_permission(permissions::FS_READ_DATA) { + FilePerms::READ + } else { + FilePerms::empty() + } | if has_permission(permissions::FS_WRITE_DATA) { + FilePerms::WRITE + } else { + FilePerms::empty() + }, )?; if has_permission(permissions::HTTP_OUTBOUND) { diff --git a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/context.rs b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/context.rs index 0cca512ed..cb218a064 100644 --- a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/context.rs +++ b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/context.rs @@ -419,13 +419,8 @@ impl pumpkin::plugin::context::HostContext for PluginHostState { .await) } - async fn get_data_folder(&mut self, context: Resource) -> wasmtime::Result { - Ok(self - .get_context(&context)? - .provider - .get_data_folder() - .to_string_lossy() - .into_owned()) + async fn get_data_folder(&mut self, _context: Resource) -> wasmtime::Result { + Ok("data".to_string()) } async fn get_server( diff --git a/pumpkin/src/plugin/mod.rs b/pumpkin/src/plugin/mod.rs index b13244b5d..b19ef48b5 100644 --- a/pumpkin/src/plugin/mod.rs +++ b/pumpkin/src/plugin/mod.rs @@ -486,15 +486,13 @@ impl PluginManager { metadata.version.green() ); for permission in &metadata.permissions { - if let Some(description) = permissions::get_permission_description(permission) { - println!( - " - {}: {}", - permission.yellow().bold(), - description.italic() - ); - } else { - println!(" - {}", permission.yellow().bold()); - } + println!( + " - {}: {}", + permission.yellow().bold(), + permissions::get_permission_description(permission) + .unwrap_or("") + .italic() + ); } let prompt = format!( diff --git a/pumpkin/src/plugin/permissions.rs b/pumpkin/src/plugin/permissions.rs index 1b6b906d0..0d41264d1 100644 --- a/pumpkin/src/plugin/permissions.rs +++ b/pumpkin/src/plugin/permissions.rs @@ -38,17 +38,14 @@ pub const NETWORK_OUTBOUND: &str = "network.outbound"; /// This is separate from `network.outbound`. This allows the use of `wasi:http`; the other allows the more powerful `wasi:sockets`. pub const HTTP_OUTBOUND: &str = "http.outbound"; -/// Allows the plugin to read files from the server's file system outside of its data folder. -pub const FS_READ: &str = "fs.read"; - -/// Allows the plugin to write files to the server's file system outside of its data folder. -pub const FS_WRITE: &str = "fs.write"; - -/// Allows the plugin to read files within its own data folder (`plugins/`). -/// This is granted by default if any other FS permission is not specified, but can be explicitly requested. +/// Allows the plugin to read files within its own data folder (`plugins/data/`). pub const FS_READ_DATA: &str = "fs.read.data"; -/// Allows the plugin to write files within its own data folder (`plugins/`). +/// Allows the plugin to write files within its own data folder (`plugins/data/`). +/// +/// Note that even without `FS_READ_DATA`, this will allow the plugin to +/// inspect (e.g. list) the contents of the directory. But it will block +/// reading any file's contents. pub const FS_WRITE_DATA: &str = "fs.write.data"; /// Allows the plugin to read all environment variables. @@ -98,14 +95,13 @@ pub fn get_permission_description(permission: &str) -> Option<&'static str> { HTTP_OUTBOUND => { Some("Allows the plugin to make outbound HTTP requests (through `wasi:http`)") } - FS_READ => Some( - "Allows the plugin to read files from the server's file system outside of its data folder.", - ), - FS_WRITE => Some( - "Allows the plugin to write files to the server's file system outside of its data folder.", - ), FS_READ_DATA => Some("Allows the plugin to read files within its own data folder."), - FS_WRITE_DATA => Some("Allows the plugin to write files within its own data folder."), + FS_WRITE_DATA => Some( + "\ +Allows the plugin to write files within its own data folder. \ +Even without `fs.read.data`, this will allow the plugin to list directory contents \ +(but not read the contents of the discovered files).", + ), SYS_ENV => Some("Allows the plugin to read all environment variables."), SYS_INFO => Some("Allows the plugin to read system information (CPU, Memory, OS)."), SYS_INFO_CPU => Some("Allows the plugin to read CPU information."),