feat: plugin permission system
This commit is contained in:
parent
6581924760
commit
bdc9b79e42
@ -88,6 +88,7 @@ pub fn plugin_impl(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
|||||||
authors: env!("CARGO_PKG_AUTHORS").split(',').map(String::from).collect(),
|
authors: env!("CARGO_PKG_AUTHORS").split(',').map(String::from).collect(),
|
||||||
description: env!("CARGO_PKG_DESCRIPTION").to_string(),
|
description: env!("CARGO_PKG_DESCRIPTION").to_string(),
|
||||||
dependencies: Vec::new(),
|
dependencies: Vec::new(),
|
||||||
|
permissions: Vec::new(),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -12,6 +12,7 @@ use tracing::{debug, warn};
|
|||||||
pub mod fun;
|
pub mod fun;
|
||||||
pub mod logging;
|
pub mod logging;
|
||||||
pub mod networking;
|
pub mod networking;
|
||||||
|
pub mod plugins;
|
||||||
pub mod recipe;
|
pub mod recipe;
|
||||||
|
|
||||||
pub mod resource_pack;
|
pub mod resource_pack;
|
||||||
@ -22,6 +23,7 @@ pub use networking::auth::AuthenticationConfig;
|
|||||||
pub use networking::compression::CompressionConfig;
|
pub use networking::compression::CompressionConfig;
|
||||||
pub use networking::lan_broadcast::LANBroadcastConfig;
|
pub use networking::lan_broadcast::LANBroadcastConfig;
|
||||||
pub use networking::rcon::RCONConfig;
|
pub use networking::rcon::RCONConfig;
|
||||||
|
pub use plugins::PluginsConfig;
|
||||||
pub use pvp::PVPConfig;
|
pub use pvp::PVPConfig;
|
||||||
pub use server_links::ServerLinksConfig;
|
pub use server_links::ServerLinksConfig;
|
||||||
|
|
||||||
@ -74,6 +76,8 @@ pub struct AdvancedConfiguration {
|
|||||||
pub fun: FunConfig,
|
pub fun: FunConfig,
|
||||||
/// Recipe-related configuration.
|
/// Recipe-related configuration.
|
||||||
pub recipe: RecipeConfig,
|
pub recipe: RecipeConfig,
|
||||||
|
/// Plugin-related configuration.
|
||||||
|
pub plugins: PluginsConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Basic configuration for core server settings.
|
/// Basic configuration for core server settings.
|
||||||
|
|||||||
8
pumpkin-config/src/plugins.rs
Normal file
8
pumpkin-config/src/plugins.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Default)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct PluginsConfig {
|
||||||
|
/// List of permissions that are globally blocked for all plugins.
|
||||||
|
pub blocked_permissions: Vec<String>,
|
||||||
|
}
|
||||||
@ -7,7 +7,7 @@
|
|||||||
//! # Quick start
|
//! # Quick start
|
||||||
//!
|
//!
|
||||||
//! ```rust,ignore
|
//! ```rust,ignore
|
||||||
//! use pumpkin_plugin_api::{Plugin, PluginMetadata, Context, register_plugin};
|
//! use pumpkin_plugin_api::{Plugin, PluginMetadata, Context, register_plugin, permissions::permissions};
|
||||||
//!
|
//!
|
||||||
//! struct MyPlugin;
|
//! struct MyPlugin;
|
||||||
//!
|
//!
|
||||||
@ -20,6 +20,7 @@
|
|||||||
//! authors: vec!["you".into()],
|
//! authors: vec!["you".into()],
|
||||||
//! description: "An example plugin.".into(),
|
//! description: "An example plugin.".into(),
|
||||||
//! dependencies: vec![],
|
//! dependencies: vec![],
|
||||||
|
//! permissions: vec![permissions::NETWORK_DNS.into()],
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
@ -34,6 +35,10 @@ use crate::{
|
|||||||
|
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
pub mod events;
|
pub mod events;
|
||||||
|
/// Constants for plugin permissions.
|
||||||
|
///
|
||||||
|
/// Use these in your `PluginMetadata` to request access to specific host features.
|
||||||
|
pub mod permissions;
|
||||||
pub mod scheduler;
|
pub mod scheduler;
|
||||||
|
|
||||||
pub mod command {
|
pub mod command {
|
||||||
@ -77,6 +82,8 @@ pub struct PluginMetadata {
|
|||||||
pub description: String,
|
pub description: String,
|
||||||
/// The list of plugin dependencies.
|
/// The list of plugin dependencies.
|
||||||
pub dependencies: Vec<String>,
|
pub dependencies: Vec<String>,
|
||||||
|
/// The list of permissions requested by the plugin.
|
||||||
|
pub permissions: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl wit::exports::pumpkin::plugin::metadata::Guest for Component {
|
impl wit::exports::pumpkin::plugin::metadata::Guest for Component {
|
||||||
@ -89,6 +96,7 @@ impl wit::exports::pumpkin::plugin::metadata::Guest for Component {
|
|||||||
authors: metadata.authors,
|
authors: metadata.authors,
|
||||||
description: metadata.description,
|
description: metadata.description,
|
||||||
dependencies: metadata.dependencies,
|
dependencies: metadata.dependencies,
|
||||||
|
permissions: metadata.permissions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
65
pumpkin-plugin-api/src/permissions.rs
Normal file
65
pumpkin-plugin-api/src/permissions.rs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/// Constants for plugin permissions.
|
||||||
|
///
|
||||||
|
/// Use these in your `PluginMetadata` to request access to specific host features.
|
||||||
|
|
||||||
|
/// Allows the plugin to perform DNS resolution.
|
||||||
|
pub const NETWORK_DNS: &str = "network.dns";
|
||||||
|
|
||||||
|
/// Allows the plugin to use TCP sockets.
|
||||||
|
pub const NETWORK_TCP: &str = "network.tcp";
|
||||||
|
|
||||||
|
/// Allows the plugin to use UDP sockets.
|
||||||
|
pub const NETWORK_UDP: &str = "network.udp";
|
||||||
|
|
||||||
|
/// Allows the plugin to initiate TCP connections.
|
||||||
|
pub const NETWORK_TCP_CONNECT: &str = "network.tcp.connect";
|
||||||
|
|
||||||
|
/// Allows the plugin to bind TCP listeners (accept inbound connections).
|
||||||
|
pub const NETWORK_TCP_BIND: &str = "network.tcp.bind";
|
||||||
|
|
||||||
|
/// Allows the plugin to send and receive UDP packets to specific destinations.
|
||||||
|
pub const NETWORK_UDP_CONNECT: &str = "network.udp.connect";
|
||||||
|
|
||||||
|
/// Allows the plugin to bind UDP sockets to local ports.
|
||||||
|
pub const NETWORK_UDP_BIND: &str = "network.udp.bind";
|
||||||
|
|
||||||
|
/// Allows the plugin to send datagram on non-connected UDP socket.
|
||||||
|
pub const NETWORK_UDP_OUTGOING_DATAGRAM: &str = "network.udp.outgoingdatagram";
|
||||||
|
|
||||||
|
/// Restricts all networking permissions to loopback addresses (localhost) only.
|
||||||
|
pub const NETWORK_LOOPBACK: &str = "network.loopback";
|
||||||
|
|
||||||
|
/// Allows the plugin to make outbound TCP/UDP connections.
|
||||||
|
/// **Warning:** This is a powerful permission.
|
||||||
|
pub const NETWORK_OUTBOUND: &str = "network.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/<name>`).
|
||||||
|
pub const FS_READ_DATA: &str = "fs.read.data";
|
||||||
|
|
||||||
|
/// Allows the plugin to write files within its own data folder (`plugins/<name>`).
|
||||||
|
pub const FS_WRITE_DATA: &str = "fs.write.data";
|
||||||
|
|
||||||
|
/// Allows the plugin to read all environment variables.
|
||||||
|
pub const SYS_ENV: &str = "sys.env";
|
||||||
|
|
||||||
|
/// Allows the plugin to read specific environment variables.
|
||||||
|
/// Used with a prefix like "sys.env.PATH".
|
||||||
|
pub const SYS_ENV_PREFIX: &str = "sys.env.";
|
||||||
|
|
||||||
|
/// Allows the plugin to read system information (CPU, Memory, OS).
|
||||||
|
pub const SYS_INFO: &str = "sys.info";
|
||||||
|
|
||||||
|
/// Allows the plugin to read CPU information.
|
||||||
|
pub const SYS_INFO_CPU: &str = "sys.info.cpu";
|
||||||
|
|
||||||
|
/// Allows the plugin to read RAM information.
|
||||||
|
pub const SYS_INFO_RAM: &str = "sys.info.ram";
|
||||||
|
|
||||||
|
/// Allows the plugin to read OS information.
|
||||||
|
pub const SYS_INFO_OS: &str = "sys.info.os";
|
||||||
@ -11,6 +11,8 @@ interface metadata {
|
|||||||
description: string,
|
description: string,
|
||||||
/// Plugin dependencies.
|
/// Plugin dependencies.
|
||||||
dependencies: list<string>,
|
dependencies: list<string>,
|
||||||
|
/// Permissions requested by the plugin.
|
||||||
|
permissions: list<string>,
|
||||||
}
|
}
|
||||||
|
|
||||||
get-metadata: func() -> plugin-metadata;
|
get-metadata: func() -> plugin-metadata;
|
||||||
|
|||||||
@ -23,6 +23,15 @@ interface server {
|
|||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
record sys-info {
|
||||||
|
cpu-count: option<u32>,
|
||||||
|
total-memory: option<u64>,
|
||||||
|
used-memory: option<u64>,
|
||||||
|
os-name: option<string>,
|
||||||
|
os-version: option<string>,
|
||||||
|
pumpkin-version: string,
|
||||||
|
}
|
||||||
|
|
||||||
variant command-sender {
|
variant command-sender {
|
||||||
console,
|
console,
|
||||||
player(player),
|
player(player),
|
||||||
@ -30,6 +39,10 @@ interface server {
|
|||||||
|
|
||||||
/// Represents the global server instance.
|
/// Represents the global server instance.
|
||||||
resource server {
|
resource server {
|
||||||
|
/// Returns system information (CPU, Memory, OS).
|
||||||
|
/// Fields are returned only if the plugin has the corresponding permissions.
|
||||||
|
get-sys-info: func() -> sys-info;
|
||||||
|
|
||||||
/// Returns the current difficulty level of the server.
|
/// Returns the current difficulty level of the server.
|
||||||
get-difficulty: func() -> difficulty;
|
get-difficulty: func() -> difficulty;
|
||||||
|
|
||||||
|
|||||||
@ -66,6 +66,11 @@ impl Context {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn get_metadata(&self) -> &PluginMetadata {
|
||||||
|
&self.metadata
|
||||||
|
}
|
||||||
|
|
||||||
/// Retrieves the data folder path for the plugin, creating it if it does not exist.
|
/// Retrieves the data folder path for the plugin, creating it if it does not exist.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
|
|||||||
@ -24,6 +24,8 @@ pub struct PluginMetadata {
|
|||||||
pub description: String,
|
pub description: String,
|
||||||
/// The dependencies of the plugin.
|
/// The dependencies of the plugin.
|
||||||
pub dependencies: Vec<String>,
|
pub dependencies: Vec<String>,
|
||||||
|
/// The permissions requested by the plugin.
|
||||||
|
pub permissions: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This type represents a future for the plugin.
|
/// This type represents a future for the plugin.
|
||||||
|
|||||||
@ -2,9 +2,11 @@ use std::{fs, path::Path, sync::Arc};
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use wasmtime::{Cache, CacheConfig, Engine, Store, component::Component, component::Linker};
|
use wasmtime::{Cache, CacheConfig, Engine, Store, component::Component, component::Linker};
|
||||||
use wasmtime_wasi::WasiCtxBuilder;
|
use wasmtime_wasi::{WasiCtxBuilder, sockets::SocketAddrUse};
|
||||||
|
|
||||||
use crate::plugin::{Context, PluginMetadata, loader::wasm::wasm_host::state::PluginHostState};
|
use crate::plugin::{
|
||||||
|
Context, PluginMetadata, loader::wasm::wasm_host::state::PluginHostState, permissions,
|
||||||
|
};
|
||||||
|
|
||||||
pub mod args;
|
pub mod args;
|
||||||
pub mod logging;
|
pub mod logging;
|
||||||
@ -144,13 +146,117 @@ impl WasmPlugin {
|
|||||||
|
|
||||||
let mut builder = WasiCtxBuilder::new();
|
let mut builder = WasiCtxBuilder::new();
|
||||||
|
|
||||||
|
let metadata = context.get_metadata();
|
||||||
|
let blocked_permissions = &context.server.advanced_config.plugins.blocked_permissions;
|
||||||
|
|
||||||
|
let filtered_permissions: Vec<String> = metadata
|
||||||
|
.permissions
|
||||||
|
.iter()
|
||||||
|
.filter(|p| !blocked_permissions.iter().any(|blocked| blocked == *p))
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let has_permission = |p: &str| filtered_permissions.iter().any(|perm| perm == p);
|
||||||
|
|
||||||
|
if has_permission(permissions::NETWORK_DNS) {
|
||||||
|
builder.allow_ip_name_lookup(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
let tcp_allowed = has_permission(permissions::NETWORK_TCP);
|
||||||
|
let udp_allowed = has_permission(permissions::NETWORK_UDP);
|
||||||
|
let tcp_connect = tcp_allowed || has_permission(permissions::NETWORK_TCP_CONNECT);
|
||||||
|
let tcp_bind = tcp_allowed || has_permission(permissions::NETWORK_TCP_BIND);
|
||||||
|
let udp_connect = udp_allowed || has_permission(permissions::NETWORK_UDP_CONNECT);
|
||||||
|
let udp_bind = udp_allowed || has_permission(permissions::NETWORK_UDP_BIND);
|
||||||
|
let udp_outgoing_datagram =
|
||||||
|
udp_allowed || has_permission(permissions::NETWORK_UDP_OUTGOING_DATAGRAM);
|
||||||
|
|
||||||
|
let loopback_only = has_permission(permissions::NETWORK_LOOPBACK);
|
||||||
|
|
||||||
|
builder.allow_tcp(tcp_connect || tcp_bind);
|
||||||
|
builder.allow_udp(udp_connect || udp_bind);
|
||||||
|
|
||||||
|
builder.socket_addr_check(move |addr, reason| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let ok = match reason {
|
||||||
|
SocketAddrUse::TcpConnect => tcp_connect,
|
||||||
|
SocketAddrUse::TcpBind => tcp_bind,
|
||||||
|
SocketAddrUse::UdpConnect => udp_connect,
|
||||||
|
SocketAddrUse::UdpBind => udp_bind,
|
||||||
|
SocketAddrUse::UdpOutgoingDatagram => udp_outgoing_datagram,
|
||||||
|
};
|
||||||
|
|
||||||
|
if loopback_only {
|
||||||
|
ok && addr.ip().is_loopback()
|
||||||
|
} else {
|
||||||
|
ok
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if has_permission(permissions::NETWORK_OUTBOUND) {
|
||||||
|
builder.inherit_network();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- System Permissions ---
|
||||||
|
|
||||||
|
// Environment Variables
|
||||||
|
if has_permission(permissions::SYS_ENV) {
|
||||||
|
builder.inherit_env();
|
||||||
|
} else {
|
||||||
|
for (key, value) in std::env::vars() {
|
||||||
|
let perm = format!("{}{}", permissions::SYS_ENV_PREFIX, key);
|
||||||
|
if has_permission(&perm) {
|
||||||
|
builder.env(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(
|
builder.preopened_dir(
|
||||||
context.get_data_folder(),
|
preopen_path,
|
||||||
context.get_data_folder().to_string_lossy(),
|
preopen_path.to_string_lossy(),
|
||||||
wasmtime_wasi::DirPerms::all(),
|
dir_perms,
|
||||||
wasmtime_wasi::FilePerms::all(),
|
file_perms,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
store.data_mut().permissions = filtered_permissions;
|
||||||
store.data_mut().wasi_ctx = builder.build();
|
store.data_mut().wasi_ctx = builder.build();
|
||||||
|
|
||||||
store.data_mut().server = Some(context.server.clone());
|
store.data_mut().server = Some(context.server.clone());
|
||||||
|
|||||||
@ -52,6 +52,7 @@ pub struct PluginHostState {
|
|||||||
pub resource_table: ResourceTable,
|
pub resource_table: ResourceTable,
|
||||||
pub plugin: Option<Weak<WasmPlugin>>,
|
pub plugin: Option<Weak<WasmPlugin>>,
|
||||||
pub server: Option<Arc<Server>>,
|
pub server: Option<Arc<Server>>,
|
||||||
|
pub permissions: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PluginHostState {
|
impl Default for PluginHostState {
|
||||||
@ -69,6 +70,7 @@ impl PluginHostState {
|
|||||||
resource_table,
|
resource_table,
|
||||||
plugin: None,
|
plugin: None,
|
||||||
server: None,
|
server: None,
|
||||||
|
permissions: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -62,8 +62,11 @@ pub async fn init_plugin(
|
|||||||
authors: metadata.authors,
|
authors: metadata.authors,
|
||||||
description: metadata.description,
|
description: metadata.description,
|
||||||
dependencies: metadata.dependencies,
|
dependencies: metadata.dependencies,
|
||||||
|
permissions: metadata.permissions.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
store.data_mut().permissions = metadata.permissions.clone();
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
WasmPlugin {
|
WasmPlugin {
|
||||||
plugin_instance: PluginInstance::V0_1(plugin),
|
plugin_instance: PluginInstance::V0_1(plugin),
|
||||||
|
|||||||
@ -6,15 +6,18 @@ use crate::command::CommandSender;
|
|||||||
use pumpkin::plugin::server::CommandSender as WasmCommandSender;
|
use pumpkin::plugin::server::CommandSender as WasmCommandSender;
|
||||||
|
|
||||||
use super::player::text_component_from_resource;
|
use super::player::text_component_from_resource;
|
||||||
use crate::plugin::loader::wasm::wasm_host::{
|
use crate::plugin::{
|
||||||
state::{PluginHostState, ServerResource},
|
loader::wasm::wasm_host::{
|
||||||
wit::v0_1::pumpkin::{
|
state::{PluginHostState, ServerResource},
|
||||||
self,
|
wit::v0_1::pumpkin::{
|
||||||
plugin::{
|
self,
|
||||||
player::Player,
|
plugin::{
|
||||||
server::{Difficulty, Dimension, Server},
|
player::Player,
|
||||||
|
server::{Difficulty, Dimension, Server, SysInfo},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
permissions,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl PluginHostState {
|
impl PluginHostState {
|
||||||
@ -28,6 +31,42 @@ impl PluginHostState {
|
|||||||
impl pumpkin::plugin::server::Host for PluginHostState {}
|
impl pumpkin::plugin::server::Host for PluginHostState {}
|
||||||
|
|
||||||
impl pumpkin::plugin::server::HostServer for PluginHostState {
|
impl pumpkin::plugin::server::HostServer for PluginHostState {
|
||||||
|
async fn get_sys_info(&mut self, _res: Resource<Server>) -> wasmtime::Result<SysInfo> {
|
||||||
|
let has_perm = |p: &str| self.permissions.iter().any(|perm| perm == p);
|
||||||
|
|
||||||
|
let mut sys = sysinfo::System::new_all();
|
||||||
|
sys.refresh_all();
|
||||||
|
|
||||||
|
let cpu_count = if has_perm(permissions::SYS_INFO) || has_perm(permissions::SYS_INFO_CPU) {
|
||||||
|
Some(sys.cpus().len() as u32)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let (total_memory, used_memory) =
|
||||||
|
if has_perm(permissions::SYS_INFO) || has_perm(permissions::SYS_INFO_RAM) {
|
||||||
|
(Some(sys.total_memory()), Some(sys.used_memory()))
|
||||||
|
} else {
|
||||||
|
(None, None)
|
||||||
|
};
|
||||||
|
|
||||||
|
let (os_name, os_version) =
|
||||||
|
if has_perm(permissions::SYS_INFO) || has_perm(permissions::SYS_INFO_OS) {
|
||||||
|
(sysinfo::System::name(), sysinfo::System::os_version())
|
||||||
|
} else {
|
||||||
|
(None, None)
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(SysInfo {
|
||||||
|
cpu_count,
|
||||||
|
total_memory,
|
||||||
|
used_memory,
|
||||||
|
os_name,
|
||||||
|
os_version,
|
||||||
|
pumpkin_version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_difficulty(&mut self, res: Resource<Server>) -> wasmtime::Result<Difficulty> {
|
async fn get_difficulty(&mut self, res: Resource<Server>) -> wasmtime::Result<Difficulty> {
|
||||||
let resource = self.get_server_res(&res)?;
|
let resource = self.get_server_res(&res)?;
|
||||||
|
|
||||||
|
|||||||
@ -14,10 +14,15 @@ use tokio::{
|
|||||||
sync::{Notify, RwLock},
|
sync::{Notify, RwLock},
|
||||||
task::JoinHandle,
|
task::JoinHandle,
|
||||||
};
|
};
|
||||||
use tracing::{debug, error, info};
|
use tracing::{debug, error, info, warn};
|
||||||
|
|
||||||
pub mod api;
|
pub mod api;
|
||||||
pub mod loader;
|
pub mod loader;
|
||||||
|
/// Constants for plugin permissions.
|
||||||
|
///
|
||||||
|
/// Plugins can request these permissions in their metadata to access specific
|
||||||
|
/// host features.
|
||||||
|
pub mod permissions;
|
||||||
|
|
||||||
use crate::{LOGGER_IMPL, plugin::loader::wasm::WasmPluginLoader, server::Server};
|
use crate::{LOGGER_IMPL, plugin::loader::wasm::WasmPluginLoader, server::Server};
|
||||||
pub use api::*;
|
pub use api::*;
|
||||||
@ -462,6 +467,7 @@ impl PluginManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Spawn initialization for a single plugin
|
/// Spawn initialization for a single plugin
|
||||||
|
#[expect(clippy::too_many_lines)]
|
||||||
async fn spawn_plugin_initialization(
|
async fn spawn_plugin_initialization(
|
||||||
&self,
|
&self,
|
||||||
mut instance: Box<dyn Plugin>,
|
mut instance: Box<dyn Plugin>,
|
||||||
@ -541,6 +547,13 @@ impl PluginManager {
|
|||||||
state_notify.notify_waiters();
|
state_notify.notify_waiters();
|
||||||
|
|
||||||
info!("Loaded {} ({})", metadata.name, metadata.version);
|
info!("Loaded {} ({})", metadata.name, metadata.version);
|
||||||
|
|
||||||
|
if !metadata.permissions.is_empty() {
|
||||||
|
warn!(
|
||||||
|
"Plugin \"{}\" uses the following permissions: {:?}",
|
||||||
|
metadata.name, metadata.permissions
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// Handle initialization failure
|
// Handle initialization failure
|
||||||
|
|||||||
71
pumpkin/src/plugin/permissions.rs
Normal file
71
pumpkin/src/plugin/permissions.rs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/// Constants for plugin permissions.
|
||||||
|
///
|
||||||
|
/// Plugins can request these permissions in their metadata to access specific
|
||||||
|
/// host features.
|
||||||
|
|
||||||
|
/// Allows the plugin to perform DNS resolution (resolving hostnames to IP addresses).
|
||||||
|
///
|
||||||
|
/// Corresponds to the `wasi:sockets/ip-name-lookup` interface.
|
||||||
|
pub const NETWORK_DNS: &str = "network.dns";
|
||||||
|
|
||||||
|
/// Allows the plugin to use TCP sockets.
|
||||||
|
pub const NETWORK_TCP: &str = "network.tcp";
|
||||||
|
|
||||||
|
/// Allows the plugin to use UDP sockets.
|
||||||
|
pub const NETWORK_UDP: &str = "network.udp";
|
||||||
|
|
||||||
|
/// Allows the plugin to initiate TCP connections.
|
||||||
|
pub const NETWORK_TCP_CONNECT: &str = "network.tcp.connect";
|
||||||
|
|
||||||
|
/// Allows the plugin to bind TCP listeners (accept inbound connections).
|
||||||
|
pub const NETWORK_TCP_BIND: &str = "network.tcp.bind";
|
||||||
|
|
||||||
|
/// Allows the plugin to send and receive UDP packets to specific destinations.
|
||||||
|
pub const NETWORK_UDP_CONNECT: &str = "network.udp.connect";
|
||||||
|
|
||||||
|
/// Allows the plugin to bind UDP sockets to local ports.
|
||||||
|
pub const NETWORK_UDP_BIND: &str = "network.udp.bind";
|
||||||
|
|
||||||
|
/// Allows the plugin to send datagram on non-connected UDP socket.
|
||||||
|
pub const NETWORK_UDP_OUTGOING_DATAGRAM: &str = "network.udp.outgoingdatagram";
|
||||||
|
|
||||||
|
/// Restricts all networking permissions to loopback addresses (localhost) only.
|
||||||
|
pub const NETWORK_LOOPBACK: &str = "network.loopback";
|
||||||
|
|
||||||
|
/// Allows the plugin to make outbound TCP/UDP connections.
|
||||||
|
///
|
||||||
|
/// This gives the plugin full access to the host's network interfaces.
|
||||||
|
/// **Warning:** This is a powerful permission and should only be granted to trusted plugins.
|
||||||
|
pub const NETWORK_OUTBOUND: &str = "network.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/<name>`).
|
||||||
|
/// This is granted by default if any other FS permission is not specified, but can be explicitly requested.
|
||||||
|
pub const FS_READ_DATA: &str = "fs.read.data";
|
||||||
|
|
||||||
|
/// Allows the plugin to write files within its own data folder (`plugins/<name>`).
|
||||||
|
pub const FS_WRITE_DATA: &str = "fs.write.data";
|
||||||
|
|
||||||
|
/// Allows the plugin to read all environment variables.
|
||||||
|
pub const SYS_ENV: &str = "sys.env";
|
||||||
|
|
||||||
|
/// Allows the plugin to read specific environment variables.
|
||||||
|
/// Used with a prefix like "sys.env.PATH".
|
||||||
|
pub const SYS_ENV_PREFIX: &str = "sys.env.";
|
||||||
|
|
||||||
|
/// Allows the plugin to read system information (CPU, Memory, OS).
|
||||||
|
pub const SYS_INFO: &str = "sys.info";
|
||||||
|
|
||||||
|
/// Allows the plugin to read CPU information.
|
||||||
|
pub const SYS_INFO_CPU: &str = "sys.info.cpu";
|
||||||
|
|
||||||
|
/// Allows the plugin to read RAM information.
|
||||||
|
pub const SYS_INFO_RAM: &str = "sys.info.ram";
|
||||||
|
|
||||||
|
/// Allows the plugin to read OS information.
|
||||||
|
pub const SYS_INFO_OS: &str = "sys.info.os";
|
||||||
Loading…
Reference in New Issue
Block a user