feat(owlry-core): move backend modules from owlry
Move the following modules from crates/owlry/src/ to crates/owlry-core/src/: - config/ (configuration loading and types) - data/ (frecency store) - filter.rs (provider filtering and prefix parsing) - notify.rs (desktop notifications) - paths.rs (XDG path handling) - plugins/ (plugin system: native loader, manifest, registry, runtime loader, Lua API) - providers/ (provider trait, manager, application, command, native_provider, lua_provider) Notable changes from the original: - providers/mod.rs: ProviderManager constructor changed from with_native_plugins() to new(core_providers, native_providers) to decouple from DmenuProvider (which stays in owlry as a UI concern) - plugins/mod.rs: commands module removed (stays in owlry as CLI concern) - Added thiserror and tempfile dependencies to owlry-core Cargo.toml
This commit is contained in:
153
crates/owlry-core/src/plugins/runtime.rs
Normal file
153
crates/owlry-core/src/plugins/runtime.rs
Normal file
@@ -0,0 +1,153 @@
|
||||
//! Lua runtime setup and sandboxing
|
||||
|
||||
use mlua::{Lua, Result as LuaResult, StdLib};
|
||||
|
||||
use super::manifest::PluginPermissions;
|
||||
|
||||
/// Configuration for the Lua sandbox
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)] // Fields used for future permission enforcement
|
||||
pub struct SandboxConfig {
|
||||
/// Allow shell command running
|
||||
pub allow_commands: bool,
|
||||
/// Allow HTTP requests
|
||||
pub allow_network: bool,
|
||||
/// Allow filesystem access outside plugin directory
|
||||
pub allow_external_fs: bool,
|
||||
/// Maximum run time per call (ms)
|
||||
pub max_run_time_ms: u64,
|
||||
/// Memory limit (bytes, 0 = unlimited)
|
||||
pub max_memory: usize,
|
||||
}
|
||||
|
||||
impl Default for SandboxConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
allow_commands: false,
|
||||
allow_network: false,
|
||||
allow_external_fs: false,
|
||||
max_run_time_ms: 5000, // 5 seconds
|
||||
max_memory: 64 * 1024 * 1024, // 64 MB
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SandboxConfig {
|
||||
/// Create a sandbox config from plugin permissions
|
||||
pub fn from_permissions(permissions: &PluginPermissions) -> Self {
|
||||
Self {
|
||||
allow_commands: !permissions.run_commands.is_empty(),
|
||||
allow_network: permissions.network,
|
||||
allow_external_fs: !permissions.filesystem.is_empty(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new sandboxed Lua runtime
|
||||
pub fn create_lua_runtime(_sandbox: &SandboxConfig) -> LuaResult<Lua> {
|
||||
// Create Lua with safe standard libraries only
|
||||
// ALL_SAFE excludes: debug, io, os (dangerous parts), package (loadlib), ffi
|
||||
// We then customize the os table to only allow safe functions
|
||||
let libs = StdLib::COROUTINE
|
||||
| StdLib::TABLE
|
||||
| StdLib::STRING
|
||||
| StdLib::UTF8
|
||||
| StdLib::MATH;
|
||||
|
||||
let lua = Lua::new_with(libs, mlua::LuaOptions::default())?;
|
||||
|
||||
// Set up safe environment
|
||||
setup_safe_globals(&lua)?;
|
||||
|
||||
Ok(lua)
|
||||
}
|
||||
|
||||
/// Set up safe global environment by removing/replacing dangerous functions
|
||||
fn setup_safe_globals(lua: &Lua) -> LuaResult<()> {
|
||||
let globals = lua.globals();
|
||||
|
||||
// Remove dangerous globals
|
||||
globals.set("dofile", mlua::Value::Nil)?;
|
||||
globals.set("loadfile", mlua::Value::Nil)?;
|
||||
|
||||
// Create a restricted os table with only safe functions
|
||||
// We do NOT include: os.exit, os.remove, os.rename, os.setlocale, os.tmpname
|
||||
// and the shell-related functions
|
||||
let os_table = lua.create_table()?;
|
||||
os_table.set("clock", lua.create_function(|_, ()| Ok(std::time::Instant::now().elapsed().as_secs_f64()))?)?;
|
||||
os_table.set("date", lua.create_function(os_date)?)?;
|
||||
os_table.set("difftime", lua.create_function(|_, (t2, t1): (f64, f64)| Ok(t2 - t1))?)?;
|
||||
os_table.set("time", lua.create_function(os_time)?)?;
|
||||
globals.set("os", os_table)?;
|
||||
|
||||
// Remove print (plugins should use owlry.log instead)
|
||||
// We'll add it back via owlry.log
|
||||
globals.set("print", mlua::Value::Nil)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Safe os.date implementation
|
||||
fn os_date(_lua: &Lua, format: Option<String>) -> LuaResult<String> {
|
||||
use chrono::Local;
|
||||
let now = Local::now();
|
||||
let fmt = format.unwrap_or_else(|| "%c".to_string());
|
||||
Ok(now.format(&fmt).to_string())
|
||||
}
|
||||
|
||||
/// Safe os.time implementation
|
||||
fn os_time(_lua: &Lua, _args: ()) -> LuaResult<i64> {
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
let duration = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap_or_default();
|
||||
Ok(duration.as_secs() as i64)
|
||||
}
|
||||
|
||||
/// Load and run a Lua file in the given runtime
|
||||
pub fn load_file(lua: &Lua, path: &std::path::Path) -> LuaResult<()> {
|
||||
let content = std::fs::read_to_string(path)
|
||||
.map_err(mlua::Error::external)?;
|
||||
lua.load(&content)
|
||||
.set_name(path.file_name().and_then(|n| n.to_str()).unwrap_or("chunk"))
|
||||
.into_function()?
|
||||
.call(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_create_sandboxed_runtime() {
|
||||
let config = SandboxConfig::default();
|
||||
let lua = create_lua_runtime(&config).unwrap();
|
||||
|
||||
// Verify dangerous functions are removed
|
||||
let result: LuaResult<mlua::Value> = lua.globals().get("dofile");
|
||||
assert!(matches!(result, Ok(mlua::Value::Nil)));
|
||||
|
||||
// Verify safe functions work
|
||||
let result: String = lua.load("return os.date('%Y')").call(()).unwrap();
|
||||
assert!(!result.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basic_lua_operations() {
|
||||
let config = SandboxConfig::default();
|
||||
let lua = create_lua_runtime(&config).unwrap();
|
||||
|
||||
// Test basic math
|
||||
let result: i32 = lua.load("return 2 + 2").call(()).unwrap();
|
||||
assert_eq!(result, 4);
|
||||
|
||||
// Test table operations
|
||||
let result: i32 = lua.load("local t = {1,2,3}; return #t").call(()).unwrap();
|
||||
assert_eq!(result, 3);
|
||||
|
||||
// Test string operations
|
||||
let result: String = lua.load("return string.upper('hello')").call(()).unwrap();
|
||||
assert_eq!(result, "HELLO");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user