Skip to content

Commit

Permalink
fix(acl): inconsistencies on urlpattern usage for remote domain URL (#…
Browse files Browse the repository at this point in the history
…9133)

* fix(acl): inconsistencies on urlpattern usage for remote domain URL

* remove println!

* typo

* fix tests
  • Loading branch information
lucasfernog authored Mar 11, 2024
1 parent 490a6b4 commit e673854
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 35 deletions.
5 changes: 5 additions & 0 deletions .changes/fix-remote-domain-url.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tauri": patch:bug
---

Fixes capability remote domain not allowing subpaths, query parameters and hash when those values are empty.
2 changes: 1 addition & 1 deletion core/tauri-config-schema/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1141,7 +1141,7 @@
],
"properties": {
"urls": {
"description": "Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).",
"description": "Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).\n\n# Examples\n\n- \"https://*.mydomain.dev\": allows subdomains of mydomain.dev - \"https://mydomain.dev/api/*\": allows any subpath of mydomain.dev/api",
"type": "array",
"items": {
"type": "string"
Expand Down
5 changes: 5 additions & 0 deletions core/tauri-utils/src/acl/capability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ fn default_capability_local() -> bool {
#[serde(rename_all = "camelCase")]
pub struct CapabilityRemote {
/// Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).
///
/// # Examples
///
/// - "https://*.mydomain.dev": allows subdomains of mydomain.dev
/// - "https://mydomain.dev/api/*": allows any subpath of mydomain.dev/api
pub urls: Vec<String>,
}

Expand Down
56 changes: 55 additions & 1 deletion core/tauri-utils/src/acl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,21 @@ impl FromStr for RemoteUrlPattern {
type Err = urlpattern::quirks::Error;

fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
let init = urlpattern::UrlPatternInit::parse_constructor_string::<regex::Regex>(s, None)?;
let mut init = urlpattern::UrlPatternInit::parse_constructor_string::<regex::Regex>(s, None)?;
if init.search.as_ref().map(|p| p.is_empty()).unwrap_or(true) {
init.search.replace("*".to_string());
}
if init.hash.as_ref().map(|p| p.is_empty()).unwrap_or(true) {
init.hash.replace("*".to_string());
}
if init
.pathname
.as_ref()
.map(|p| p.is_empty() || p == "/")
.unwrap_or(true)
{
init.pathname.replace("*".to_string());
}
let pattern = urlpattern::UrlPattern::parse(init)?;
Ok(Self(Arc::new(pattern), s.to_string()))
}
Expand Down Expand Up @@ -251,6 +265,46 @@ pub enum ExecutionContext {
},
}

#[cfg(test)]
mod tests {
use crate::acl::RemoteUrlPattern;

#[test]
fn url_pattern_domain_wildcard() {
let pattern: RemoteUrlPattern = "http://*".parse().unwrap();

assert!(pattern.test(&"http://tauri.app/path".parse().unwrap()));
assert!(pattern.test(&"http://tauri.app/path?q=1".parse().unwrap()));

assert!(pattern.test(&"http://localhost/path".parse().unwrap()));
assert!(pattern.test(&"http://localhost/path?q=1".parse().unwrap()));

let pattern: RemoteUrlPattern = "http://*.tauri.app".parse().unwrap();

assert!(!pattern.test(&"http://tauri.app/path".parse().unwrap()));
assert!(!pattern.test(&"http://tauri.app/path?q=1".parse().unwrap()));
assert!(pattern.test(&"http://api.tauri.app/path".parse().unwrap()));
assert!(pattern.test(&"http://api.tauri.app/path?q=1".parse().unwrap()));
assert!(!pattern.test(&"http://localhost/path".parse().unwrap()));
assert!(!pattern.test(&"http://localhost/path?q=1".parse().unwrap()));
}

#[test]
fn url_pattern_path_wildcard() {
let pattern: RemoteUrlPattern = "http://localhost/*".parse().unwrap();
assert!(pattern.test(&"http://localhost/path".parse().unwrap()));
assert!(pattern.test(&"http://localhost/path?q=1".parse().unwrap()));
}

#[test]
fn url_pattern_scheme_wildcard() {
let pattern: RemoteUrlPattern = "*://localhost".parse().unwrap();
assert!(pattern.test(&"http://localhost/path".parse().unwrap()));
assert!(pattern.test(&"https://localhost/path?q=1".parse().unwrap()));
assert!(pattern.test(&"custom://localhost/path".parse().unwrap()));
}
}

#[cfg(feature = "build")]
mod build_ {
use std::convert::identity;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,50 +90,59 @@ Resolved {
},
},
pathname: Component {
pattern_string: "/",
pattern_string: "*",
regexp: Ok(
Regex(
"^/$",
"^(.*)$",
),
),
group_name_list: [],
group_name_list: [
"0",
],
matcher: Matcher {
prefix: "",
suffix: "",
inner: Literal {
literal: "/",
inner: SingleCapture {
filter: None,
allow_empty: true,
},
},
},
search: Component {
pattern_string: "",
pattern_string: "*",
regexp: Ok(
Regex(
"^$",
"^(.*)$",
),
),
group_name_list: [],
group_name_list: [
"0",
],
matcher: Matcher {
prefix: "",
suffix: "",
inner: Literal {
literal: "",
inner: SingleCapture {
filter: None,
allow_empty: true,
},
},
},
hash: Component {
pattern_string: "",
pattern_string: "*",
regexp: Ok(
Regex(
"^$",
"^(.*)$",
),
),
group_name_list: [],
group_name_list: [
"0",
],
matcher: Matcher {
prefix: "",
suffix: "",
inner: Literal {
literal: "",
inner: SingleCapture {
filter: None,
allow_empty: true,
},
},
},
Expand Down Expand Up @@ -251,50 +260,59 @@ Resolved {
},
},
pathname: Component {
pattern_string: "/",
pattern_string: "*",
regexp: Ok(
Regex(
"^/$",
"^(.*)$",
),
),
group_name_list: [],
group_name_list: [
"0",
],
matcher: Matcher {
prefix: "",
suffix: "",
inner: Literal {
literal: "/",
inner: SingleCapture {
filter: None,
allow_empty: true,
},
},
},
search: Component {
pattern_string: "",
pattern_string: "*",
regexp: Ok(
Regex(
"^$",
"^(.*)$",
),
),
group_name_list: [],
group_name_list: [
"0",
],
matcher: Matcher {
prefix: "",
suffix: "",
inner: Literal {
literal: "",
inner: SingleCapture {
filter: None,
allow_empty: true,
},
},
},
hash: Component {
pattern_string: "",
pattern_string: "*",
regexp: Ok(
Regex(
"^$",
"^(.*)$",
),
),
group_name_list: [],
group_name_list: [
"0",
],
matcher: Matcher {
prefix: "",
suffix: "",
inner: Literal {
literal: "",
inner: SingleCapture {
filter: None,
allow_empty: true,
},
},
},
Expand Down
7 changes: 5 additions & 2 deletions examples/api/src-tauri/capabilities/run-app.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "run-app",
"description": "permissions to run the app",
"windows": ["main", "main-*"],
"windows": [
"main",
"main-*"
],
"permissions": [
{
"identifier": "allow-log-operation",
Expand Down Expand Up @@ -96,4 +99,4 @@
"tray:allow-set-icon-as-template",
"tray:allow-set-show-menu-on-left-click"
]
}
}
2 changes: 1 addition & 1 deletion tooling/cli/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1141,7 +1141,7 @@
],
"properties": {
"urls": {
"description": "Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).",
"description": "Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).\n\n# Examples\n\n- \"https://*.mydomain.dev\": allows subdomains of mydomain.dev - \"https://mydomain.dev/api/*\": allows any subpath of mydomain.dev/api",
"type": "array",
"items": {
"type": "string"
Expand Down

0 comments on commit e673854

Please sign in to comment.