Skip to content

Commit

Permalink
fix(shell): fix schema requiring sidecar property even though it is…
Browse files Browse the repository at this point in the history
… 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 <lucas@tauri.app>
  • Loading branch information
amrbashir and lucasfernog authored Oct 1, 2024
1 parent 2f7e32b commit 44273b9
Show file tree
Hide file tree
Showing 10 changed files with 213 additions and 101 deletions.
6 changes: 6 additions & 0 deletions .changes/shell-schema-required-sidcar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"shell": "patch"
---

Fix the plugin schema requiring to set `sidecar` property when it is in fact optional.

18 changes: 10 additions & 8 deletions plugins/fs/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@ enum FsScopeEntry {
},
}

// Ensure scope entry is kept up to date
impl From<FsScopeEntry> for scope::EntryRaw {
fn from(value: FsScopeEntry) -> Self {
match value {
FsScopeEntry::Value(path) => scope::EntryRaw::Value(path),
FsScopeEntry::Object { path } => scope::EntryRaw::Object { path },
}
}
// Ensure `FsScopeEntry` and `scope::EntryRaw` is kept in sync
fn _f() {
match scope::EntryRaw::Value(PathBuf::new()) {
scope::EntryRaw::Value(path) => FsScopeEntry::Value(path),
scope::EntryRaw::Object { path } => FsScopeEntry::Object { path },
};
match FsScopeEntry::Value(PathBuf::new()) {
FsScopeEntry::Value(path) => scope::EntryRaw::Value(path),
FsScopeEntry::Object { path } => scope::EntryRaw::Object { path },
};
}

const BASE_DIR_VARS: &[&str] = &[
Expand Down
3 changes: 1 addition & 2 deletions plugins/fs/src/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@ use std::{

use serde::Deserialize;

#[doc(hidden)]
#[derive(Deserialize)]
#[serde(untagged)]
pub enum EntryRaw {
pub(crate) enum EntryRaw {
Value(PathBuf),
Object { path: PathBuf },
}
Expand Down
27 changes: 10 additions & 17 deletions plugins/http/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,23 +47,16 @@ enum HttpScopeEntry {
},
}

// Ensure scope entry is kept up to date
impl From<HttpScopeEntry> for scope::Entry {
fn from(value: HttpScopeEntry) -> Self {
let url = match value {
HttpScopeEntry::Value(url) => url,
HttpScopeEntry::Object { url } => url,
};

scope::Entry {
url: urlpattern::UrlPattern::parse(
urlpattern::UrlPatternInit::parse_constructor_string::<regex::Regex>(&url, None)
.unwrap(),
Default::default(),
)
.unwrap(),
}
}
// Ensure `HttpScopeEntry` and `scope::EntryRaw` is kept in sync
fn _f() {
match scope::EntryRaw::Value(String::new()) {
scope::EntryRaw::Value(url) => HttpScopeEntry::Value(url),
scope::EntryRaw::Object { url } => HttpScopeEntry::Object { url },
};
match HttpScopeEntry::Value(String::new()) {
HttpScopeEntry::Value(url) => scope::EntryRaw::Value(url),
HttpScopeEntry::Object { url } => scope::EntryRaw::Object { url },
};
}

fn main() {
Expand Down
14 changes: 7 additions & 7 deletions plugins/http/src/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,18 @@ fn parse_url_pattern(s: &str) -> Result<UrlPattern, urlpattern::quirks::Error> {
UrlPattern::parse(init, Default::default())
}

#[derive(Deserialize)]
#[serde(untagged)]
pub(crate) enum EntryRaw {
Value(String),
Object { url: String },
}

impl<'de> Deserialize<'de> for Entry {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(untagged)]
enum EntryRaw {
Value(String),
Object { url: String },
}

EntryRaw::deserialize(deserializer).and_then(|raw| {
let url = match raw {
EntryRaw::Value(url) => url,
Expand Down
1 change: 0 additions & 1 deletion plugins/shell/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ serde = { workspace = true }

[dependencies]
serde = { workspace = true }
schemars = { workspace = true }
serde_json = { workspace = true }
tauri = { workspace = true }
tokio = { version = "1", features = ["time"] }
Expand Down
160 changes: 159 additions & 1 deletion plugins/shell/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,173 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

use std::path::PathBuf;

use schemars::JsonSchema;

#[path = "src/scope_entry.rs"]
mod scope_entry;

/// A command argument allowed to be executed by the webview API.
#[derive(Debug, PartialEq, Eq, Clone, Hash, schemars::JsonSchema)]
#[serde(untagged, deny_unknown_fields)]
#[non_exhaustive]
pub enum ShellScopeEntryAllowedArg {
/// A non-configurable argument that is passed to the command in the order it was specified.
Fixed(String),

/// A variable that is set while calling the command from the webview API.
///
Var {
/// [regex] validator to require passed values to conform to an expected input.
///
/// This will require the argument value passed to this variable to match the `validator` regex
/// before it will be executed.
///
/// The regex string is by default surrounded by `^...$` to match the full string.
/// For example the `https?://\w+` regex would be registered as `^https?://\w+$`.
///
/// [regex]: <https://docs.rs/regex/latest/regex/#syntax>
validator: String,

/// Marks the validator as a raw regex, meaning the plugin should not make any modification at runtime.
///
/// This means the regex will not match on the entire string by default, which might
/// be exploited if your regex allow unexpected input to be considered valid.
/// When using this option, make sure your regex is correct.
#[serde(default)]
raw: bool,
},
}

/// A set of command arguments allowed to be executed by the webview API.
///
/// A value of `true` will allow any arguments to be passed to the command. `false` will disable all
/// arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to
/// be passed to the attached command configuration.
#[derive(Debug, PartialEq, Eq, Clone, Hash, JsonSchema)]
#[serde(untagged, deny_unknown_fields)]
#[non_exhaustive]
pub enum ShellScopeEntryAllowedArgs {
/// Use a simple boolean to allow all or disable all arguments to this command configuration.
Flag(bool),

/// A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.
List(Vec<ShellScopeEntryAllowedArg>),
}

impl Default for ShellScopeEntryAllowedArgs {
fn default() -> Self {
Self::Flag(false)
}
}

/// Shell scope entry.
#[derive(JsonSchema)]
#[serde(untagged, deny_unknown_fields)]
#[allow(unused)]
pub(crate) enum ShellScopeEntry {
Command {
/// The name for this allowed shell command configuration.
///
/// This name will be used inside of the webview API to call this command along with
/// any specified arguments.
name: String,
/// The command name.
/// It 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`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`,
/// `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.
// use default just so the schema doesn't flag it as required
#[serde(rename = "cmd")]
command: PathBuf,
/// The allowed arguments for the command execution.
#[serde(default)]
args: ShellScopeEntryAllowedArgs,
},
Sidecar {
/// The name for this allowed shell command configuration.
///
/// This name will be used inside of the webview API to call this command along with
/// any specified arguments.
name: String,
/// The allowed arguments for the command execution.
#[serde(default)]
args: ShellScopeEntryAllowedArgs,
/// If this command is a sidecar command.
sidecar: bool,
},
}

// Ensure `ShellScopeEntry` and `scope_entry::EntryRaw`
// and `ShellScopeEntryAllowedArg` and `ShellAllowedArg`
// and `ShellScopeEntryAllowedArgs` and `ShellAllowedArgs`
// are kept in sync
#[allow(clippy::unnecessary_operation)]
fn _f() {
match (ShellScopeEntry::Sidecar {
name: String::new(),
args: ShellScopeEntryAllowedArgs::Flag(false),
sidecar: true,
}) {
ShellScopeEntry::Command {
name,
command,
args,
} => scope_entry::EntryRaw {
name,
command: Some(command),
args: match args {
ShellScopeEntryAllowedArgs::Flag(flag) => scope_entry::ShellAllowedArgs::Flag(flag),
ShellScopeEntryAllowedArgs::List(vec) => scope_entry::ShellAllowedArgs::List(
vec.into_iter()
.map(|s| match s {
ShellScopeEntryAllowedArg::Fixed(fixed) => {
scope_entry::ShellAllowedArg::Fixed(fixed)
}
ShellScopeEntryAllowedArg::Var { validator, raw } => {
scope_entry::ShellAllowedArg::Var { validator, raw }
}
})
.collect(),
),
},
sidecar: false,
},
ShellScopeEntry::Sidecar {
name,
args,
sidecar,
} => scope_entry::EntryRaw {
name,
command: None,
args: match args {
ShellScopeEntryAllowedArgs::Flag(flag) => scope_entry::ShellAllowedArgs::Flag(flag),
ShellScopeEntryAllowedArgs::List(vec) => scope_entry::ShellAllowedArgs::List(
vec.into_iter()
.map(|s| match s {
ShellScopeEntryAllowedArg::Fixed(fixed) => {
scope_entry::ShellAllowedArg::Fixed(fixed)
}
ShellScopeEntryAllowedArg::Var { validator, raw } => {
scope_entry::ShellAllowedArg::Var { validator, raw }
}
})
.collect(),
),
},
sidecar,
},
};
}

const COMMANDS: &[&str] = &["execute", "spawn", "stdin_write", "kill", "open"];

fn main() {
tauri_plugin::Builder::new(COMMANDS)
.global_api_script_path("./api-iife.js")
.global_scope_schema(schemars::schema_for!(scope_entry::Entry))
.global_scope_schema(schemars::schema_for!(ShellScopeEntry))
.android_path("android")
.ios_path("ios")
.build();
Expand Down
Loading

0 comments on commit 44273b9

Please sign in to comment.