mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-07-10 16:27:05 +02:00
feat(http): enhance scope URL matching via urlpattern (#1030)
* feat(http): enhance scope URL matching via urlpattern * update schema
This commit is contained in:
committed by
GitHub
parent
d9870f1948
commit
ac520a2841
+59
-23
@@ -4,11 +4,17 @@
|
||||
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use url::Url;
|
||||
use urlpattern::{UrlPattern, UrlPatternInit, UrlPatternMatchInput};
|
||||
|
||||
#[allow(rustdoc::bare_urls)]
|
||||
#[derive(Debug)]
|
||||
pub struct Entry {
|
||||
pub url: glob::Pattern,
|
||||
pub url: UrlPattern,
|
||||
}
|
||||
|
||||
fn parse_url_pattern(s: &str) -> Result<UrlPattern, urlpattern::quirks::Error> {
|
||||
let init = UrlPatternInit::parse_constructor_string::<regex::Regex>(s, None)?;
|
||||
UrlPattern::parse(init)
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Entry {
|
||||
@@ -23,9 +29,9 @@ impl<'de> Deserialize<'de> for Entry {
|
||||
|
||||
EntryRaw::deserialize(deserializer).and_then(|raw| {
|
||||
Ok(Entry {
|
||||
url: glob::Pattern::new(&raw.url).map_err(|e| {
|
||||
url: parse_url_pattern(&raw.url).map_err(|e| {
|
||||
serde::de::Error::custom(format!(
|
||||
"URL `{}` is not a valid glob pattern: {e}",
|
||||
"`{}` is not a valid URL pattern: {e}",
|
||||
raw.url
|
||||
))
|
||||
})?,
|
||||
@@ -50,19 +56,19 @@ impl<'a> Scope<'a> {
|
||||
/// Determines if the given URL is allowed on this scope.
|
||||
pub fn is_allowed(&self, url: &Url) -> bool {
|
||||
let denied = self.denied.iter().any(|entry| {
|
||||
entry.url.matches(url.as_str())
|
||||
|| entry
|
||||
.url
|
||||
.matches(url.as_str().strip_suffix('/').unwrap_or_default())
|
||||
entry
|
||||
.url
|
||||
.test(UrlPatternMatchInput::Url(url.clone()))
|
||||
.unwrap_or_default()
|
||||
});
|
||||
if denied {
|
||||
false
|
||||
} else {
|
||||
self.allowed.iter().any(|entry| {
|
||||
entry.url.matches(url.as_str())
|
||||
|| entry
|
||||
.url
|
||||
.matches(url.as_str().strip_suffix('/').unwrap_or_default())
|
||||
entry
|
||||
.url
|
||||
.test(UrlPatternMatchInput::Url(url.clone()))
|
||||
.unwrap_or_default()
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -75,16 +81,24 @@ mod tests {
|
||||
use super::Entry;
|
||||
|
||||
impl FromStr for Entry {
|
||||
type Err = glob::PatternError;
|
||||
type Err = urlpattern::quirks::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let pattern = s.parse()?;
|
||||
let pattern = super::parse_url_pattern(s)?;
|
||||
Ok(Self { url: pattern })
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_allowed() {
|
||||
fn denied_takes_precedence() {
|
||||
let allow = "http://localhost:8080/file.png".parse().unwrap();
|
||||
let deny = "http://localhost:8080/*".parse().unwrap();
|
||||
let scope = super::Scope::new(vec![&allow], vec![&deny]);
|
||||
assert!(!scope.is_allowed(&"http://localhost:8080/file.png".parse().unwrap()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fixed_url() {
|
||||
// plain URL
|
||||
let entry = "http://localhost:8080".parse().unwrap();
|
||||
let scope = super::Scope::new(vec![&entry], Vec::new());
|
||||
@@ -96,13 +110,10 @@ mod tests {
|
||||
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()));
|
||||
}
|
||||
|
||||
// deny takes precedence
|
||||
let allow = "http://localhost:8080/file.png".parse().unwrap();
|
||||
let deny = "http://localhost:8080/*".parse().unwrap();
|
||||
let scope = super::Scope::new(vec![&allow], vec![&deny]);
|
||||
assert!(!scope.is_allowed(&"http://localhost:8080/file.png".parse().unwrap()));
|
||||
|
||||
#[test]
|
||||
fn fixed_path() {
|
||||
// URL with fixed path
|
||||
let entry = "http://localhost:8080/file.png".parse().unwrap();
|
||||
let scope = super::Scope::new(vec![&entry], Vec::new());
|
||||
@@ -112,8 +123,10 @@ mod tests {
|
||||
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
|
||||
#[test]
|
||||
fn pattern_wildcard() {
|
||||
let entry = "http://localhost:8080/*.png".parse().unwrap();
|
||||
let scope = super::Scope::new(vec![&entry], Vec::new());
|
||||
|
||||
@@ -121,18 +134,41 @@ mod tests {
|
||||
assert!(scope.is_allowed(&"http://localhost:8080/assets/file.png".parse().unwrap()));
|
||||
|
||||
assert!(!scope.is_allowed(&"http://localhost:8080/file.jpeg".parse().unwrap()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn domain_wildcard() {
|
||||
let entry = "http://*".parse().unwrap();
|
||||
let scope = super::Scope::new(vec![&entry], Vec::new());
|
||||
|
||||
assert!(scope.is_allowed(&"http://something.else".parse().unwrap()));
|
||||
assert!(scope.is_allowed(&"http://something.else/path/to/file".parse().unwrap()));
|
||||
assert!(!scope.is_allowed(&"http://something.else/path/to/file".parse().unwrap()));
|
||||
assert!(!scope.is_allowed(&"https://something.else".parse().unwrap()));
|
||||
|
||||
let entry = "http://**".parse().unwrap();
|
||||
let entry = "http://*/*".parse().unwrap();
|
||||
let scope = super::Scope::new(vec![&entry], Vec::new());
|
||||
|
||||
assert!(scope.is_allowed(&"http://something.else".parse().unwrap()));
|
||||
assert!(scope.is_allowed(&"http://something.else/path/to/file".parse().unwrap()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scheme_wildcard() {
|
||||
let entry = "*://*".parse().unwrap();
|
||||
let scope = super::Scope::new(vec![&entry], Vec::new());
|
||||
|
||||
assert!(scope.is_allowed(&"http://something.else".parse().unwrap()));
|
||||
assert!(!scope.is_allowed(&"http://something.else/path/to/file".parse().unwrap()));
|
||||
assert!(scope.is_allowed(&"file://path".parse().unwrap()));
|
||||
assert!(!scope.is_allowed(&"file://path/to/file".parse().unwrap()));
|
||||
assert!(scope.is_allowed(&"https://something.else".parse().unwrap()));
|
||||
|
||||
let entry = "*://*/*".parse().unwrap();
|
||||
let scope = super::Scope::new(vec![&entry], Vec::new());
|
||||
|
||||
assert!(scope.is_allowed(&"http://something.else".parse().unwrap()));
|
||||
assert!(scope.is_allowed(&"http://something.else/path/to/file".parse().unwrap()));
|
||||
assert!(scope.is_allowed(&"file://path/to/file".parse().unwrap()));
|
||||
assert!(scope.is_allowed(&"https://something.else".parse().unwrap()));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user