Skip to content

Commit

Permalink
feat(core): add option for require_literal_leading_dot, closes #6158
Browse files Browse the repository at this point in the history
  • Loading branch information
amrbashir committed May 15, 2023
1 parent 46a58af commit 485fc0e
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 79 deletions.
5 changes: 5 additions & 0 deletions .changes/config-require-literal_leading_dot.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'tauri-utils': 'patch'
---

Add option to configure `require_literal_leading_dot` on `fs` and `asset` protcol scopes.
8 changes: 7 additions & 1 deletion core/tauri-config-schema/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2069,8 +2069,14 @@
"items": {
"type": "string"
}
},
"require_literal_leading_dot": {
"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 `true` on Unix systems and `true` on Windows",
"default": true,
"type": "boolean"
}
}
},
"additionalProperties": false
}
]
},
Expand Down
103 changes: 38 additions & 65 deletions core/tauri-utils/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))]
Expand Down Expand Up @@ -519,23 +523,23 @@ 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.
/// Requires an internet connection.
/// 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.
/// Does not require an internet connection.
/// 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.
Expand All @@ -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 }
}
}

Expand Down Expand Up @@ -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>,
Expand All @@ -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
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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")]
Expand All @@ -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.
///
Expand All @@ -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")]
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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)]
Expand Down Expand Up @@ -1304,6 +1278,7 @@ macro_rules! check_feature {
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(untagged)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub enum FsAllowlistScope {
/// A list of paths that are allowed by this scope.
AllowedPaths(Vec<PathBuf>),
Expand All @@ -1316,6 +1291,17 @@ 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 `true` on Unix systems and `true` on Windows
// dotfiles are not supposed to be exposed by default on unix
#[cfg_attr(unix, serde(default = "default_true"))]
#[cfg_attr(windows, serde(default = "default_true"))]
require_literal_leading_dot: bool,
},
}

Expand Down Expand Up @@ -2570,7 +2556,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.
///
Expand Down Expand Up @@ -2601,7 +2587,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>,
Expand Down Expand Up @@ -2631,7 +2617,7 @@ impl Default for UpdaterConfig {
fn default() -> Self {
Self {
active: false,
dialog: default_dialog(),
dialog: true,
endpoints: None,
pubkey: "".into(),
windows: Default::default(),
Expand All @@ -2654,26 +2640,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))]
Expand Down Expand Up @@ -3699,10 +3671,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 = require_literal_leading_dot;
quote! { #prefix::Scope { allow: #allow, deny: #deny, require_literal_leading_dot: #require_literal_leading_dot } }
}
});
}
Expand Down
45 changes: 33 additions & 12 deletions core/tauri/src/scope/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -106,10 +107,29 @@ impl Scope {
}
}

let require_literal_leading_dot = match scope {
// dotfiles are not supposed to be exposed by default on unix
#[cfg(unix)]
FsAllowlistScope::AllowedPaths(_) => false,
#[cfg(windows)]
FsAllowlistScope::AllowedPaths(_) => true,
FsAllowlistScope::Scope {
require_literal_leading_dot,
..
} => *require_literal_leading_dot,
};

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()
},
})
}

Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -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()
},
}
}

Expand Down
Loading

0 comments on commit 485fc0e

Please sign in to comment.