diff --git a/assets/plugins/status-bar.wasm b/assets/plugins/status-bar.wasm index d1ad39e902..285669e597 100644 Binary files a/assets/plugins/status-bar.wasm and b/assets/plugins/status-bar.wasm differ diff --git a/assets/plugins/strider.wasm b/assets/plugins/strider.wasm index b57557a7ad..ab3d3cd9f4 100644 Binary files a/assets/plugins/strider.wasm and b/assets/plugins/strider.wasm differ diff --git a/assets/plugins/tab-bar.wasm b/assets/plugins/tab-bar.wasm index 896512fe24..39705bcb12 100644 Binary files a/assets/plugins/tab-bar.wasm and b/assets/plugins/tab-bar.wasm differ diff --git a/zellij-server/src/tab.rs b/zellij-server/src/tab.rs index b90d7827ce..ba8a8c88b4 100644 --- a/zellij-server/src/tab.rs +++ b/zellij-server/src/tab.rs @@ -325,10 +325,15 @@ impl Tab { if let Some(Run::Plugin(Some(plugin))) = &layout.run { let (pid_tx, pid_rx) = channel(); self.senders - .send_to_plugin(PluginInstruction::Load(pid_tx, plugin.clone(), tab_index)) + .send_to_plugin(PluginInstruction::Load( + pid_tx, + plugin.path.clone(), + tab_index, + plugin._allow_exec_host_cmd, + )) .unwrap(); let pid = pid_rx.recv().unwrap(); - let title = String::from(plugin.as_path().as_os_str().to_string_lossy()); + let title = String::from(plugin.path.as_path().as_os_str().to_string_lossy()); let mut new_plugin = PluginPane::new( pid, *position_and_size, diff --git a/zellij-server/src/wasm_vm.rs b/zellij-server/src/wasm_vm.rs index e8d9344eed..cda1142923 100644 --- a/zellij-server/src/wasm_vm.rs +++ b/zellij-server/src/wasm_vm.rs @@ -1,4 +1,4 @@ -use log::info; +use log::{info, warn}; use std::collections::{HashMap, HashSet}; use std::fs; use std::path::PathBuf; @@ -28,8 +28,8 @@ use zellij_utils::{input::command::TerminalAction, serde, zellij_tile}; #[derive(Clone, Debug)] pub(crate) enum PluginInstruction { - Load(Sender, PathBuf, usize), // tx_pid, path_of_plugin , tab_index - Update(Option, Event), // Focused plugin / broadcast, event data + Load(Sender, PathBuf, usize, bool), // tx_pid, path_of_plugin , tab_index, allow_exec_host_cmd + Update(Option, Event), // Focused plugin / broadcast, event data Render(Sender, u32, usize, usize), // String buffer, plugin id, rows, cols Unload(u32), Exit, @@ -54,6 +54,8 @@ pub(crate) struct PluginEnv { pub senders: ThreadSenders, pub wasi_env: WasiEnv, pub subscriptions: Arc>>, + // FIXME: Once permission system is ready, this could be removed + pub _allow_exec_host_cmd: bool, } // Thread main -------------------------------------------------------------------------------------------------------- @@ -65,7 +67,7 @@ pub(crate) fn wasm_thread_main(bus: Bus, store: Store, data_d let (event, mut err_ctx) = bus.recv().expect("failed to receive event on channel"); err_ctx.add_call(ContextType::Plugin((&event).into())); match event { - PluginInstruction::Load(pid_tx, path, tab_index) => { + PluginInstruction::Load(pid_tx, path, tab_index, _allow_exec_host_cmd) => { let plugin_dir = data_dir.join("plugins/"); let wasm_bytes = fs::read(&path) .or_else(|_| fs::read(&path.with_extension("wasm"))) @@ -99,12 +101,17 @@ pub(crate) fn wasm_thread_main(bus: Bus, store: Store, data_d let wasi = wasi_env.import_object(&module).unwrap(); + if _allow_exec_host_cmd { + info!("Plugin({:?}) is able to run any host command, this may lead to some security issues!", path); + } + let plugin_env = PluginEnv { plugin_id, tab_index, senders: bus.senders.clone(), wasi_env, subscriptions: Arc::new(Mutex::new(HashSet::new())), + _allow_exec_host_cmd, }; let zellij = zellij_exports(&store, &plugin_env); @@ -174,6 +181,7 @@ pub(crate) fn zellij_exports(store: &Store, plugin_env: &PluginEnv) -> ImportObj host_get_plugin_ids, host_open_file, host_set_timeout, + host_exec_cmd, } } @@ -248,6 +256,24 @@ fn host_set_timeout(plugin_env: &PluginEnv, secs: f64) { }); } +fn host_exec_cmd(plugin_env: &PluginEnv) { + let mut cmdline: Vec = wasi_read_object(&plugin_env.wasi_env); + let command = cmdline.remove(0); + + // Bail out if we're forbidden to run command + if !plugin_env._allow_exec_host_cmd { + warn!("This plugin isn't allow to run command in host side, skip running this command: '{cmd} {args}'.", + cmd = command, args = cmdline.join(" ")); + return; + } + + // Here, we don't wait the command to finish + process::Command::new(command) + .args(cmdline) + .spawn() + .unwrap(); +} + // Helper Functions --------------------------------------------------------------------------------------------------- pub fn wasi_read_string(wasi_env: &WasiEnv) -> String { diff --git a/zellij-tile/src/shim.rs b/zellij-tile/src/shim.rs index b514b55aa1..bdd6f4fb18 100644 --- a/zellij-tile/src/shim.rs +++ b/zellij-tile/src/shim.rs @@ -37,6 +37,10 @@ pub fn open_file(path: &Path) { pub fn set_timeout(secs: f64) { unsafe { host_set_timeout(secs) }; } +pub fn exec_cmd(cmd: &[&str]) { + object_to_stdout(&cmd); + unsafe { host_exec_cmd() }; +} // Internal Functions @@ -60,4 +64,5 @@ extern "C" { fn host_get_plugin_ids(); fn host_open_file(); fn host_set_timeout(secs: f64); + fn host_exec_cmd(); } diff --git a/zellij-utils/assets/layouts/default.yaml b/zellij-utils/assets/layouts/default.yaml index e0a28da125..549dea24b9 100644 --- a/zellij-utils/assets/layouts/default.yaml +++ b/zellij-utils/assets/layouts/default.yaml @@ -7,7 +7,8 @@ template: split_size: Fixed: 1 run: - plugin: tab-bar + plugin: + path: tab-bar - direction: Vertical body: true - direction: Vertical @@ -15,6 +16,7 @@ template: split_size: Fixed: 2 run: - plugin: status-bar + plugin: + path: status-bar tabs: - direction: Vertical diff --git a/zellij-utils/assets/layouts/disable-status-bar.yaml b/zellij-utils/assets/layouts/disable-status-bar.yaml index a58ef4cf48..e97bb8f1e9 100644 --- a/zellij-utils/assets/layouts/disable-status-bar.yaml +++ b/zellij-utils/assets/layouts/disable-status-bar.yaml @@ -7,6 +7,7 @@ template: split_size: Fixed: 1 run: - plugin: tab-bar + plugin: + path: tab-bar - direction: Vertical body: true diff --git a/zellij-utils/assets/layouts/strider.yaml b/zellij-utils/assets/layouts/strider.yaml index 96e3c290e9..ccb2a5748a 100644 --- a/zellij-utils/assets/layouts/strider.yaml +++ b/zellij-utils/assets/layouts/strider.yaml @@ -7,7 +7,8 @@ template: split_size: Fixed: 1 run: - plugin: tab-bar + plugin: + path: tab-bar - direction: Vertical body: true - direction: Vertical @@ -15,7 +16,8 @@ template: split_size: Fixed: 2 run: - plugin: status-bar + plugin: + path: status-bar tabs: - direction: Vertical parts: @@ -23,5 +25,6 @@ tabs: split_size: Percent: 20 run: - plugin: strider + plugin: + path: strider - direction: Horizontal diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index be71130cdb..5d4b349263 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -53,11 +53,19 @@ pub enum SplitSize { #[serde(crate = "self::serde")] pub enum Run { #[serde(rename = "plugin")] - Plugin(Option), + Plugin(Option), #[serde(rename = "command")] Command(RunCommand), } +#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq)] +#[serde(crate = "self::serde")] +pub struct RunPlugin { + pub path: PathBuf, + #[serde(default)] + pub _allow_exec_host_cmd: bool, +} + // The layout struct ultimately used to build the layouts. #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] #[serde(crate = "self::serde")] diff --git a/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-default-plugins.yaml b/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-default-plugins.yaml index 8804639579..ae54a0c9bf 100644 --- a/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-default-plugins.yaml +++ b/zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-default-plugins.yaml @@ -6,14 +6,16 @@ template: split_size: Fixed: 1 run: - plugin: tab-bar + plugin: + path: tab-bar - direction: Horizontal body: true - direction: Vertical split_size: Fixed: 2 run: - plugin: status-bar + plugin: + path: status-bar tabs: - direction: Vertical diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs index abce56a4e6..1b696b0a1e 100644 --- a/zellij-utils/src/input/unit/layout_test.rs +++ b/zellij-utils/src/input/unit/layout_test.rs @@ -45,7 +45,10 @@ fn default_layout_merged_correctly() { borderless: true, parts: vec![], split_size: Some(SplitSize::Fixed(1)), - run: Some(Run::Plugin(Some("tab-bar".into()))), + run: Some(Run::Plugin(Some(RunPlugin { + path: "tab-bar".into(), + ..Default::default() + }))), }, Layout { direction: Direction::Vertical, @@ -59,7 +62,10 @@ fn default_layout_merged_correctly() { borderless: true, parts: vec![], split_size: Some(SplitSize::Fixed(2)), - run: Some(Run::Plugin(Some("status-bar".into()))), + run: Some(Run::Plugin(Some(RunPlugin { + path: "status-bar".into(), + ..Default::default() + }))), }, ], split_size: None, @@ -83,7 +89,10 @@ fn default_layout_new_tab_correct() { borderless: true, parts: vec![], split_size: Some(SplitSize::Fixed(1)), - run: Some(Run::Plugin(Some("tab-bar".into()))), + run: Some(Run::Plugin(Some(RunPlugin { + path: "tab-bar".into(), + ..Default::default() + }))), }, Layout { direction: Direction::Horizontal, @@ -97,7 +106,10 @@ fn default_layout_new_tab_correct() { borderless: true, parts: vec![], split_size: Some(SplitSize::Fixed(2)), - run: Some(Run::Plugin(Some("status-bar".into()))), + run: Some(Run::Plugin(Some(RunPlugin { + path: "status-bar".into(), + ..Default::default() + }))), }, ], split_size: None, @@ -253,7 +265,10 @@ fn three_panes_with_tab_and_default_plugins_merged_correctly() { borderless: false, parts: vec![], split_size: Some(SplitSize::Fixed(1)), - run: Some(Run::Plugin(Some("tab-bar".into()))), + run: Some(Run::Plugin(Some(RunPlugin { + path: "tab-bar".into(), + ..Default::default() + }))), }, Layout { direction: Direction::Vertical, @@ -297,7 +312,10 @@ fn three_panes_with_tab_and_default_plugins_merged_correctly() { borderless: false, parts: vec![], split_size: Some(SplitSize::Fixed(2)), - run: Some(Run::Plugin(Some("status-bar".into()))), + run: Some(Run::Plugin(Some(RunPlugin { + path: "status-bar".into(), + ..Default::default() + }))), }, ], split_size: None, @@ -321,7 +339,10 @@ fn three_panes_with_tab_and_default_plugins_new_tab_is_correct() { borderless: false, parts: vec![], split_size: Some(SplitSize::Fixed(1)), - run: Some(Run::Plugin(Some("tab-bar".into()))), + run: Some(Run::Plugin(Some(RunPlugin { + path: "tab-bar".into(), + ..Default::default() + }))), }, Layout { direction: Direction::Horizontal, @@ -335,7 +356,10 @@ fn three_panes_with_tab_and_default_plugins_new_tab_is_correct() { borderless: false, parts: vec![], split_size: Some(SplitSize::Fixed(2)), - run: Some(Run::Plugin(Some("status-bar".into()))), + run: Some(Run::Plugin(Some(RunPlugin { + path: "status-bar".into(), + ..Default::default() + }))), }, ], split_size: None,