Skip to content

Commit 44273b9

Browse files
fix(shell): fix schema requiring sidecar property even though it is optional (#1839)
* fix(shell): fix schema requiring `sidecar` property even though it is optional * fix clippy * make `cmd` and `sidecar` exclusive * make args optional * cleanup --------- Co-authored-by: Lucas Nogueira <[email protected]>
1 parent 2f7e32b commit 44273b9

File tree

10 files changed

+213
-101
lines changed

10 files changed

+213
-101
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"shell": "patch"
3+
---
4+
5+
Fix the plugin schema requiring to set `sidecar` property when it is in fact optional.
6+

plugins/fs/build.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,16 @@ enum FsScopeEntry {
2424
},
2525
}
2626

27-
// Ensure scope entry is kept up to date
28-
impl From<FsScopeEntry> for scope::EntryRaw {
29-
fn from(value: FsScopeEntry) -> Self {
30-
match value {
31-
FsScopeEntry::Value(path) => scope::EntryRaw::Value(path),
32-
FsScopeEntry::Object { path } => scope::EntryRaw::Object { path },
33-
}
34-
}
27+
// Ensure `FsScopeEntry` and `scope::EntryRaw` is kept in sync
28+
fn _f() {
29+
match scope::EntryRaw::Value(PathBuf::new()) {
30+
scope::EntryRaw::Value(path) => FsScopeEntry::Value(path),
31+
scope::EntryRaw::Object { path } => FsScopeEntry::Object { path },
32+
};
33+
match FsScopeEntry::Value(PathBuf::new()) {
34+
FsScopeEntry::Value(path) => scope::EntryRaw::Value(path),
35+
FsScopeEntry::Object { path } => scope::EntryRaw::Object { path },
36+
};
3537
}
3638

3739
const BASE_DIR_VARS: &[&str] = &[

plugins/fs/src/scope.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,9 @@ use std::{
1313

1414
use serde::Deserialize;
1515

16-
#[doc(hidden)]
1716
#[derive(Deserialize)]
1817
#[serde(untagged)]
19-
pub enum EntryRaw {
18+
pub(crate) enum EntryRaw {
2019
Value(PathBuf),
2120
Object { path: PathBuf },
2221
}

plugins/http/build.rs

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -47,23 +47,16 @@ enum HttpScopeEntry {
4747
},
4848
}
4949

50-
// Ensure scope entry is kept up to date
51-
impl From<HttpScopeEntry> for scope::Entry {
52-
fn from(value: HttpScopeEntry) -> Self {
53-
let url = match value {
54-
HttpScopeEntry::Value(url) => url,
55-
HttpScopeEntry::Object { url } => url,
56-
};
57-
58-
scope::Entry {
59-
url: urlpattern::UrlPattern::parse(
60-
urlpattern::UrlPatternInit::parse_constructor_string::<regex::Regex>(&url, None)
61-
.unwrap(),
62-
Default::default(),
63-
)
64-
.unwrap(),
65-
}
66-
}
50+
// Ensure `HttpScopeEntry` and `scope::EntryRaw` is kept in sync
51+
fn _f() {
52+
match scope::EntryRaw::Value(String::new()) {
53+
scope::EntryRaw::Value(url) => HttpScopeEntry::Value(url),
54+
scope::EntryRaw::Object { url } => HttpScopeEntry::Object { url },
55+
};
56+
match HttpScopeEntry::Value(String::new()) {
57+
HttpScopeEntry::Value(url) => scope::EntryRaw::Value(url),
58+
HttpScopeEntry::Object { url } => scope::EntryRaw::Object { url },
59+
};
6760
}
6861

6962
fn main() {

plugins/http/src/scope.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,18 @@ fn parse_url_pattern(s: &str) -> Result<UrlPattern, urlpattern::quirks::Error> {
3333
UrlPattern::parse(init, Default::default())
3434
}
3535

36+
#[derive(Deserialize)]
37+
#[serde(untagged)]
38+
pub(crate) enum EntryRaw {
39+
Value(String),
40+
Object { url: String },
41+
}
42+
3643
impl<'de> Deserialize<'de> for Entry {
3744
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
3845
where
3946
D: Deserializer<'de>,
4047
{
41-
#[derive(Deserialize)]
42-
#[serde(untagged)]
43-
enum EntryRaw {
44-
Value(String),
45-
Object { url: String },
46-
}
47-
4848
EntryRaw::deserialize(deserializer).and_then(|raw| {
4949
let url = match raw {
5050
EntryRaw::Value(url) => url,

plugins/shell/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ serde = { workspace = true }
2727

2828
[dependencies]
2929
serde = { workspace = true }
30-
schemars = { workspace = true }
3130
serde_json = { workspace = true }
3231
tauri = { workspace = true }
3332
tokio = { version = "1", features = ["time"] }

plugins/shell/build.rs

Lines changed: 159 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,173 @@
22
// SPDX-License-Identifier: Apache-2.0
33
// SPDX-License-Identifier: MIT
44

5+
use std::path::PathBuf;
6+
7+
use schemars::JsonSchema;
8+
59
#[path = "src/scope_entry.rs"]
610
mod scope_entry;
711

12+
/// A command argument allowed to be executed by the webview API.
13+
#[derive(Debug, PartialEq, Eq, Clone, Hash, schemars::JsonSchema)]
14+
#[serde(untagged, deny_unknown_fields)]
15+
#[non_exhaustive]
16+
pub enum ShellScopeEntryAllowedArg {
17+
/// A non-configurable argument that is passed to the command in the order it was specified.
18+
Fixed(String),
19+
20+
/// A variable that is set while calling the command from the webview API.
21+
///
22+
Var {
23+
/// [regex] validator to require passed values to conform to an expected input.
24+
///
25+
/// This will require the argument value passed to this variable to match the `validator` regex
26+
/// before it will be executed.
27+
///
28+
/// The regex string is by default surrounded by `^...$` to match the full string.
29+
/// For example the `https?://\w+` regex would be registered as `^https?://\w+$`.
30+
///
31+
/// [regex]: <https://docs.rs/regex/latest/regex/#syntax>
32+
validator: String,
33+
34+
/// Marks the validator as a raw regex, meaning the plugin should not make any modification at runtime.
35+
///
36+
/// This means the regex will not match on the entire string by default, which might
37+
/// be exploited if your regex allow unexpected input to be considered valid.
38+
/// When using this option, make sure your regex is correct.
39+
#[serde(default)]
40+
raw: bool,
41+
},
42+
}
43+
44+
/// A set of command arguments allowed to be executed by the webview API.
45+
///
46+
/// A value of `true` will allow any arguments to be passed to the command. `false` will disable all
47+
/// arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to
48+
/// be passed to the attached command configuration.
49+
#[derive(Debug, PartialEq, Eq, Clone, Hash, JsonSchema)]
50+
#[serde(untagged, deny_unknown_fields)]
51+
#[non_exhaustive]
52+
pub enum ShellScopeEntryAllowedArgs {
53+
/// Use a simple boolean to allow all or disable all arguments to this command configuration.
54+
Flag(bool),
55+
56+
/// A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.
57+
List(Vec<ShellScopeEntryAllowedArg>),
58+
}
59+
60+
impl Default for ShellScopeEntryAllowedArgs {
61+
fn default() -> Self {
62+
Self::Flag(false)
63+
}
64+
}
65+
66+
/// Shell scope entry.
67+
#[derive(JsonSchema)]
68+
#[serde(untagged, deny_unknown_fields)]
69+
#[allow(unused)]
70+
pub(crate) enum ShellScopeEntry {
71+
Command {
72+
/// The name for this allowed shell command configuration.
73+
///
74+
/// This name will be used inside of the webview API to call this command along with
75+
/// any specified arguments.
76+
name: String,
77+
/// The command name.
78+
/// It can start with a variable that resolves to a system base directory.
79+
/// The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`,
80+
/// `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`,
81+
/// `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`,
82+
/// `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.
83+
// use default just so the schema doesn't flag it as required
84+
#[serde(rename = "cmd")]
85+
command: PathBuf,
86+
/// The allowed arguments for the command execution.
87+
#[serde(default)]
88+
args: ShellScopeEntryAllowedArgs,
89+
},
90+
Sidecar {
91+
/// The name for this allowed shell command configuration.
92+
///
93+
/// This name will be used inside of the webview API to call this command along with
94+
/// any specified arguments.
95+
name: String,
96+
/// The allowed arguments for the command execution.
97+
#[serde(default)]
98+
args: ShellScopeEntryAllowedArgs,
99+
/// If this command is a sidecar command.
100+
sidecar: bool,
101+
},
102+
}
103+
104+
// Ensure `ShellScopeEntry` and `scope_entry::EntryRaw`
105+
// and `ShellScopeEntryAllowedArg` and `ShellAllowedArg`
106+
// and `ShellScopeEntryAllowedArgs` and `ShellAllowedArgs`
107+
// are kept in sync
108+
#[allow(clippy::unnecessary_operation)]
109+
fn _f() {
110+
match (ShellScopeEntry::Sidecar {
111+
name: String::new(),
112+
args: ShellScopeEntryAllowedArgs::Flag(false),
113+
sidecar: true,
114+
}) {
115+
ShellScopeEntry::Command {
116+
name,
117+
command,
118+
args,
119+
} => scope_entry::EntryRaw {
120+
name,
121+
command: Some(command),
122+
args: match args {
123+
ShellScopeEntryAllowedArgs::Flag(flag) => scope_entry::ShellAllowedArgs::Flag(flag),
124+
ShellScopeEntryAllowedArgs::List(vec) => scope_entry::ShellAllowedArgs::List(
125+
vec.into_iter()
126+
.map(|s| match s {
127+
ShellScopeEntryAllowedArg::Fixed(fixed) => {
128+
scope_entry::ShellAllowedArg::Fixed(fixed)
129+
}
130+
ShellScopeEntryAllowedArg::Var { validator, raw } => {
131+
scope_entry::ShellAllowedArg::Var { validator, raw }
132+
}
133+
})
134+
.collect(),
135+
),
136+
},
137+
sidecar: false,
138+
},
139+
ShellScopeEntry::Sidecar {
140+
name,
141+
args,
142+
sidecar,
143+
} => scope_entry::EntryRaw {
144+
name,
145+
command: None,
146+
args: match args {
147+
ShellScopeEntryAllowedArgs::Flag(flag) => scope_entry::ShellAllowedArgs::Flag(flag),
148+
ShellScopeEntryAllowedArgs::List(vec) => scope_entry::ShellAllowedArgs::List(
149+
vec.into_iter()
150+
.map(|s| match s {
151+
ShellScopeEntryAllowedArg::Fixed(fixed) => {
152+
scope_entry::ShellAllowedArg::Fixed(fixed)
153+
}
154+
ShellScopeEntryAllowedArg::Var { validator, raw } => {
155+
scope_entry::ShellAllowedArg::Var { validator, raw }
156+
}
157+
})
158+
.collect(),
159+
),
160+
},
161+
sidecar,
162+
},
163+
};
164+
}
165+
8166
const COMMANDS: &[&str] = &["execute", "spawn", "stdin_write", "kill", "open"];
9167

10168
fn main() {
11169
tauri_plugin::Builder::new(COMMANDS)
12170
.global_api_script_path("./api-iife.js")
13-
.global_scope_schema(schemars::schema_for!(scope_entry::Entry))
171+
.global_scope_schema(schemars::schema_for!(ShellScopeEntry))
14172
.android_path("android")
15173
.ios_path("ios")
16174
.build();

0 commit comments

Comments
 (0)