diff --git a/desktop/tauri/src-tauri/src/window.rs b/desktop/tauri/src-tauri/src/window.rs index 43c03d46..50bfb4af 100644 --- a/desktop/tauri/src-tauri/src/window.rs +++ b/desktop/tauri/src-tauri/src/window.rs @@ -9,6 +9,8 @@ use crate::{portmaster::PortmasterExt, traymenu}; const LIGHT_PM_ICON: &[u8] = include_bytes!("../../../../assets/data/icons/pm_light_512.png"); const DARK_PM_ICON: &[u8] = include_bytes!("../../../../assets/data/icons/pm_dark_512.png"); +const CUSTOM_ENVVAR_FOR_WEBVIEW_PROCESS: &str = "PORTMASTER_UI_WEBVIEW_PROCESS"; + /// Either returns the existing "main" window or creates a new one. /// /// The window is not automatically shown (i.e it starts hidden). @@ -24,6 +26,7 @@ pub fn create_main_window(app: &AppHandle) -> Result { } else { debug!("[tauri] creating main window"); + do_before_window_create(); // required operations before window creation let res = WebviewWindowBuilder::new(app, "main", WebviewUrl::App("index.html".into())) .title("Portmaster") .visible(false) @@ -31,6 +34,7 @@ pub fn create_main_window(app: &AppHandle) -> Result { .min_inner_size(800.0, 600.0) .theme(Some(Theme::Dark)) .build(); + do_after_window_create(); // required operations after window creation match res { Ok(win) => { @@ -69,6 +73,8 @@ pub fn create_splash_window(app: &AppHandle) -> Result { let _ = window.show(); Ok(window) } else { + + do_before_window_create(); // required operations before window creation let window = WebviewWindowBuilder::new(app, "splash", WebviewUrl::App("index.html".into())) .center() .closable(false) @@ -78,6 +84,7 @@ pub fn create_splash_window(app: &AppHandle) -> Result { .title("Portmaster") .inner_size(600.0, 250.0) .build()?; + do_after_window_create(); // required operations after window creation set_window_icon(&window); let _ = window.request_user_attention(Some(UserAttentionType::Informational)); @@ -118,6 +125,28 @@ pub fn set_window_icon(window: &WebviewWindow) { }; } +/// This function must be called before the window is created. +/// +/// Temporarily sets the environment variable `PORTMASTER_WEBVIEW_UI_PROCESS` to "true". +/// This ensures that any child process (i.e., the WebView process) spawned during window creation +/// will inherit this environment variable. This allows portmaster-core to detect that the process +/// is a child WebView of the main process. +/// +/// IMPORTANT: After the window is created, you must call `do_after_window_create()` to remove +/// the environment variable from the main process environment. +pub fn do_before_window_create() { + std::env::set_var(CUSTOM_ENVVAR_FOR_WEBVIEW_PROCESS, "true"); +} + +/// This function must be called after the window is created. +/// +/// Removes the `PORTMASTER_WEBVIEW_UI_PROCESS` environment variable from the main process. +/// This ensures that only the child WebView process has the variable set, and the main process +/// does not retain it. +pub fn do_after_window_create() { + std::env::remove_var(CUSTOM_ENVVAR_FOR_WEBVIEW_PROCESS); +} + /// Opens a window for the tauri application. /// /// If the main window has already been created, it is instructed to diff --git a/service/process/profile.go b/service/process/profile.go index 7653bd72..8217aa9b 100644 --- a/service/process/profile.go +++ b/service/process/profile.go @@ -118,6 +118,8 @@ func (p *Process) IsPortmasterUi(ctx context.Context) bool { var previousPid int proc := p + hasPmWebviewEnvVar := false + for i := 0; i < checkLevels; i++ { if proc.Pid == UnidentifiedProcessID || proc.Pid == SystemProcessID { break @@ -125,7 +127,32 @@ func (p *Process) IsPortmasterUi(ctx context.Context) bool { realPath, err := filepath.EvalSymlinks(proc.Path) if err == nil && realPath == module.portmasterUIPath { - return true + if runtime.GOOS != "windows" { + return true + } + + // On Windows, avoid false positive detection of the Portmaster UI. + // For example: + // There may be cases where a system browser is launched from the Portmaster UI, + // making it a child of the Portmaster UI process (e.g., user clicked a link in the UI). + // To ensure that 'p' is the actual Portmaster UI process, we check for the presence + // of the 'PORTMASTER_UI_WEBVIEW_PROCESS' environment variable in the process and its parents. + // If the env var is set, we are a child (WebView window) of the Portmaster UI process. + // Otherwise, the process was launched by the Portmaster UI, but should not be trusted as the Portmaster UI process. + if i == 0 { + return true // We are the main Portmaster UI process. + } + if hasPmWebviewEnvVar { + return true // We are a WebView window of the Portmaster UI process. + } + // The process was launched by the Portmaster UI, but should not be trusted as the Portmaster UI process. + log.Tracer(ctx).Warning(fmt.Sprintf("process: %d '%s' is a child of the Portmaster UI, but does not have the PORTMASTER_UI_WEBVIEW_PROCESS environment variable set. Ignoring.", p.Pid, p.Path)) + return false + } + + // Check if the process has the environment variable set. + if _, ok := proc.Env["PORTMASTER_UI_WEBVIEW_PROCESS"]; ok { + hasPmWebviewEnvVar = true } if i < checkLevels-1 { // no need to check parent if we are at the last level