feat(core): shell execute API scope [TRI-002] (#36)

* feat(core): shell execute API scope [TRI-002]

* fix tests

* also check with empty extension

* lockfile
This commit is contained in:
Lucas Fernandes Nogueira
2021-12-01 00:05:14 -03:00
committed by Lucas Nogueira
parent eae311e6e2
commit d4db95e716
10 changed files with 94 additions and 274 deletions

View File

@@ -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.

View File

@@ -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<Url>);
#[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;

View File

@@ -458,6 +458,12 @@ macro_rules! shared_app_impl {
pub fn asset_protocol_scope(&self) -> FsScope {
self.state::<Scopes>().inner().asset_protocol.clone()
}
/// Gets the scope for the shell execute APIs.
#[cfg(shell_execute)]
pub fn shell_scope(&self) -> FsScope {
self.state::<Scopes>().inner().shell.clone()
}
}
};
}
@@ -1027,6 +1033,13 @@ impl<R: Runtime> Builder<R> {
),
#[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);

View File

@@ -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<String>,
on_event_fn: CallbackFn,
#[serde(default)]
@@ -77,7 +79,7 @@ impl Cmd {
#[allow(unused_variables)]
fn execute<R: Runtime>(
context: InvokeContext<R>,
program: String,
program: PathBuf,
args: Vec<String>,
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::<Scopes>().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<String>,
_on_event_fn: CallbackFn,
_options: CommandOptions,

View File

@@ -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<serde_json::Error> for Error {

View File

@@ -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,
}

View File

@@ -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();

View File

@@ -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",

View File

@@ -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"

View File

@@ -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 <https://tauri.studio/en/docs/usage/guides/bundler/sidecar>.",
"default": false,
@@ -1372,6 +1387,9 @@
"all": false,
"execute": false,
"open": false,
"scope": [
"$APP/**"
],
"sidecar": false
},
"window": {