Skip to content

Commit 6aeda78

Browse files
committed
fix(shell): fix schema requiring sidecar property even though it is optional
1 parent 04a0aea commit 6aeda78

File tree

8 files changed

+187
-99
lines changed

8 files changed

+187
-99
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
@@ -20,7 +20,6 @@ serde = { workspace = true }
2020

2121
[dependencies]
2222
serde = { workspace = true }
23-
schemars = { workspace = true }
2423
serde_json = { workspace = true }
2524
tauri = { workspace = true }
2625
tokio = { version = "1", features = ["time"] }

plugins/shell/build.rs

Lines changed: 135 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,149 @@
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+
#[allow(unused)]
69+
pub(crate) struct ShellScopeEntry {
70+
/// The name for this allowed shell command configuration.
71+
///
72+
/// This name will be used inside of the webview API to call this command along with
73+
/// any specified arguments.
74+
name: String,
75+
/// The command name.
76+
/// It can start with a variable that resolves to a system base directory.
77+
/// The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`,
78+
/// `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`,
79+
/// `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`,
80+
/// `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.
81+
// use default just so the schema doesn't flag it as required
82+
#[serde(rename = "cmd")]
83+
command: Option<PathBuf>,
84+
/// The allowed arguments for the command execution.
85+
#[serde(default)]
86+
args: ShellScopeEntryAllowedArgs,
87+
/// If this command is a sidecar command.
88+
#[serde(default)]
89+
sidecar: bool,
90+
}
91+
92+
// Ensure `ShellScopeEntry` and `scope_entry::EntryRaw`
93+
// and `ShellScopeEntryAllowedArg` and `ShellAllowedArg`
94+
// and `ShellScopeEntryAllowedArgs` and `ShellAllowedArgs`
95+
// are kept in sync
96+
fn _f() {
97+
let v = scope_entry::EntryRaw {
98+
name: String::new(),
99+
command: None,
100+
args: scope_entry::ShellAllowedArgs::default(),
101+
sidecar: false,
102+
};
103+
104+
ShellScopeEntry {
105+
name: v.name,
106+
command: v.command,
107+
args: match v.args {
108+
scope_entry::ShellAllowedArgs::Flag(flag) => ShellScopeEntryAllowedArgs::Flag(flag),
109+
scope_entry::ShellAllowedArgs::List(vec) => ShellScopeEntryAllowedArgs::List(
110+
vec.into_iter()
111+
.map(|s| match s {
112+
scope_entry::ShellAllowedArg::Fixed(fixed) => {
113+
ShellScopeEntryAllowedArg::Fixed(fixed)
114+
}
115+
scope_entry::ShellAllowedArg::Var { validator, raw } => {
116+
ShellScopeEntryAllowedArg::Var { validator, raw }
117+
}
118+
})
119+
.collect(),
120+
),
121+
},
122+
sidecar: v.sidecar,
123+
};
124+
125+
match ShellScopeEntryAllowedArgs::Flag(false) {
126+
ShellScopeEntryAllowedArgs::Flag(flag) => scope_entry::ShellAllowedArgs::Flag(flag),
127+
ShellScopeEntryAllowedArgs::List(vec) => scope_entry::ShellAllowedArgs::List(
128+
vec.into_iter()
129+
.map(|s| match s {
130+
ShellScopeEntryAllowedArg::Fixed(fixed) => {
131+
scope_entry::ShellAllowedArg::Fixed(fixed)
132+
}
133+
ShellScopeEntryAllowedArg::Var { validator, raw } => {
134+
scope_entry::ShellAllowedArg::Var { validator, raw }
135+
}
136+
})
137+
.collect(),
138+
),
139+
};
140+
}
141+
8142
const COMMANDS: &[&str] = &["execute", "spawn", "stdin_write", "kill", "open"];
9143

