diff --git a/.changes/scopes.md b/.changes/scopes.md index 20a3573be..096838e7b 100644 --- a/.changes/scopes.md +++ b/.changes/scopes.md @@ -5,3 +5,4 @@ Scopes the `filesystem` APIs from the webview access using `tauri.conf.json > tauri > allowlist > fs > scope`. Scopes the `asset` protocol access using `tauri.conf.json > tauri > allowlist > protocol > assetScope`. Scopes the `http` APIs from the webview access using `tauri.conf.json > tauri > allowlist > http > scope`. +Scopes the `shell` execute API from the webview access using `tauri.conf.json > tauri > allowlist > shell > scope`. Additionally, check the `tauri.conf.json > tauri > bundle > externalBin` to prevent access to unknown sidecars. diff --git a/core/tauri-utils/src/config.rs b/core/tauri-utils/src/config.rs index 97d54e8cc..57e1fa1f7 100644 --- a/core/tauri-utils/src/config.rs +++ b/core/tauri-utils/src/config.rs @@ -575,8 +575,8 @@ macro_rules! check_feature { }; } -/// Filesystem API scope definition. -/// It is a list of glob patterns that restrict the filesystem API access from the webview. +/// Filesystem scope definition. +/// It is a list of glob patterns that restrict the API access from the webview. /// Each pattern can start with a variable that resolves to a system base directory. /// The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, /// `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, @@ -830,6 +830,10 @@ impl Allowlist for WindowAllowlistConfig { #[cfg_attr(feature = "schema", derive(JsonSchema))] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct ShellAllowlistConfig { + /// Access scope for the binary execution APIs. + /// Sidecars are automatically enabled. + #[serde(default)] + pub scope: FsAllowlistScope, /// Use this flag to enable all shell API features. #[serde(default)] pub all: bool, @@ -849,6 +853,7 @@ pub struct ShellAllowlistConfig { impl Allowlist for ShellAllowlistConfig { fn all_features() -> Vec<&'static str> { let allowlist = Self { + scope: Default::default(), all: false, execute: true, sidecar: true, @@ -939,6 +944,7 @@ pub struct HttpAllowlistScope(pub Vec); #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct HttpAllowlistConfig { /// The access scope for the HTTP APIs. + #[serde(default)] pub scope: HttpAllowlistScope, /// Use this flag to enable all HTTP API features. #[serde(default)] @@ -1955,7 +1961,7 @@ mod build { let long_description = quote!(None); let deb = quote!(Default::default()); let macos = quote!(Default::default()); - let external_bin = quote!(None); + let external_bin = opt_vec_str_lit(self.external_bin.as_ref()); let windows = &self.windows; literal_struct!( @@ -2081,6 +2087,13 @@ mod build { } } + impl ToTokens for ShellAllowlistConfig { + fn to_tokens(&self, tokens: &mut TokenStream) { + let scope = &self.scope; + tokens.append_all(quote! { ::tauri::utils::config::ShellAllowlistConfig { scope: #scope, ..Default::default() } }) + } + } + impl ToTokens for AllowlistConfig { fn to_tokens(&self, tokens: &mut TokenStream) { let fs = &self.fs; diff --git a/core/tauri/src/app.rs b/core/tauri/src/app.rs index 434ff9006..2505b570c 100644 --- a/core/tauri/src/app.rs +++ b/core/tauri/src/app.rs @@ -458,6 +458,12 @@ macro_rules! shared_app_impl { pub fn asset_protocol_scope(&self) -> FsScope { self.state::().inner().asset_protocol.clone() } + + /// Gets the scope for the shell execute APIs. + #[cfg(shell_execute)] + pub fn shell_scope(&self) -> FsScope { + self.state::().inner().shell.clone() + } } }; } @@ -1027,6 +1033,13 @@ impl Builder { ), #[cfg(http_request)] http: crate::scope::HttpScope::for_http_api(&app.config().tauri.allowlist.http.scope), + #[cfg(shell_execute)] + shell: FsScope::for_fs_api( + &app.manager.config(), + app.package_info(), + &env, + &app.config().tauri.allowlist.shell.scope, + ), }); app.manage(env); diff --git a/core/tauri/src/endpoints/shell.rs b/core/tauri/src/endpoints/shell.rs index 8557a1556..a5dae251f 100644 --- a/core/tauri/src/endpoints/shell.rs +++ b/core/tauri/src/endpoints/shell.rs @@ -4,6 +4,8 @@ use super::InvokeContext; use crate::{api::ipc::CallbackFn, Runtime}; +#[cfg(shell_execute)] +use crate::{Manager, Scopes}; use serde::Deserialize; use tauri_macros::{module_command_handler, CommandModule}; @@ -54,7 +56,7 @@ pub enum Cmd { /// The execute script API. #[serde(rename_all = "camelCase")] Execute { - program: String, + program: PathBuf, args: Vec, on_event_fn: CallbackFn, #[serde(default)] @@ -77,7 +79,7 @@ impl Cmd { #[allow(unused_variables)] fn execute( context: InvokeContext, - program: String, + program: PathBuf, args: Vec, on_event_fn: CallbackFn, options: CommandOptions, @@ -88,14 +90,38 @@ impl Cmd { "shell > sidecar".to_string(), )); #[cfg(shell_sidecar)] - crate::api::process::Command::new_sidecar(program)? + { + let program_as_string = program.display().to_string(); + let program_no_ext_as_string = program.with_extension("").display().to_string(); + let is_configured = context + .config + .tauri + .bundle + .external_bin + .as_ref() + .map(|bins| { + bins + .iter() + .any(|b| b == &program_as_string || b == program_no_ext_as_string) + }) + .unwrap_or_default(); + if is_configured { + crate::api::process::Command::new_sidecar(program_as_string)? + } else { + return Err(crate::Error::SidecarNotAllowed(program)); + } + } } else { #[cfg(not(shell_execute))] return Err(crate::Error::ApiNotAllowlisted( "shell > execute".to_string(), )); #[cfg(shell_execute)] - crate::api::process::Command::new(program) + if context.window.state::().shell.is_allowed(&program) { + crate::api::process::Command::new(program.display().to_string()) + } else { + return Err(crate::Error::ProgramNotAllowed(program)); + } }; #[cfg(any(shell_execute, shell_sidecar))] { @@ -195,6 +221,7 @@ impl Cmd { mod tests { use super::{Buffer, ChildId, CommandOptions}; use crate::api::ipc::CallbackFn; + use std::path::PathBuf; use quickcheck::{Arbitrary, Gen}; @@ -217,7 +244,7 @@ mod tests { #[tauri_macros::module_command_test(shell_execute, "shell > execute")] #[quickcheck_macros::quickcheck] fn execute( - _program: String, + _program: PathBuf, _args: Vec, _on_event_fn: CallbackFn, _options: CommandOptions, diff --git a/core/tauri/src/error.rs b/core/tauri/src/error.rs index 1bb933767..7c403258c 100644 --- a/core/tauri/src/error.rs +++ b/core/tauri/src/error.rs @@ -90,6 +90,12 @@ pub enum Error { /// URL not allowed by the scope. #[error("url not allowed on the configured scope: {0}")] UrlNotAllowed(url::Url), + /// Sidecar not allowed by the configuration. + #[error("sidecar not configured under `tauri.conf.json > tauri > bundle > externalBin`: {0}")] + SidecarNotAllowed(PathBuf), + /// Program not allowed by the scope. + #[error("program not allowed on the configured shell scope: {0}")] + ProgramNotAllowed(PathBuf), } impl From for Error { diff --git a/core/tauri/src/scope/mod.rs b/core/tauri/src/scope/mod.rs index 09eec7f62..2670acf47 100644 --- a/core/tauri/src/scope/mod.rs +++ b/core/tauri/src/scope/mod.rs @@ -4,11 +4,9 @@ mod fs; mod http; -mod shell; pub use self::http::Scope as HttpScope; pub use fs::Scope as FsScope; -pub use shell::Scope as ShellScope; pub(crate) struct Scopes { pub fs: FsScope, @@ -16,4 +14,6 @@ pub(crate) struct Scopes { pub asset_protocol: FsScope, #[cfg(http_request)] pub http: HttpScope, + #[cfg(shell_execute)] + pub shell: FsScope, } diff --git a/core/tauri/src/scope/shell.rs b/core/tauri/src/scope/shell.rs deleted file mode 100644 index 1386bfe13..000000000 --- a/core/tauri/src/scope/shell.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2019-2021 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -/// Scope for the shell API access. -pub struct Scope(); diff --git a/core/tauri/tests/restart/Cargo.lock b/core/tauri/tests/restart/Cargo.lock index 24791b115..b505a44f5 100644 --- a/core/tauri/tests/restart/Cargo.lock +++ b/core/tauri/tests/restart/Cargo.lock @@ -3107,7 +3107,7 @@ checksum = "2c8d5cf83fb08083438c5c46723e6206b2970da57ce314f80b57724439aaacab" [[package]] name = "wry" version = "0.12.2" -source = "git+https://github.com/tauri-sec/wry?branch=next#0f9eb9b1b9288b5be443bced0e8fa58a2479c491" +source = "git+ssh://git@github.com/tauri-sec/wry?branch=next#0f9eb9b1b9288b5be443bced0e8fa58a2479c491" dependencies = [ "cocoa", "core-graphics 0.22.3", diff --git a/examples/sidecar/src-tauri/Cargo.lock b/examples/sidecar/src-tauri/Cargo.lock index 323390a3b..543d70d2e 100644 --- a/examples/sidecar/src-tauri/Cargo.lock +++ b/examples/sidecar/src-tauri/Cargo.lock @@ -74,24 +74,6 @@ dependencies = [ "system-deps 3.2.0", ] -[[package]] -name = "attohttpc" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e69e13a99a7e6e070bb114f7ff381e58c7ccc188630121fc4c2fe4bcf24cd072" -dependencies = [ - "flate2", - "http", - "log", - "native-tls", - "openssl", - "serde", - "serde_json", - "serde_urlencoded", - "url", - "wildmatch", -] - [[package]] name = "autocfg" version = "1.0.1" @@ -158,12 +140,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "bumpalo" -version = "3.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" - [[package]] name = "byteorder" version = "1.4.3" @@ -1378,15 +1354,6 @@ dependencies = [ "libc", ] -[[package]] -name = "js-sys" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" -dependencies = [ - "wasm-bindgen", -] - [[package]] name = "kuchiki" version = "0.8.1" @@ -1522,24 +1489,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "native-tls" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "ndk" version = "0.4.0" @@ -1670,17 +1619,6 @@ dependencies = [ "objc_exception", ] -[[package]] -name = "objc-foundation" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" -dependencies = [ - "block", - "objc", - "objc_id", -] - [[package]] name = "objc_exception" version = "0.1.2" @@ -1711,39 +1649,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" -[[package]] -name = "openssl" -version = "0.10.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9facdb76fec0b73c406f125d44d86fdad818d66fef0531eec9233ca425ff4a" -dependencies = [ - "bitflags", - "cfg-if 1.0.0", - "foreign-types", - "libc", - "once_cell", - "openssl-sys", -] - -[[package]] -name = "openssl-probe" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" - -[[package]] -name = "openssl-sys" -version = "0.9.67" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69df2d8dfc6ce3aaf44b40dec6f487d5a886516cf6879c49e98e0710f310a058" -dependencies = [ - "autocfg", - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "os_pipe" version = "0.9.2" @@ -2229,29 +2134,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "rfd" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acac5884e3a23b02ebd6ce50fd2729732cdbdb16ea944fbbfbfa638a67992aa" -dependencies = [ - "block", - "dispatch", - "glib-sys 0.14.0", - "gobject-sys 0.14.0", - "gtk-sys", - "js-sys", - "lazy_static", - "objc", - "objc-foundation", - "objc_id", - "raw-window-handle", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winapi", -] - [[package]] name = "rustc_version" version = "0.3.3" @@ -2282,16 +2164,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "schannel" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" -dependencies = [ - "lazy_static", - "winapi", -] - [[package]] name = "scoped-tls" version = "1.0.0" @@ -2304,29 +2176,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "security-framework" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87" -dependencies = [ - "bitflags", - "core-foundation 0.9.2", - "core-foundation-sys 0.8.3", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" -dependencies = [ - "core-foundation-sys 0.8.3", - "libc", -] - [[package]] name = "selectors" version = "0.22.0" @@ -2413,18 +2262,6 @@ dependencies = [ "syn", ] -[[package]] -name = "serde_urlencoded" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - [[package]] name = "serde_with" version = "1.11.0" @@ -2733,7 +2570,6 @@ dependencies = [ name = "tauri" version = "1.0.0-beta.8" dependencies = [ - "attohttpc", "bincode", "cfg_aliases", "data-url", @@ -2754,7 +2590,6 @@ dependencies = [ "rand 0.8.4", "raw-window-handle", "regex", - "rfd", "semver 1.0.4", "serde", "serde_json", @@ -2809,6 +2644,7 @@ dependencies = [ name = "tauri-macros" version = "1.0.0-beta.5" dependencies = [ + "heck", "proc-macro2", "quote", "syn", @@ -3113,12 +2949,6 @@ dependencies = [ "getrandom 0.2.3", ] -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version-compare" version = "0.0.10" @@ -3166,82 +2996,6 @@ version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" -[[package]] -name = "wasm-bindgen" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" -dependencies = [ - "cfg-if 1.0.0", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" -dependencies = [ - "bumpalo", - "lazy_static", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" -dependencies = [ - "cfg-if 1.0.0", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" - -[[package]] -name = "web-sys" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "webkit2gtk" version = "0.15.1" @@ -3323,12 +3077,6 @@ dependencies = [ "windows", ] -[[package]] -name = "wildmatch" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c48bd20df7e4ced539c12f570f937c6b4884928a87fee70a479d72f031d4e0" - [[package]] name = "winapi" version = "0.3.9" diff --git a/tooling/cli.rs/schema.json b/tooling/cli.rs/schema.json index 905b02fea..bc1bfe740 100644 --- a/tooling/cli.rs/schema.json +++ b/tooling/cli.rs/schema.json @@ -103,6 +103,9 @@ "all": false, "execute": false, "open": false, + "scope": [ + "$APP/**" + ], "sidecar": false }, "window": { @@ -339,6 +342,9 @@ "all": false, "execute": false, "open": false, + "scope": [ + "$APP/**" + ], "sidecar": false }, "allOf": [ @@ -1013,7 +1019,7 @@ "additionalProperties": false }, "FsAllowlistScope": { - "description": "Filesystem API scope definition. It is a list of glob patterns that restrict the filesystem API access from the webview. Each pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$CWD`.", + "description": "Filesystem scope definition. It is a list of glob patterns that restrict the API access from the webview. Each pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$CWD`.", "type": "array", "items": { "type": "string" @@ -1034,9 +1040,6 @@ "HttpAllowlistConfig": { "description": "Allowlist for the HTTP APIs.", "type": "object", - "required": [ - "scope" - ], "properties": { "all": { "description": "Use this flag to enable all HTTP API features.", @@ -1050,6 +1053,7 @@ }, "scope": { "description": "The access scope for the HTTP APIs.", + "default": [], "allOf": [ { "$ref": "#/definitions/HttpAllowlistScope" @@ -1277,6 +1281,17 @@ "default": false, "type": "boolean" }, + "scope": { + "description": "Access scope for the binary execution APIs. Sidecars are automatically enabled.", + "default": [ + "$APP/**" + ], + "allOf": [ + { + "$ref": "#/definitions/FsAllowlistScope" + } + ] + }, "sidecar": { "description": "Enable sidecar execution, allowing the JavaScript layer to spawn a sidecar program, an executable that is shipped with the application. For more information see .", "default": false, @@ -1372,6 +1387,9 @@ "all": false, "execute": false, "open": false, + "scope": [ + "$APP/**" + ], "sidecar": false }, "window": {