Procházet zdrojové kódy

feat(core): add option for `require_literal_leading_dot`, closes #6158 (#6969)

* feat(core): add option for `require_literal_leading_dot`, closes #6158

* change to `Option<bool>`

* fix to tokens impl

* Apply suggestions from code review

Co-authored-by: Simon Hyll <hyllsimon@gmail.com>

---------

Co-authored-by: Simon Hyll <hyllsimon@gmail.com>
Amr Bashir před 2 roky
rodič
revize
acc36fe117

+ 5 - 0
.changes/config-require-literal_leading_dot.md

@@ -0,0 +1,5 @@
+---
+'tauri-utils': 'patch'
+---
+
+Add option to configure `require_literal_leading_dot` on `fs` and `asset` protcol scopes.

+ 7 - 0
core/tauri-config-schema/schema.json

@@ -2069,6 +2069,13 @@
               "items": {
                 "type": "string"
               }
+            },
+            "requireLiteralLeadingDot": {
+              "description": "Whether or not paths that contain components that start with a `.` will require that `.` appears literally in the pattern; `*`, `?`, `**`, or `[...]` will not match. This is useful because such files are conventionally considered hidden on Unix systems and it might be desirable to skip them when listing files.\n\nDefaults to `false` on Unix systems and `true` on Windows",
+              "type": [
+                "boolean",
+                "null"
+              ]
             }
           }
         }

+ 38 - 66
core/tauri-utils/src/config.rs

@@ -38,6 +38,10 @@ use crate::TitleBarStyle;
 
 pub use self::parse::parse;
 
+fn default_true() -> bool {
+  true
+}
+
 /// An URL to open on a Tauri webview window.
 #[derive(PartialEq, Eq, Debug, Clone, Deserialize, Serialize)]
 #[cfg_attr(feature = "schema", derive(JsonSchema))]