10144
fn main() {
11145
tauri_plugin::Builder::new(COMMANDS)
12146
.global_api_script_path("./api-iife.js")
13-
.global_scope_schema(schemars::schema_for!(scope_entry::Entry))
147+
.global_scope_schema(schemars::schema_for!(ShellScopeEntry))
14148
.android_path("android")
15149
.ios_path("ios")
16150
.build();

plugins/shell/src/scope_entry.rs

Lines changed: 18 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -7,48 +7,31 @@ use serde::{de::Error as DeError, Deserialize, Deserializer};
77
use std::path::PathBuf;
88

99
/// A command allowed to be executed by the webview API.
10-
#[derive(Debug, Clone, PartialEq, Eq, Hash, schemars::JsonSchema)]
11-
pub struct Entry {
12-
/// The name for this allowed shell command configuration.
13-
///
14-
/// This name will be used inside of the webview API to call this command along with
15-
/// any specified arguments.
16-
pub name: String,
10+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11+
pub(crate) struct Entry {
12+
pub(crate) name: String,
13+
pub(crate) command: PathBuf,
14+
pub(crate) args: ShellAllowedArgs,
15+
pub(crate) sidecar: bool,
16+
}
1717

18-
/// The command name.
19-
/// It can start with a variable that resolves to a system base directory.
20-
/// The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`,
21-
/// `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`,
22-
/// `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`,
23-
/// `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.
24-
// use default just so the schema doesn't flag it as required
18+
#[derive(Deserialize)]
19+
pub(crate) struct EntryRaw {
20+
pub(crate) name: String,
2521
#[serde(rename = "cmd")]
26-
pub command: PathBuf,
27-
28-
/// The allowed arguments for the command execution.
29-
pub args: ShellAllowedArgs,
30-
31-
/// If this command is a sidecar command.
32-
pub sidecar: bool,
22+
pub(crate) command: Option<PathBuf>,
23+
#[serde(default)]
24+
pub(crate) args: ShellAllowedArgs,
25+
#[serde(default)]
26+
pub(crate) sidecar: bool,
3327
}
3428

3529
impl<'de> Deserialize<'de> for Entry {
3630
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3731
where
3832
D: Deserializer<'de>,
3933
{
40-
#[derive(Deserialize)]
41-
struct InnerEntry {
42-
name: String,
43-
#[serde(rename = "cmd")]
44-
command: Option<PathBuf>,
45-
#[serde(default)]
46-
args: ShellAllowedArgs,
47-
#[serde(default)]
48-
sidecar: bool,
49-
}
50-
51-
let config = InnerEntry::deserialize(deserializer)?;
34+
let config = EntryRaw::deserialize(deserializer)?;
5235

5336
if !config.sidecar && config.command.is_none() {
5437
return Err(DeError::custom(
@@ -65,19 +48,11 @@ impl<'de> Deserialize<'de> for Entry {
6548
}
6649
}
6750

68-
/// A set of command arguments allowed to be executed by the webview API.
69-
///
70-
/// A value of `true` will allow any arguments to be passed to the command. `false` will disable all
71-
/// arguments. A list of [`ShellAllowedArg`] will set those arguments as the only valid arguments to
72-
/// be passed to the attached command configuration.
73-
#[derive(Debug, PartialEq, Eq, Clone, Hash, Deserialize, schemars::JsonSchema)]
51+
#[derive(Debug, PartialEq, Eq, Clone, Hash, Deserialize)]
7452
#[serde(untagged, deny_unknown_fields)]
7553
#[non_exhaustive]
7654
pub enum ShellAllowedArgs {
77-
/// Use a simple boolean to allow all or disable all arguments to this command configuration.
7855
Flag(bool),
79-
80-
/// A specific set of [`ShellAllowedArg`] that are valid to call for the command configuration.
8156
List(Vec<ShellAllowedArg>),
8257
}
8358

@@ -87,33 +62,13 @@ impl Default for ShellAllowedArgs {
8762
}
8863
}
8964

90-
/// A command argument allowed to be executed by the webview API.
91-
#[derive(Debug, PartialEq, Eq, Clone, Hash, Deserialize, schemars::JsonSchema)]
65+
#[derive(Debug, PartialEq, Eq, Clone, Hash, Deserialize)]
9266
#[serde(untagged, deny_unknown_fields)]
9367
#[non_exhaustive]
9468
pub enum ShellAllowedArg {
95-
/// A non-configurable argument that is passed to the command in the order it was specified.
9669
Fixed(String),
97-
98-
/// A variable that is set while calling the command from the webview API.
99-
///
10070
Var {
101-
/// [regex] validator to require passed values to conform to an expected input.
102-
///
103-
/// This will require the argument value passed to this variable to match the `validator` regex
104-
/// before it will be executed.
105-
///
106-
/// The regex string is by default surrounded by `^...$` to match the full string.
107-
/// For example the `https?://\w+` regex would be registered as `^https?://\w+$`.
108-
///
109-
/// [regex]: <https://docs.rs/regex/latest/regex/#syntax>
11071
validator: String,
111-
112-
/// Marks the validator as a raw regex, meaning the plugin should not make any modification at runtime.
113-
///
114-
/// This means the regex will not match on the entire string by default, which might
115-
/// be exploited if your regex allow unexpected input to be considered valid.
116-
/// When using this option, make sure your regex is correct.
11772
#[serde(default)]
11873
raw: bool,
11974
},

0 commit comments

Comments
 (0)