From 34df132fb14212ba7330adc9ccd64267751950c8 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Fri, 2 Aug 2024 10:03:35 -0300 Subject: [PATCH] feat(shell): enhance regex validators to match on entire string (#1603) --- .changes/shell-open-regex-match-string.md | 5 +++++ .changes/shell-regex-match-string.md | 6 ++++++ examples/api/src-tauri/capabilities/base.json | 6 +++--- .../api/src-tauri/gen/schemas/desktop-schema.json | 7 ++++++- plugins/shell/src/config.rs | 3 +++ plugins/shell/src/lib.rs | 3 ++- plugins/shell/src/scope.rs | 11 ++++++++--- plugins/shell/src/scope_entry.rs | 13 ++++++++++++- 8 files changed, 45 insertions(+), 9 deletions(-) create mode 100644 .changes/shell-open-regex-match-string.md create mode 100644 .changes/shell-regex-match-string.md diff --git a/.changes/shell-open-regex-match-string.md b/.changes/shell-open-regex-match-string.md new file mode 100644 index 000000000..05ee1444e --- /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 000000000..b7b798d13 --- /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 e0d05d6f9..037909a28 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 086c11589..e261cea05 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 95390988a..cad267a26 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 e5f716889..1ec90e2e1 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 5f59fd7af..8178ab101 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 ff94a3a78..e40936c30 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, }, }