Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-term/src/handlers/workspace_trust.rs')
| -rw-r--r-- | helix-term/src/handlers/workspace_trust.rs | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/helix-term/src/handlers/workspace_trust.rs b/helix-term/src/handlers/workspace_trust.rs new file mode 100644 index 00000000..68e9d8bd --- /dev/null +++ b/helix-term/src/handlers/workspace_trust.rs @@ -0,0 +1,100 @@ +use std::{collections::HashSet, path::PathBuf}; + +use helix_event::register_hook; +use helix_loader::workspace_trust::{ + quick_query_workspace_with_explicit_untrust, TrustUntrustStatus, WorkspaceTrust, +}; +use helix_view::{events::DocumentDidOpen, handlers::Handlers, DocumentId}; +use once_cell::sync::Lazy; +use parking_lot::Mutex; + +use crate::{compositor::Compositor, job, ui}; + +const ID: &str = "workspace-trust-select"; + +/// A set of canonicalized workspace paths which have been prompted for trust at runtime. +static PROMPTED_WORKSPACES: Lazy<Mutex<HashSet<PathBuf>>> = + Lazy::new(|| Mutex::new(HashSet::new())); + +pub(super) fn register_hooks(_handlers: &Handlers) { + register_hook!(move |event: &mut DocumentDidOpen<'_>| { + let doc = doc!(event.editor, &event.doc); + + // If there is no servers to be loaded, then the workspace might not be trusted yet + if doc.language_servers().next().is_none() { + if let TrustUntrustStatus::DenyOnce = + quick_query_workspace_with_explicit_untrust(event.editor.config().insecure) + { + let (workspace, _) = helix_loader::find_workspace(); + job::dispatch_blocking(|_editor, compositor| prompt(workspace, compositor)); + } + } + Ok(()) + }); +} + +pub fn prompt(path: PathBuf, compositor: &mut Compositor) { + let mut workspaces = PROMPTED_WORKSPACES.lock(); + if workspaces.contains(&path) { + return; + } else { + workspaces.insert(path.clone()); + } + let select = select(); + compositor.replace_or_push(ID, select); +} + +const TRUST_MESSAGE: &str = "Trust this workspace? + +Trusted workspaces may load local config files and auto-start language servers. Config and language servers can execute arbitrary code. Only trust workspaces which you know contain harmless config and code."; + +fn select() -> ui::Select<TrustUntrustStatus> { + ui::Select::new( + TRUST_MESSAGE, + [ + TrustUntrustStatus::DenyOnce, + TrustUntrustStatus::DenyAlways, + TrustUntrustStatus::AllowAlways, + ], + (), + move |editor, option, event| { + if event == ui::PromptEvent::Validate { + let mut trust = WorkspaceTrust::load(true); + match option { + TrustUntrustStatus::DenyAlways => { + trust.exclude_workspace(); + } + TrustUntrustStatus::DenyOnce => { + // Do nothing + } + TrustUntrustStatus::AllowAlways => { + trust.trust_workspace(); + + let documents: Vec<DocumentId> = editor.documents.keys().cloned().collect(); + for document_id in documents.iter() { + editor.launch_language_servers(*document_id); + } + + let _ = editor + .config_events + .0 + .send(helix_view::editor::ConfigEvent::Refresh); + } + } + } + }, + ) +} + +impl crate::ui::menu::Item for TrustUntrustStatus { + type Data = (); + + fn format(&self, _data: &Self::Data) -> tui::widgets::Row<'_> { + match self { + TrustUntrustStatus::DenyAlways => "Never", + TrustUntrustStatus::DenyOnce => "Not now", + TrustUntrustStatus::AllowAlways => "Always", + } + .into() + } +} |