Skip to content

Commit d4db95e

Browse files
committed
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
1 parent eae311e commit d4db95e

10 files changed

Lines changed: 94 additions & 274 deletions

File tree

.changes/scopes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55
Scopes the `filesystem` APIs from the webview access using `tauri.conf.json > tauri > allowlist > fs > scope`.
66
Scopes the `asset` protocol access using `tauri.conf.json > tauri > allowlist > protocol > assetScope`.
77
Scopes the `http` APIs from the webview access using `tauri.conf.json > tauri > allowlist > http > scope`.
8+
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.

core/tauri-utils/src/config.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -575,8 +575,8 @@ macro_rules! check_feature {
575575
};
576576
}
577577

578-
/// Filesystem API scope definition.
579-
/// It is a list of glob patterns that restrict the filesystem API access from the webview.
578+
/// Filesystem scope definition.
579+
/// It is a list of glob patterns that restrict the API access from the webview.
580580
/// Each pattern can start with a variable that resolves to a system base directory.
581581
/// The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`,
582582
/// `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`,
@@ -830,6 +830,10 @@ impl Allowlist for WindowAllowlistConfig {
830830
#[cfg_attr(feature = "schema", derive(JsonSchema))]
831831
#[serde(rename_all = "camelCase", deny_unknown_fields)]
832832
pub struct ShellAllowlistConfig {
833+
/// Access scope for the binary execution APIs.
834+
/// Sidecars are automatically enabled.
835+
#[serde(default)]
836+
pub scope: FsAllowlistScope,
833837
/// Use this flag to enable all shell API features.
834838
#[serde(default)]
835839
pub all: bool,
@@ -849,6 +853,7 @@ pub struct ShellAllowlistConfig {
849853
impl Allowlist for ShellAllowlistConfig {
850854
fn all_features() -> Vec<&'static str> {
851855
let allowlist = Self {
856+
scope: Default::default(),
852857
all: false,
853858
execute: true,
854859
sidecar: true,
@@ -939,6 +944,7 @@ pub struct HttpAllowlistScope(pub Vec<Url>);
939944
#[serde(rename_all = "camelCase", deny_unknown_fields)]
940945
pub struct HttpAllowlistConfig {
941946
/// The access scope for the HTTP APIs.
947+
#[serde(default)]
942948
pub scope: HttpAllowlistScope,
943949
/// Use this flag to enable all HTTP API features.
944950
#[serde(default)]
@@ -1955,7 +1961,7 @@ mod build {
19551961
let long_description = quote!(None);
19561962
let deb = quote!(Default::default());
19571963
let macos = quote!(Default::default());
1958-
let external_bin = quote!(None);
1964+
let external_bin = opt_vec_str_lit(self.external_bin.as_ref());
19591965
let windows = &self.windows;
19601966

19611967
literal_struct!(
@@ -2081,6 +2087,13 @@ mod build {
20812087
}
20822088
}
20832089

2090+
impl ToTokens for ShellAllowlistConfig {
2091+
fn to_tokens(&self, tokens: &mut TokenStream) {
2092+
let scope = &self.scope;
2093+
tokens.append_all(quote! { ::tauri::utils::config::ShellAllowlistConfig { scope: #scope, ..Default::default() } })
2094+
}
2095+
}
2096+
20842097
impl ToTokens for AllowlistConfig {
20852098
fn to_tokens(&self, tokens: &mut TokenStream) {
20862099
let fs = &self.fs;

core/tauri/src/app.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,12 @@ macro_rules! shared_app_impl {
458458
pub fn asset_protocol_scope(&self) -> FsScope {
459459
self.state::<Scopes>().inner().asset_protocol.clone()
460460
}
461+
462+
/// Gets the scope for the shell execute APIs.
463+
#[cfg(shell_execute)]
464+
pub fn shell_scope(&self) -> FsScope {
465+
self.state::<Scopes>().inner().shell.clone()
466+
}
461467
}
462468
};
463469
}
@@ -1027,6 +1033,13 @@ impl<R: Runtime> Builder<R> {
10271033
),
10281034
#[cfg(http_request)]
10291035
http: crate::scope::HttpScope::for_http_api(&app.config().tauri.allowlist.http.scope),
1036+
#[cfg(shell_execute)]
1037+
shell: FsScope::for_fs_api(
1038+
&app.manager.config(),
1039+
app.package_info(),
1040+
&env,
1041+
&app.config().tauri.allowlist.shell.scope,
1042+
),
10301043
});
10311044
app.manage(env);
10321045

core/tauri/src/endpoints/shell.rs

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
use super::InvokeContext;
66
use crate::{api::ipc::CallbackFn, Runtime};
7+
#[cfg(shell_execute)]
8+
use crate::{Manager, Scopes};
79
use serde::Deserialize;
810
use tauri_macros::{module_command_handler, CommandModule};
911

@@ -54,7 +56,7 @@ pub enum Cmd {
5456
/// The execute script API.
5557
#[serde(rename_all = "camelCase")]
5658
Execute {
57-
program: String,
59+
program: PathBuf,
5860
args: Vec<String>,
5961
on_event_fn: CallbackFn,
6062
#[serde(default)]
@@ -77,7 +79,7 @@ impl Cmd {
7779
#[allow(unused_variables)]
7880
fn execute<R: Runtime>(
7981
context: InvokeContext<R>,
80-
program: String,
82+
program: PathBuf,
8183
args: Vec<String>,
8284
on_event_fn: CallbackFn,
8385
options: CommandOptions,
@@ -88,14 +90,38 @@ impl Cmd {
8890
"shell > sidecar".to_string(),
8991
));
9092
#[cfg(shell_sidecar)]
91-
crate::api::process::Command::new_sidecar(program)?
93+
{
94+
let program_as_string = program.display().to_string();
95+
let program_no_ext_as_string = program.with_extension("").display().to_string();
96+
let is_configured = context
97+
.config
98+
.tauri
99+
.bundle
100+
.external_bin
101+
.as_ref()
102+
.map(|bins| {
103+
bins
104+
.iter()
105+
.any(|b| b == &program_as_string || b == program_no_ext_as_string)
106+
})
107+
.unwrap_or_default();
108+
if is_configured {
109+
crate::api::process::Command::new_sidecar(program_as_string)?
110+
} else {
111+
return Err(crate::Error::SidecarNotAllowed(program));
112+
}
113+
}
92114
} else {
93115
#[cfg(not(shell_execute))]
94116
return Err(crate::Error::ApiNotAllowlisted(
95117
"shell > execute".to_string(),
96118
));
97119
#[cfg(shell_execute)]
98-
crate::api::process::Command::new(program)
120+
if context.window.state::<Scopes>().shell.is_allowed(&program) {
121+
crate::api::process::Command::new(program.display().to_string())
122+
} else {
123+
return Err(crate::Error::ProgramNotAllowed(program));
124+
}
99125
};
100126
#[cfg(any(shell_execute, shell_sidecar))]
101127
{
@@ -195,6 +221,7 @@ impl Cmd {
195221
mod tests {
196222
use super::{Buffer, ChildId, CommandOptions};
197223
use crate::api::ipc::CallbackFn;
224+
use std::path::PathBuf;
198225

199226
use quickcheck::{Arbitrary, Gen};
200227

@@ -217,7 +244,7 @@ mod tests {
217244
#[tauri_macros::module_command_test(shell_execute, "shell > execute")]
218245
#[quickcheck_macros::quickcheck]
219246
fn execute(
220-
_program: String,
247+
_program: PathBuf,
221248
_args: Vec<String>,
222249
_on_event_fn: CallbackFn,
223250
_options: CommandOptions,

core/tauri/src/error.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@ pub enum Error {
9090
/// URL not allowed by the scope.
9191
#[error("url not allowed on the configured scope: {0}")]
9292
UrlNotAllowed(url::Url),
93+
/// Sidecar not allowed by the configuration.
94+
#[error("sidecar not configured under `tauri.conf.json > tauri > bundle > externalBin`: {0}")]
95+
SidecarNotAllowed(PathBuf),
96+
/// Program not allowed by the scope.
97+
#[error("program not allowed on the configured shell scope: {0}")]
98+
ProgramNotAllowed(PathBuf),
9399
}
94100

95101
impl From<serde_json::Error> for Error {

core/tauri/src/scope/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@
44

55
mod fs;
66
mod http;
7-
mod shell;
87

98
pub use self::http::Scope as HttpScope;
109
pub use fs::Scope as FsScope;
11-
pub use shell::Scope as ShellScope;
1210

1311
pub(crate) struct Scopes {
1412
pub fs: FsScope,
1513
#[cfg(protocol_asset)]
1614
pub asset_protocol: FsScope,
1715
#[cfg(http_request)]
1816
pub http: HttpScope,
17+
#[cfg(shell_execute)]
18+
pub shell: FsScope,
1919
}

core/tauri/src/scope/shell.rs

Lines changed: 0 additions & 6 deletions
This file was deleted.

core/tauri/tests/restart/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)