@@ -519,7 +523,7 @@ pub enum WebviewInstallMode {
   /// Results in a smaller installer size, but is not recommended on Windows 7.
   DownloadBootstrapper {
     /// Instructs the installer to run the bootstrapper in silent mode. Defaults to `true`.
-    #[serde(default = "default_webview_install_silent")]
+    #[serde(default = "default_true")]
     silent: bool,
   },
   /// Embed the bootstrapper and run it.
@@ -527,7 +531,7 @@ pub enum WebviewInstallMode {
   /// Increases the installer size by around 1.8MB, but offers better support on Windows 7.
   EmbedBootstrapper {
     /// Instructs the installer to run the bootstrapper in silent mode. Defaults to `true`.
-    #[serde(default = "default_webview_install_silent")]
+    #[serde(default = "default_true")]
     silent: bool,
   },
   /// Embed the offline installer and run it.
@@ -535,7 +539,7 @@ pub enum WebviewInstallMode {
   /// Increases the installer size by around 127MB.
   OfflineInstaller {
     /// Instructs the installer to run the installer in silent mode. Defaults to `true`.
-    #[serde(default = "default_webview_install_silent")]
+    #[serde(default = "default_true")]
     silent: bool,
   },
   /// Embed a fixed webview2 version and use it at runtime.
@@ -549,15 +553,9 @@ pub enum WebviewInstallMode {
   },
 }
 
-fn default_webview_install_silent() -> bool {
-  true
-}
-
 impl Default for WebviewInstallMode {
   fn default() -> Self {
-    Self::DownloadBootstrapper {
-      silent: default_webview_install_silent(),
-    }
+    Self::DownloadBootstrapper { silent: true }
   }
 }
 
@@ -598,7 +596,7 @@ pub struct WindowsConfig {
   /// For instance, if `1.2.1` is installed, the user won't be able to install app version `1.2.0` or `1.1.5`.
   ///
   /// The default value of this flag is `true`.
-  #[serde(default = "default_allow_downgrades", alias = "allow-downgrades")]
+  #[serde(default = "default_true", alias = "allow-downgrades")]
   pub allow_downgrades: bool,
   /// Configuration for the MSI generated with WiX.
   pub wix: Option<WixConfig>,
@@ -615,17 +613,13 @@ impl Default for WindowsConfig {
       tsp: false,
       webview_install_mode: Default::default(),
       webview_fixed_runtime_path: None,
-      allow_downgrades: default_allow_downgrades(),
+      allow_downgrades: true,
       wix: None,
       nsis: None,
     }
   }
 }
 
-fn default_allow_downgrades() -> bool {
-  true
-}
-
 /// Configuration for tauri-bundler.
 ///
 /// See more: https://tauri.app/v1/api/config#bundleconfig
@@ -901,7 +895,7 @@ pub struct WindowConfig {
   /// Whether the file drop is enabled or not on the webview. By default it is enabled.
   ///
   /// Disabling it is required to use drag and drop on the frontend on Windows.
-  #[serde(default = "default_file_drop_enabled", alias = "file-drop-enabled")]
+  #[serde(default = "default_true", alias = "file-drop-enabled")]
   pub file_drop_enabled: bool,
   /// Whether or not the window starts centered or not.
   #[serde(default)]
@@ -929,7 +923,7 @@ pub struct WindowConfig {
   #[serde(alias = "max-height")]
   pub max_height: Option<f64>,
   /// Whether the window is resizable or not.
-  #[serde(default = "default_resizable")]
+  #[serde(default = "default_true")]
   pub resizable: bool,
   /// The window title.
   #[serde(default = "default_title")]
@@ -938,7 +932,7 @@ pub struct WindowConfig {
   #[serde(default)]
   pub fullscreen: bool,
   /// Whether the window will be initially focused or not.
-  #[serde(default = "default_focus")]
+  #[serde(default = "default_true")]
   pub focus: bool,
   /// Whether the window is transparent or not.
   ///
@@ -950,10 +944,10 @@ pub struct WindowConfig {
   #[serde(default)]
   pub maximized: bool,
   /// Whether the window is visible or not.
-  #[serde(default = "default_visible")]
+  #[serde(default = "default_true")]
   pub visible: bool,
   /// Whether the window should have borders and bars.
-  #[serde(default = "default_decorations")]
+  #[serde(default = "default_true")]
   pub decorations: bool,
   /// Whether the window should always be on top of other windows.
   #[serde(default, alias = "always-on-top")]
@@ -995,7 +989,7 @@ impl Default for WindowConfig {
       label: default_window_label(),
       url: WindowUrl::default(),
       user_agent: None,
-      file_drop_enabled: default_file_drop_enabled(),
+      file_drop_enabled: true,
       center: false,
       x: None,
       y: None,
@@ -1005,14 +999,14 @@ impl Default for WindowConfig {
       min_height: None,
       max_width: None,
       max_height: None,
-      resizable: default_resizable(),
+      resizable: true,
       title: default_title(),
       fullscreen: false,
       focus: false,
       transparent: false,
       maximized: false,
-      visible: default_visible(),
-      decorations: default_decorations(),
+      visible: true,
+      decorations: true,
       always_on_top: false,
       content_protected: false,
       skip_taskbar: false,
@@ -1038,30 +1032,10 @@ fn default_height() -> f64 {
   600f64
 }
 
-fn default_resizable() -> bool {
-  true
-}
-
 fn default_title() -> String {
   "Tauri App".to_string()
 }
 
-fn default_focus() -> bool {
-  true
-}
-
-fn default_visible() -> bool {
-  true
-}
-
-fn default_decorations() -> bool {
-  true
-}
-
-fn default_file_drop_enabled() -> bool {
-  true
-}
-
 /// A Content-Security-Policy directive source list.
 /// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/Sources#sources>.
 #[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
@@ -1302,12 +1276,13 @@ macro_rules! check_feature {
 /// `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`,
 /// `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.
 #[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
-#[cfg_attr(feature = "schema", derive(JsonSchema))]
 #[serde(untagged)]
+#[cfg_attr(feature = "schema", derive(JsonSchema))]
 pub enum FsAllowlistScope {
   /// A list of paths that are allowed by this scope.
   AllowedPaths(Vec<PathBuf>),
   /// A complete scope configuration.
+  #[serde(rename_all = "camelCase")]
   Scope {
     /// A list of paths that are allowed by this scope.
     #[serde(default)]
@@ -1316,6 +1291,16 @@ pub enum FsAllowlistScope {
     /// This gets precedence over the [`Self::Scope::allow`] list.
     #[serde(default)]
     deny: Vec<PathBuf>,
+    /// Whether or not paths that contain components that start with a `.`
+    /// will require that `.` appears literally in the pattern; `*`, `?`, `**`,
+    /// or `[...]` will not match. This is useful because such files are
+    /// conventionally considered hidden on Unix systems and it might be
+    /// desirable to skip them when listing files.
+    ///
+    /// Defaults to `false` on Unix systems and `true` on Windows
+    // dotfiles are not supposed to be exposed by default on unix
+    #[serde(alias = "require-literal-leading-dot")]
+    require_literal_leading_dot: Option<bool>,
   },
 }
 
@@ -2580,7 +2565,7 @@ pub struct UpdaterConfig {
   #[serde(default)]
   pub active: bool,
   /// Display built-in dialog or use event system if disabled.
-  #[serde(default = "default_dialog")]
+  #[serde(default = "default_true")]
   pub dialog: bool,
   /// The updater endpoints. TLS is enforced on production.
   ///
@@ -2611,7 +2596,7 @@ impl<'de> Deserialize<'de> for UpdaterConfig {
     struct InnerUpdaterConfig {
       #[serde(default)]
       active: bool,
-      #[serde(default = "default_dialog")]
+      #[serde(default = "default_true")]
       dialog: bool,
       endpoints: Option<Vec<UpdaterEndpoint>>,
       pubkey: Option<String>,
@@ -2641,7 +2626,7 @@ impl Default for UpdaterConfig {
   fn default() -> Self {
     Self {
       active: false,
-      dialog: default_dialog(),
+      dialog: true,
       endpoints: None,
       pubkey: "".into(),
       windows: Default::default(),
@@ -2664,26 +2649,12 @@ pub struct SystemTrayConfig {
   #[serde(default, alias = "icon-as-template")]
   pub icon_as_template: bool,
   /// A Boolean value that determines whether the menu should appear when the tray icon receives a left click on macOS.
-  #[serde(
-    default = "default_tray_menu_on_left_click",
-    alias = "menu-on-left-click"
-  )]
+  #[serde(default = "default_true", alias = "menu-on-left-click")]
   pub menu_on_left_click: bool,
   /// Title for MacOS tray
   pub title: Option<String>,
 }
 
-fn default_tray_menu_on_left_click() -> bool {
-  true
-}
-
-// We enable the unnecessary_wraps because we need
-// to use an Option for dialog otherwise the CLI schema will mark
-// the dialog as a required field which is not as we default it to true.
-fn default_dialog() -> bool {
-  true
-}
-
 /// Defines the URL or assets to embed in the application.
 #[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
 #[cfg_attr(feature = "schema", derive(JsonSchema))]
@@ -3709,10 +3680,11 @@ mod build {
           let allowed_paths = vec_lit(allow, path_buf_lit);
           quote! { #prefix::AllowedPaths(#allowed_paths) }
         }
-        Self::Scope { allow, deny } => {
+        Self::Scope { allow, deny , require_literal_leading_dot} => {
           let allow = vec_lit(allow, path_buf_lit);
           let deny = vec_lit(deny, path_buf_lit);
-          quote! { #prefix::Scope { allow: #allow, deny: #deny } }
+          let  require_literal_leading_dot = opt_lit(require_literal_leading_dot.as_ref());
+          quote! { #prefix::Scope { allow: #allow, deny: #deny, require_literal_leading_dot: #require_literal_leading_dot } }
         }
       });
     }

+ 33 - 12
core/tauri/src/scope/fs.rs

@@ -35,6 +35,7 @@ pub struct Scope {
   allowed_patterns: Arc<Mutex<HashSet<Pattern>>>,
   forbidden_patterns: Arc<Mutex<HashSet<Pattern>>>,
   event_listeners: Arc<Mutex<HashMap<Uuid, EventListener>>>,
+  match_options: glob::MatchOptions,
 }
 
 impl fmt::Debug for Scope {
@@ -106,10 +107,29 @@ impl Scope {
       }
     }
 
+    let require_literal_leading_dot = match scope {
+      FsAllowlistScope::Scope {
+        require_literal_leading_dot: Some(require),
+        ..
+      } => *require,
+      // dotfiles are not supposed to be exposed by default on unix
+      #[cfg(unix)]
+      _ => false,
+      #[cfg(windows)]
+      _ => true,
+    };
+
     Ok(Self {
       allowed_patterns: Arc::new(Mutex::new(allowed_patterns)),
       forbidden_patterns: Arc::new(Mutex::new(forbidden_patterns)),
       event_listeners: Default::default(),
+      match_options: glob::MatchOptions {
+        // this is needed so `/dir/*` doesn't match files within subdirectories such as `/dir/subdir/file.txt`
+        // see: https://github.com/tauri-apps/tauri/security/advisories/GHSA-6mv3-wm7j-h4w5
+        require_literal_separator: true,
+        require_literal_leading_dot,
+        ..Default::default()
+      },
     })
   }
 
@@ -216,22 +236,12 @@ impl Scope {
 
     if let Ok(path) = path {
       let path: PathBuf = path.components().collect();
-      let options = glob::MatchOptions {
-        // this is needed so `/dir/*` doesn't match files within subdirectories such as `/dir/subdir/file.txt`
-        // see: https://github.com/tauri-apps/tauri/security/advisories/GHSA-6mv3-wm7j-h4w5
-        require_literal_separator: true,
-        // dotfiles are not supposed to be exposed by default
-        #[cfg(unix)]
-        require_literal_leading_dot: true,
-        ..Default::default()
-      };
-
       let forbidden = self
         .forbidden_patterns
         .lock()
         .unwrap()
         .iter()
-        .any(|p| p.matches_path_with(&path, options));
+        .any(|p| p.matches_path_with(&path, self.match_options));
 
       if forbidden {
         false
@@ -241,7 +251,7 @@ impl Scope {
           .lock()
           .unwrap()
           .iter()
-          .any(|p| p.matches_path_with(&path, options));
+          .any(|p| p.matches_path_with(&path, self.match_options));
         allowed
       }
     } else {
@@ -271,6 +281,17 @@ mod tests {
       allowed_patterns: Default::default(),
       forbidden_patterns: Default::default(),
       event_listeners: Default::default(),
+      match_options: glob::MatchOptions {
+        // this is needed so `/dir/*` doesn't match files within subdirectories such as `/dir/subdir/file.txt`
+        // see: https://github.com/tauri-apps/tauri/security/advisories/GHSA-6mv3-wm7j-h4w5
+        require_literal_separator: true,
+        // dotfiles are not supposed to be exposed by default on unix
+        #[cfg(unix)]
+        require_literal_leading_dot: false,
+        #[cfg(windows)]
+        require_literal_leading_dot: true,
+        ..Default::default()
+      },
     }
   }
 

+ 7 - 0
tooling/cli/schema.json

@@ -2069,6 +2069,13 @@
               "items": {
                 "type": "string"
               }
+            },
+            "requireLiteralLeadingDot": {
+              "description": "Whether or not paths that contain components that start with a `.` will require that `.` appears literally in the pattern; `*`, `?`, `**`, or `[...]` will not match. This is useful because such files are conventionally considered hidden on Unix systems and it might be desirable to skip them when listing files.\n\nDefaults to `false` on Unix systems and `true` on Windows",
+              "type": [
+                "boolean",
+                "null"
+              ]
             }
           }
         }