From 273b16c523263bf05bf83811c28f35ab62b7d847 Mon Sep 17 00:00:00 2001 From: Vladimir Stoilov Date: Thu, 11 Jul 2024 09:33:21 +0300 Subject: [PATCH] [cmds] Handle edge cases when running tauri in beta --- cmds/portmaster-start/main.go | 2 + cmds/portmaster-start/run.go | 27 +- .../src-tauri/gen/schemas/windows-schema.json | 286 +++++++++++++++++- desktop/tauri/src-tauri/src/main.rs | 2 +- 4 files changed, 301 insertions(+), 16 deletions(-) diff --git a/cmds/portmaster-start/main.go b/cmds/portmaster-start/main.go index fd2e5a32..8d796b21 100644 --- a/cmds/portmaster-start/main.go +++ b/cmds/portmaster-start/main.go @@ -28,6 +28,7 @@ var ( maxRetries int dataRoot *utils.DirStructure logsRoot *utils.DirStructure + forceOldUI bool updateURLFlag string userAgentFlag string @@ -74,6 +75,7 @@ func init() { flags.StringVar(&userAgentFlag, "update-agent", "", "Set an alternative user agent for requests to the update server") flags.IntVar(&maxRetries, "max-retries", 5, "Maximum number of retries when starting a Portmaster component") flags.BoolVar(&stdinSignals, "input-signals", false, "Emulate signals using stdin.") + flags.BoolVar(&forceOldUI, "old-ui", false, "Use the old ui. (Beta)") _ = rootCmd.MarkPersistentFlagDirname("data") _ = flags.MarkHidden("input-signals") } diff --git a/cmds/portmaster-start/run.go b/cmds/portmaster-start/run.go index e5dbaf7c..5b4e04fd 100644 --- a/cmds/portmaster-start/run.go +++ b/cmds/portmaster-start/run.go @@ -30,7 +30,8 @@ const ( ControlledFailureExitCode = 24 // StartOldUIExitCode is an exit code that is returned by the UI when there. This is manfully triaged by the user, if the new UI does not work for them. - StartOldUIExitCode = -3 + StartOldUIExitCode = 77 + MissingDependencyExitCode = 0xc0000135 // Windows STATUS_DLL_NOT_FOUND exeSuffix = ".exe" zipSuffix = ".zip" @@ -60,7 +61,21 @@ type Options struct { RestartOnFail bool // Try restarting automatically, if the started component fails. } +// This is a temp value that will be used to test the new UI in beta. +var app2Options = Options{ + Name: "Portmaster App2", + Identifier: "app2/portmaster", + AllowDownload: false, + AllowHidingWindow: false, + RestartOnFail: true, +} + func init() { + // Make sure the new UI has a proper extension. + if onWindows { + app2Options.Identifier += ".exe" + } + registerComponent([]Options{ { Name: "Portmaster Core", @@ -75,6 +90,7 @@ func init() { Identifier: "app/portmaster-app.zip", AllowDownload: false, AllowHidingWindow: false, + RestartOnFail: true, }, { Name: "Portmaster App2", @@ -322,11 +338,11 @@ func persistOutputStreams(opts *Options, version string, cmd *exec.Cmd) (chan st } func execute(opts *Options, args []string) (cont bool, err error) { - if registry.UsePreReleases && opts.ShortIdentifier == "app" { + if !forceOldUI && registry.UsePreReleases && opts.ShortIdentifier == "app" { // Check if new ui was already tried. if !fallBackToOldUI { - opts.Identifier = "app2/portmaster" - opts.ShortIdentifier = "app2" + opts = &app2Options + log.Println("Using new UI") } } @@ -462,6 +478,9 @@ func parseExitError(err error) (restart bool, errWithCtx error) { case StartOldUIExitCode: fallBackToOldUI = true return true, errors.New("user requested old UI") + case MissingDependencyExitCode: + fallBackToOldUI = true + return true, errors.New("new UI failed with missing dependency") default: return true, fmt.Errorf("unknown exit code %w", exErr) } diff --git a/desktop/tauri/src-tauri/gen/schemas/windows-schema.json b/desktop/tauri/src-tauri/gen/schemas/windows-schema.json index 40ff6ec9..49f2493c 100644 --- a/desktop/tauri/src-tauri/gen/schemas/windows-schema.json +++ b/desktop/tauri/src-tauri/gen/schemas/windows-schema.json @@ -37,7 +37,7 @@ ], "definitions": { "Capability": { - "description": "a grouping and boundary mechanism developers can use to separate windows or plugins functionality from each other at runtime.\n\nIf a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create trust groups and reduce impact of vulnerabilities in certain plugins or windows. Windows can be added to a capability by exact name or glob patterns like *, admin-* or main-window.", + "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows fine grained access to the Tauri core, application, or plugin commands. If a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"path:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, \"platforms\": [\"macOS\",\"windows\"] } ```", "type": "object", "required": [ "identifier", @@ -45,16 +45,16 @@ ], "properties": { "identifier": { - "description": "Identifier of the capability.", + "description": "Identifier of the capability.\n\n## Example\n\n`main-user-files-write`", "type": "string" }, "description": { - "description": "Description of the capability.", + "description": "Description of what the capability is intended to allow on associated windows.\n\nIt should contain a description of what the grouped permissions should allow.\n\n## Example\n\nThis capability allows the `main` window access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.", "default": "", "type": "string" }, "remote": { - "description": "Configure remote URLs that can use the capability permissions.", + "description": "Configure remote URLs that can use the capability permissions.\n\nThis setting is optional and defaults to not being set, as our default use case is that the content is served from our local application.\n\n:::caution Make sure you understand the security implications of providing remote sources with local system access. :::\n\n## Example\n\n```json { \"urls\": [\"https://*.mydomain.dev\"] } ```", "anyOf": [ { "$ref": "#/definitions/CapabilityRemote" @@ -70,28 +70,29 @@ "type": "boolean" }, "windows": { - "description": "List of windows that uses this capability. Can be a glob pattern.\n\nOn multiwebview windows, prefer [`Self::webviews`] for a fine grained access control.", + "description": "List of windows that are affected by this capability. Can be a glob pattern.\n\nOn multiwebview windows, prefer [`Self::webviews`] for a fine grained access control.\n\n## Example\n\n`[\"main\"]`", "type": "array", "items": { "type": "string" } }, "webviews": { - "description": "List of webviews that uses this capability. Can be a glob pattern.\n\nThis is only required when using on multiwebview contexts, by default all child webviews of a window that matches [`Self::windows`] are linked.", + "description": "List of webviews that are affected by this capability. Can be a glob pattern.\n\nThis is only required when using on multiwebview contexts, by default all child webviews of a window that matches [`Self::windows`] are linked.\n\n## Example\n\n`[\"sub-webview-one\", \"sub-webview-two\"]`", "type": "array", "items": { "type": "string" } }, "permissions": { - "description": "List of permissions attached to this capability. Must include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`.", + "description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"path:default\", \"event:default\", \"window:default\", \"app:default\", \"image:default\", \"resources:default\", \"menu:default\", \"tray:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ```", "type": "array", "items": { "$ref": "#/definitions/PermissionEntry" - } + }, + "uniqueItems": true }, "platforms": { - "description": "Target platforms this capability applies. By default all platforms are affected by this capability.", + "description": "Limit which target platforms this capability applies to.\n\nBy default all platforms are targeted.\n\n## Example\n\n`[\"macOS\",\"windows\"]`", "type": [ "array", "null" @@ -110,7 +111,7 @@ ], "properties": { "urls": { - "description": "Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).\n\n# Examples\n\n- \"https://*.mydomain.dev\": allows subdomains of mydomain.dev - \"https://mydomain.dev/api/*\": allows any subpath of mydomain.dev/api", + "description": "Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).\n\n## Examples\n\n- \"https://*.mydomain.dev\": allows subdomains of mydomain.dev - \"https://mydomain.dev/api/*\": allows any subpath of mydomain.dev/api", "type": "array", "items": { "type": "string" @@ -142,6 +143,7 @@ "identifier": { "oneOf": [ { + "description": "shell:default -> This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality without any specific\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n", "type": "string", "enum": [ "shell:default" @@ -168,6 +170,13 @@ "shell:allow-open" ] }, + { + "description": "shell:allow-spawn -> Enables the spawn command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:allow-spawn" + ] + }, { "description": "shell:allow-stdin-write -> Enables the stdin_write command without any pre-configured scope.", "type": "string", @@ -196,6 +205,13 @@ "shell:deny-open" ] }, + { + "description": "shell:deny-spawn -> Denies the spawn command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:deny-spawn" + ] + }, { "description": "shell:deny-stdin-write -> Denies the stdin_write command without any pre-configured scope.", "type": "string", @@ -304,6 +320,13 @@ "app:allow-app-show" ] }, + { + "description": "app:allow-default-window-icon -> Enables the default_window_icon command without any pre-configured scope.", + "type": "string", + "enum": [ + "app:allow-default-window-icon" + ] + }, { "description": "app:allow-name -> Enables the name command without any pre-configured scope.", "type": "string", @@ -339,6 +362,13 @@ "app:deny-app-show" ] }, + { + "description": "app:deny-default-window-icon -> Denies the default_window_icon command without any pre-configured scope.", + "type": "string", + "enum": [ + "app:deny-default-window-icon" + ] + }, { "description": "app:deny-name -> Denies the name command without any pre-configured scope.", "type": "string", @@ -416,6 +446,7 @@ ] }, { + "description": "dialog:default -> This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n", "type": "string", "enum": [ "dialog:default" @@ -631,6 +662,27 @@ "image:deny-size" ] }, + { + "description": "log:default -> Allows the log command", + "type": "string", + "enum": [ + "log:default" + ] + }, + { + "description": "log:allow-log -> Enables the log command without any pre-configured scope.", + "type": "string", + "enum": [ + "log:allow-log" + ] + }, + { + "description": "log:deny-log -> Denies the log command without any pre-configured scope.", + "type": "string", + "enum": [ + "log:deny-log" + ] + }, { "description": "menu:default -> Default permissions for the plugin.", "type": "string", @@ -947,12 +999,61 @@ ] }, { - "description": "notification:default -> Allows requesting permission, checking permission state and sending notifications", + "description": "notification:default -> This permission set configures which\nnotification features are by default exposed.\n\n#### Granted Permissions\n\nIt allows all notification related features.\n\n", "type": "string", "enum": [ "notification:default" ] }, + { + "description": "notification:allow-batch -> Enables the batch command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:allow-batch" + ] + }, + { + "description": "notification:allow-cancel -> Enables the cancel command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:allow-cancel" + ] + }, + { + "description": "notification:allow-check-permissions -> Enables the check_permissions command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:allow-check-permissions" + ] + }, + { + "description": "notification:allow-create-channel -> Enables the create_channel command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:allow-create-channel" + ] + }, + { + "description": "notification:allow-delete-channel -> Enables the delete_channel command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:allow-delete-channel" + ] + }, + { + "description": "notification:allow-get-active -> Enables the get_active command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:allow-get-active" + ] + }, + { + "description": "notification:allow-get-pending -> Enables the get_pending command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:allow-get-pending" + ] + }, { "description": "notification:allow-is-permission-granted -> Enables the is_permission_granted command without any pre-configured scope.", "type": "string", @@ -960,6 +1061,13 @@ "notification:allow-is-permission-granted" ] }, + { + "description": "notification:allow-list-channels -> Enables the list_channels command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:allow-list-channels" + ] + }, { "description": "notification:allow-notify -> Enables the notify command without any pre-configured scope.", "type": "string", @@ -967,6 +1075,34 @@ "notification:allow-notify" ] }, + { + "description": "notification:allow-permission-state -> Enables the permission_state command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:allow-permission-state" + ] + }, + { + "description": "notification:allow-register-action-types -> Enables the register_action_types command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:allow-register-action-types" + ] + }, + { + "description": "notification:allow-register-listener -> Enables the register_listener command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:allow-register-listener" + ] + }, + { + "description": "notification:allow-remove-active -> Enables the remove_active command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:allow-remove-active" + ] + }, { "description": "notification:allow-request-permission -> Enables the request_permission command without any pre-configured scope.", "type": "string", @@ -974,6 +1110,62 @@ "notification:allow-request-permission" ] }, + { + "description": "notification:allow-show -> Enables the show command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:allow-show" + ] + }, + { + "description": "notification:deny-batch -> Denies the batch command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:deny-batch" + ] + }, + { + "description": "notification:deny-cancel -> Denies the cancel command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:deny-cancel" + ] + }, + { + "description": "notification:deny-check-permissions -> Denies the check_permissions command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:deny-check-permissions" + ] + }, + { + "description": "notification:deny-create-channel -> Denies the create_channel command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:deny-create-channel" + ] + }, + { + "description": "notification:deny-delete-channel -> Denies the delete_channel command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:deny-delete-channel" + ] + }, + { + "description": "notification:deny-get-active -> Denies the get_active command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:deny-get-active" + ] + }, + { + "description": "notification:deny-get-pending -> Denies the get_pending command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:deny-get-pending" + ] + }, { "description": "notification:deny-is-permission-granted -> Denies the is_permission_granted command without any pre-configured scope.", "type": "string", @@ -981,6 +1173,13 @@ "notification:deny-is-permission-granted" ] }, + { + "description": "notification:deny-list-channels -> Denies the list_channels command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:deny-list-channels" + ] + }, { "description": "notification:deny-notify -> Denies the notify command without any pre-configured scope.", "type": "string", @@ -988,6 +1187,34 @@ "notification:deny-notify" ] }, + { + "description": "notification:deny-permission-state -> Denies the permission_state command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:deny-permission-state" + ] + }, + { + "description": "notification:deny-register-action-types -> Denies the register_action_types command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:deny-register-action-types" + ] + }, + { + "description": "notification:deny-register-listener -> Denies the register_listener command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:deny-register-listener" + ] + }, + { + "description": "notification:deny-remove-active -> Denies the remove_active command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:deny-remove-active" + ] + }, { "description": "notification:deny-request-permission -> Denies the request_permission command without any pre-configured scope.", "type": "string", @@ -996,6 +1223,14 @@ ] }, { + "description": "notification:deny-show -> Denies the show command without any pre-configured scope.", + "type": "string", + "enum": [ + "notification:deny-show" + ] + }, + { + "description": "os:default -> This permission set configures which\noperating system information are available\nto gather from the frontend.\n\n#### Granted Permissions\n\nAll information except the host name are available.\n\n", "type": "string", "enum": [ "os:default" @@ -1254,6 +1489,7 @@ ] }, { + "description": "shell:default -> This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality without any specific\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n", "type": "string", "enum": [ "shell:default" @@ -1280,6 +1516,13 @@ "shell:allow-open" ] }, + { + "description": "shell:allow-spawn -> Enables the spawn command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:allow-spawn" + ] + }, { "description": "shell:allow-stdin-write -> Enables the stdin_write command without any pre-configured scope.", "type": "string", @@ -1308,6 +1551,13 @@ "shell:deny-open" ] }, + { + "description": "shell:deny-spawn -> Denies the spawn command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:deny-spawn" + ] + }, { "description": "shell:deny-stdin-write -> Denies the stdin_write command without any pre-configured scope.", "type": "string", @@ -1819,6 +2069,13 @@ "window:allow-minimize" ] }, + { + "description": "window:allow-monitor-from-point -> Enables the monitor_from_point command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-monitor-from-point" + ] + }, { "description": "window:allow-outer-position -> Enables the outer_position command without any pre-configured scope.", "type": "string", @@ -2253,6 +2510,13 @@ "window:deny-minimize" ] }, + { + "description": "window:deny-monitor-from-point -> Denies the monitor_from_point command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-monitor-from-point" + ] + }, { "description": "window:deny-outer-position -> Denies the outer_position command without any pre-configured scope.", "type": "string", diff --git a/desktop/tauri/src-tauri/src/main.rs b/desktop/tauri/src-tauri/src/main.rs index 5c11e2e9..0384de78 100644 --- a/desktop/tauri/src-tauri/src/main.rs +++ b/desktop/tauri/src-tauri/src/main.rs @@ -26,7 +26,7 @@ use window::{close_splash_window, create_main_window}; #[macro_use] extern crate lazy_static; -const FALLBACK_TO_OLD_UI_EXIT_CODE: i32 = -3; +const FALLBACK_TO_OLD_UI_EXIT_CODE: i32 = 77; #[derive(Clone, serde::Serialize)] struct Payload {