From b82e2b5cf77a2c0561c15885a5d6abf4eab97bdf Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Fri, 28 Jan 2022 21:07:08 -0300 Subject: [PATCH] feat(core): configure HTTP scope using glob patterns --- core/tauri-utils/src/config.rs | 1 + core/tauri/src/scope/http.rs | 51 +++++++++++++++++++++++++- examples/api/src-tauri/tauri.conf.json | 5 ++- 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/core/tauri-utils/src/config.rs b/core/tauri-utils/src/config.rs index 1c3e94a03..f25d92696 100644 --- a/core/tauri-utils/src/config.rs +++ b/core/tauri-utils/src/config.rs @@ -1058,6 +1058,7 @@ impl Allowlist for DialogAllowlistConfig { /// HTTP API scope definition. /// It is a list of URLs that can be accessed by the webview when using the HTTP APIs. +/// The URL path is matched against the request URL using a glob pattern. #[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)] #[cfg_attr(feature = "schema", derive(JsonSchema))] pub struct HttpAllowlistScope(pub Vec); diff --git a/core/tauri/src/scope/http.rs b/core/tauri/src/scope/http.rs index a83deaa80..a17849e4e 100644 --- a/core/tauri/src/scope/http.rs +++ b/core/tauri/src/scope/http.rs @@ -22,9 +22,56 @@ impl Scope { /// Determines if the given URL is allowed on this scope. pub fn is_allowed(&self, url: &Url) -> bool { self.allowed_urls.iter().any(|allowed| { - allowed.scheme() == url.scheme() + let origin_matches = allowed.scheme() == url.scheme() && allowed.host() == url.host() - && allowed.port() == url.port() + && allowed.port() == url.port(); + let allowed_path_pattern = glob::Pattern::new(allowed.path()) + .unwrap_or_else(|_| panic!("invalid glob pattern on URL `{}` path", allowed)); + origin_matches && allowed_path_pattern.matches(url.path()) }) } } + +#[cfg(test)] +mod tests { + use tauri_utils::config::HttpAllowlistScope; + + #[test] + fn is_allowed() { + // plain URL + let scope = super::Scope::for_http_api(&HttpAllowlistScope(vec!["http://localhost:8080" + .parse() + .unwrap()])); + assert!(scope.is_allowed(&"http://localhost:8080".parse().unwrap())); + assert!(scope.is_allowed(&"http://localhost:8080/".parse().unwrap())); + + assert!(!scope.is_allowed(&"http://localhost:8080/file".parse().unwrap())); + assert!(!scope.is_allowed(&"http://localhost:8080/path/to/asset.png".parse().unwrap())); + assert!(!scope.is_allowed(&"https://localhost:8080".parse().unwrap())); + assert!(!scope.is_allowed(&"http://localhost:8081".parse().unwrap())); + assert!(!scope.is_allowed(&"http://local:8080".parse().unwrap())); + + // URL with fixed path + let scope = + super::Scope::for_http_api(&HttpAllowlistScope(vec!["http://localhost:8080/file.png" + .parse() + .unwrap()])); + + assert!(scope.is_allowed(&"http://localhost:8080/file.png".parse().unwrap())); + + assert!(!scope.is_allowed(&"http://localhost:8080".parse().unwrap())); + assert!(!scope.is_allowed(&"http://localhost:8080/file".parse().unwrap())); + assert!(!scope.is_allowed(&"http://localhost:8080/file.png/other.jpg".parse().unwrap())); + + // URL with glob pattern + let scope = + super::Scope::for_http_api(&HttpAllowlistScope(vec!["http://localhost:8080/*.png" + .parse() + .unwrap()])); + + assert!(scope.is_allowed(&"http://localhost:8080/file.png".parse().unwrap())); + assert!(scope.is_allowed(&"http://localhost:8080/assets/file.png".parse().unwrap())); + + assert!(!scope.is_allowed(&"http://localhost:8080/file.jpeg".parse().unwrap())); + } +} diff --git a/examples/api/src-tauri/tauri.conf.json b/examples/api/src-tauri/tauri.conf.json index 89c1c530b..b912725a5 100644 --- a/examples/api/src-tauri/tauri.conf.json +++ b/examples/api/src-tauri/tauri.conf.json @@ -80,6 +80,7 @@ "shell": { "scope": [ { + "name": "test", "cmd": "__test", "args": [ "-d --date ^\\d{4}-\\d{2}-\\d{2}$", @@ -90,9 +91,11 @@ ] }, { + "name": "sh", "cmd": "sh" }, { + "name": "cmd", "cmd": "cmd" } ] @@ -102,7 +105,7 @@ "assetScope": ["$RESOURCE/**"] }, "http": { - "scope": ["https://jsonplaceholder.typicode.com"] + "scope": ["https://jsonplaceholder.typicode.com/todos/*"] } }, "windows": [