use glob patterns by default

This commit is contained in:
Lucas Nogueira 2025-10-14 08:08:24 -03:00
parent 710256b20c
commit e32108a453
No known key found for this signature in database
GPG Key ID: 7C32FCA95C8C95D7
9 changed files with 150 additions and 19 deletions

View File

@ -1168,13 +1168,13 @@
]
},
"urls": {
"description": "Only allow URLs matching the given pattern list when set.",
"description": "Only allow URLs matching the given pattern list when set.\n\n By default it is a glob pattern, but can use the full [URLPattern] spec if the `url-pattern` feature is enabled.\n\n [URLPattern]: https://developer.mozilla.org/en-US/docs/Web/API/URLPattern",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/UrlPattern"
"$ref": "#/definitions/UrlScope"
}
}
},
@ -1194,13 +1194,13 @@
]
},
"urls": {
"description": "Only allow URLs matching the given pattern list when set.",
"description": "Only allow URLs matching the given pattern list when set.\n\n By default it is a glob pattern, but can use the full [URLPattern] spec if the `url-pattern` feature is enabled.\n\n [URLPattern]: https://developer.mozilla.org/en-US/docs/Web/API/URLPattern",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/UrlPattern"
"$ref": "#/definitions/UrlScope"
}
}
},
@ -1224,7 +1224,20 @@
}
]
},
"UrlPattern": {
"UrlScope": {
"description": "A scope to match URLs.",
"anyOf": [
{
"description": "A [`GlobPattern`] to match URLs.",
"allOf": [
{
"$ref": "#/definitions/GlobPattern"
}
]
}
]
},
"GlobPattern": {
"type": "string"
},
"SecurityConfig": {

View File

@ -1168,13 +1168,13 @@
]
},
"urls": {
"description": "Only allow URLs matching the given pattern list when set.",
"description": "Only allow URLs matching the given pattern list when set.\n\n By default it is a glob pattern, but can use the full [URLPattern] spec if the `url-pattern` feature is enabled.\n\n [URLPattern]: https://developer.mozilla.org/en-US/docs/Web/API/URLPattern",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/UrlPattern"
"$ref": "#/definitions/UrlScope"
}
}
},
@ -1194,13 +1194,13 @@
]
},
"urls": {
"description": "Only allow URLs matching the given pattern list when set.",
"description": "Only allow URLs matching the given pattern list when set.\n\n By default it is a glob pattern, but can use the full [URLPattern] spec if the `url-pattern` feature is enabled.\n\n [URLPattern]: https://developer.mozilla.org/en-US/docs/Web/API/URLPattern",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/UrlPattern"
"$ref": "#/definitions/UrlScope"
}
}
},
@ -1224,7 +1224,20 @@
}
]
},
"UrlPattern": {
"UrlScope": {
"description": "A scope to match URLs.",
"anyOf": [
{
"description": "A [`GlobPattern`] to match URLs.",
"allOf": [
{
"$ref": "#/definitions/GlobPattern"
}
]
}
]
},
"GlobPattern": {
"type": "string"
},
"SecurityConfig": {

View File

@ -75,3 +75,4 @@ config-json5 = ["json5"]
config-toml = []
resources = ["walkdir"]
html-manipulation = ["dep:html5ever", "dep:kuchiki"]
url-pattern = []

View File

@ -62,7 +62,7 @@ fn add_description(schema: Schema, description: impl Into<String>) -> Schema {
pub mod parse;
use crate::{
acl::capability::Capability, url::UrlPattern, TitleBarStyle, WindowEffect, WindowEffectState,
acl::capability::Capability, url::UrlScope, TitleBarStyle, WindowEffect, WindowEffectState,
};
pub use self::parse::parse;
@ -1658,12 +1658,20 @@ pub enum OnNewWindow {
/// Allow the window to be created using the default webview implementation.
AllowDefault {
/// Only allow URLs matching the given pattern list when set.
urls: Option<Vec<UrlPattern>>,
///
/// By default it is a glob pattern, but can use the full [URLPattern] spec if the `url-pattern` feature is enabled.
///
/// [URLPattern]: https://developer.mozilla.org/en-US/docs/Web/API/URLPattern
urls: Option<Vec<UrlScope>>,
},
/// Allow the window to be created using a Tauri window.
AllowTauriWindow {
/// Only allow URLs matching the given pattern list when set.
urls: Option<Vec<UrlPattern>>,
///
/// By default it is a glob pattern, but can use the full [URLPattern] spec if the `url-pattern` feature is enabled.
///
/// [URLPattern]: https://developer.mozilla.org/en-US/docs/Web/API/URLPattern
urls: Option<Vec<UrlScope>>,
},
/// Deny the window from being created.
#[default]
@ -3557,11 +3565,11 @@ mod build {
tokens.append_all(match self {
Self::AllowDefault { urls } => {
let urls = opt_vec_lit(urls.as_ref(), url_pattern_lit);
let urls = opt_vec_lit(urls.as_ref(), url_scope_lit);
quote! { #prefix::AllowDefault { urls: #urls } }
}
Self::AllowTauriWindow { urls } => {
let urls = opt_vec_lit(urls.as_ref(), url_pattern_lit);
let urls = opt_vec_lit(urls.as_ref(), url_scope_lit);
quote! { #prefix::AllowTauriWindow { urls: #urls } }
}
Self::Deny => quote! { #prefix::Deny },

View File

@ -11,7 +11,7 @@ use quote::{quote, ToTokens};
use serde_json::Value as JsonValue;
use url::Url;
use crate::url::UrlPattern;
use crate::url::{GlobPattern, UrlPattern, UrlScope};
/// Write a `TokenStream` of the `$struct`'s fields to the `$tokens`.
///
@ -100,6 +100,29 @@ pub fn url_pattern_lit(url: &UrlPattern) -> TokenStream {
quote! { #url.parse().unwrap() }
}
/// Creates a [`GlobPattern`] constructor `TokenStream`.
pub fn glob_pattern_lit(pattern: &GlobPattern) -> TokenStream {
let pattern = pattern.0.as_str();
quote! { #pattern.parse().unwrap() }
}
/// Creates a [`UrlScope`] constructor `TokenStream`.
pub fn url_scope_lit(url: &UrlScope) -> TokenStream {
let prefix = quote! { ::tauri::utils::url::UrlScope };
match url {
#[cfg(feature = "url-pattern")]
UrlScope::UrlPattern(url) => {
let url = url.as_str();
quote! { #prefix::UrlPattern(#url.parse().unwrap()) }
}
#[cfg(not(feature = "url-pattern"))]
UrlScope::Glob(glob) => {
let pattern = glob.0.as_str();
quote! { #prefix::Glob(#pattern.parse().unwrap()) }
}
}
}
/// Create a map constructor, mapping keys and values with other `TokenStream`s.
///
/// This function is pretty generic because the types of keys AND values get transformed.

View File

@ -9,6 +9,77 @@ use std::{str::FromStr, sync::Arc};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use url::Url;
/// A scope to match URLs.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(untagged)]
pub enum UrlScope {
/// A [`UrlPattern`] to match URLs.
///
/// This is only available if the `url-pattern` feature is enabled.
#[cfg(feature = "url-pattern")]
UrlPattern(UrlPattern),
/// A [`GlobPattern`] to match URLs.
#[cfg(not(feature = "url-pattern"))]
Glob(GlobPattern),
}
impl UrlScope {
/// Test if a given URL is matched by the scope.
pub fn test(&self, url: &Url) -> bool {
match self {
#[cfg(feature = "url-pattern")]
Self::UrlPattern(pattern) => pattern.test(url),
#[cfg(not(feature = "url-pattern"))]
Self::Glob(pattern) => pattern.0.matches(url.as_str()),
}
}
}
/// A [`glob::Pattern`] to match URLs.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GlobPattern(pub(crate) glob::Pattern);
#[cfg(feature = "schema")]
impl schemars::JsonSchema for GlobPattern {
fn schema_name() -> String {
"GlobPattern".to_string()
}
fn json_schema(_gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
String::json_schema(_gen)
}
}
impl FromStr for GlobPattern {
type Err = glob::PatternError;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
glob::Pattern::new(s).map(Self)
}
}
impl Serialize for GlobPattern {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.0.as_str())
}
}
impl<'de> Deserialize<'de> for GlobPattern {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
glob::Pattern::new(&s)
.map(Self)
.map_err(serde::de::Error::custom)
}
}
/// UrlPattern to match URLs.
#[derive(Debug, Clone)]
pub struct UrlPattern(Arc<urlpattern::UrlPattern>, String);
@ -73,7 +144,7 @@ impl UrlPattern {
&self.1
}
/// Test if a given URL matches the pattern.
/// Test if a given URL is matched by the pattern.
pub fn test(&self, url: &Url) -> bool {
self
.0

View File

@ -221,6 +221,7 @@ image-png = ["image/png"]
macos-proxy = ["tauri-runtime-wry?/macos-proxy"]
dynamic-acl = []
specta = ["dep:specta"]
url-pattern = ["tauri-utils/url-pattern"]
[[example]]
name = "commands"

View File

@ -37,6 +37,7 @@
//! - **macos-proxy**: Adds support for [`WebviewBuilder::proxy_url`] on macOS. Requires macOS 14+.
//! - **specta**: Add support for [`specta::specta`](https://docs.rs/specta/%5E2.0.0-rc.9/specta/attr.specta.html) with Tauri arguments such as [`State`](crate::State), [`Window`](crate::Window) and [`AppHandle`](crate::AppHandle)
//! - **dynamic-acl** *(enabled by default)*: Enables you to add ACLs at runtime, notably it enables the [`Manager::add_capability`] function.
//! - **url-pattern**: Enables using the [URLPattern] spec for URL scopes on JavaScript APIs.
//!
//! ## Cargo allowlist features
//!

View File

@ -906,14 +906,14 @@ interface WebviewOptions {
*
* 'allowDefault' lets the webview open the URL using the native implementation.
* 'allowTauriWindow' creates a Tauri window to load the URL.
* Additionally you can provide a list of filters to only allow URLs matching certain {@link https://developer.mozilla.org/en-US/docs/Web/API/URLPattern|URL patterns}.
* Additionally you can provide a list of filters to only allow URLs matching certain glob patterns.
* It can leverage the {@link https://developer.mozilla.org/en-US/docs/Web/API/URLPattern|URL pattern spec} if the `url-pattern` Cargo feature is enabled.
*
* A new window is requested to be opened by the {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/open|window.open API}.
*
* ## Platform-specific
*
* - **Android / iOS**: Not supported.
*
* */
onNewWindow?: OnNewWindow
}