diff --git a/.changes/shell-open-regex-match-string.md b/.changes/shell-open-regex-match-string.md new file mode 100644 index 00000000..05ee1444 --- /dev/null +++ b/.changes/shell-open-regex-match-string.md @@ -0,0 +1,5 @@ +--- +"shell": patch +--- + +Change the `open` scope validator regex to match on the entire string. diff --git a/.changes/shell-regex-match-string.md b/.changes/shell-regex-match-string.md new file mode 100644 index 00000000..b7b798d1 --- /dev/null +++ b/.changes/shell-regex-match-string.md @@ -0,0 +1,6 @@ +--- +"shell": patch +--- + +Change the `execute` scope argument validator regex to match on the entire string by default. +If this behavior is not desired check the `raw` boolean configuration option that is available along the `validator` string. diff --git a/examples/api/src-tauri/capabilities/base.json b/examples/api/src-tauri/capabilities/base.json index e0d05d6f..037909a2 100644 --- a/examples/api/src-tauri/capabilities/base.json +++ b/examples/api/src-tauri/capabilities/base.json @@ -36,7 +36,7 @@ "dialog:allow-confirm", "dialog:allow-message", { - "identifier": "shell:allow-execute", + "identifier": "shell:allow-spawn", "allow": [ { "name": "sh", @@ -44,7 +44,7 @@ "args": [ "-c", { - "validator": "\\S+" + "validator": ".+" } ] }, @@ -54,7 +54,7 @@ "args": [ "/C", { - "validator": "\\S+" + "validator": ".+" } ] } diff --git a/examples/api/src-tauri/gen/schemas/desktop-schema.json b/examples/api/src-tauri/gen/schemas/desktop-schema.json index 086c1158..e261cea0 100644 --- a/examples/api/src-tauri/gen/schemas/desktop-schema.json +++ b/examples/api/src-tauri/gen/schemas/desktop-schema.json @@ -7410,8 +7410,13 @@ "validator" ], "properties": { + "raw": { + "description": "Marks the validator as a raw regex, meaning the plugin should not make any modification at runtime.\n\nThis 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.", + "default": false, + "type": "boolean" + }, "validator": { - "description": "[regex] validator to require passed values to conform to an expected input.\n\nThis will require the argument value passed to this variable to match the `validator` regex before it will be executed.\n\n[regex]: https://docs.rs/regex/latest/regex/#syntax", + "description": "[regex] validator to require passed values to conform to an expected input.\n\nThis will require the argument value passed to this variable to match the `validator` regex before it will be executed.\n\nThe regex string is by default surrounded by `^...$` to match the full string. For example the `https?://\\w+` regex would be registered as `^https?://\\w+$`.\n\n[regex]: ", "type": "string" } }, diff --git a/plugins/shell/src/config.rs b/plugins/shell/src/config.rs index 95390988..cad267a2 100644 --- a/plugins/shell/src/config.rs +++ b/plugins/shell/src/config.rs @@ -25,6 +25,9 @@ pub enum ShellAllowlistOpen { /// Enable the shell open API, with a custom regex that the opened path must match against. /// + /// The regex string is automatically surrounded by `^...$` to match the full string. + /// For example the `https?://\w+` regex would be registered as `^https?://\w+$`. + /// /// If using a custom regex to support a non-http(s) schema, care should be used to prevent values /// that allow flag-like strings to pass validation. e.g. `--enable-debugging`, `-i`, `/R`. Validate(String), diff --git a/plugins/shell/src/lib.rs b/plugins/shell/src/lib.rs index e5f71688..1ec90e2e 100644 --- a/plugins/shell/src/lib.rs +++ b/plugins/shell/src/lib.rs @@ -148,8 +148,9 @@ fn open_scope(open: &config::ShellAllowlistOpen) -> scope::OpenScope { Some(Regex::new(r"^((mailto:\w+)|(tel:\w+)|(https?://\w+)).+").unwrap()) } config::ShellAllowlistOpen::Validate(validator) => { + let regex = format!("^{validator}$"); let validator = - Regex::new(validator).unwrap_or_else(|e| panic!("invalid regex {validator}: {e}")); + Regex::new(®ex).unwrap_or_else(|e| panic!("invalid regex {regex}: {e}")); Some(validator) } }; diff --git a/plugins/shell/src/scope.rs b/plugins/shell/src/scope.rs index 5f59fd7a..8178ab10 100644 --- a/plugins/shell/src/scope.rs +++ b/plugins/shell/src/scope.rs @@ -88,9 +88,14 @@ impl ScopeObject for ScopeAllowedCommand { crate::scope_entry::ShellAllowedArg::Fixed(fixed) => { crate::scope::ScopeAllowedArg::Fixed(fixed) } - crate::scope_entry::ShellAllowedArg::Var { validator } => { - let validator = Regex::new(&validator) - .unwrap_or_else(|e| panic!("invalid regex {validator}: {e}")); + crate::scope_entry::ShellAllowedArg::Var { validator, raw } => { + let regex = if raw { + validator + } else { + format!("^{validator}$") + }; + let validator = Regex::new(®ex) + .unwrap_or_else(|e| panic!("invalid regex {regex}: {e}")); crate::scope::ScopeAllowedArg::Var { validator } } }); diff --git a/plugins/shell/src/scope_entry.rs b/plugins/shell/src/scope_entry.rs index ff94a3a7..e40936c3 100644 --- a/plugins/shell/src/scope_entry.rs +++ b/plugins/shell/src/scope_entry.rs @@ -103,7 +103,18 @@ pub enum ShellAllowedArg { /// This will require the argument value passed to this variable to match the `validator` regex /// before it will be executed. /// - /// [regex]: https://docs.rs/regex/latest/regex/#syntax + /// 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]: 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, }, }