Skip to content

Commit 7920ff1

Browse files
committed
feat: scope the fs API and the asset protocol [TRI-026] [TRI-010] [TRI-011] (#10)
1 parent 3420aa5 commit 7920ff1

25 files changed

Lines changed: 679 additions & 117 deletions

File tree

.changes/asset-allowlist.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri-utils": patch
3+
---
4+
5+
Added `asset` allowlist configuration, which enables the `asset` protocol and defines it access scope.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri": patch
3+
---
4+
5+
The `asset://` custom protocol is only defined when either the `api-all`, `protocol-all` or `protocol-asset` feature flags are enabled. These feature flags are accessible with the `tauri.conf.json` allowlist.

.changes/scope-config.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri-utils": patch
3+
---
4+
5+
Adds `scope` glob array config under `tauri > allowlist > fs`.

.changes/scopes.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"tauri": patch
3+
---
4+
5+
Scopes the filesystem APIs from the webview access using `tauri.conf.json > tauri > allowlist > fs > scope`.
6+
Scopes the `asset` protocol access using `tauri.conf.json > tauri > allowlist > protocol > assetScope`.

core/tauri-utils/src/config.rs

Lines changed: 210 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -556,8 +556,10 @@ pub struct SecurityConfig {
556556

557557
/// Defines an allowlist type.
558558
pub trait Allowlist {
559+
/// Returns all features associated with the allowlist struct.
560+
fn all_features() -> Vec<&'static str>;
559561
/// Returns the tauri features enabled on this allowlist.
560-
fn to_features(&self) -> Vec<&str>;
562+
fn to_features(&self) -> Vec<&'static str>;
561563
}
562564

563565
macro_rules! check_feature {
@@ -568,11 +570,29 @@ macro_rules! check_feature {
568570
};
569571
}
570572

573+
/// Filesystem API scope definition.
574+
/// It is a list of glob patterns that restrict the filesystem API access from the webview.
575+
/// Each pattern can start with a variable that resolves to a system base directory.
576+
/// The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`,
577+
/// `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`,
578+
/// `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$CWD`.
579+
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
580+
#[cfg_attr(feature = "schema", derive(JsonSchema))]
581+
pub struct FsAllowlistScope(pub Vec<PathBuf>);
582+
583+
impl Default for FsAllowlistScope {
584+
fn default() -> Self {
585+
Self(vec!["$APP/**".into()])
586+
}
587+
}
588+
571589
/// Allowlist for the file system APIs.
572590
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
573591
#[cfg_attr(feature = "schema", derive(JsonSchema))]
574592
#[serde(rename_all = "camelCase", deny_unknown_fields)]
575593
pub struct FsAllowlistConfig {
594+
/// The access scope for the filesystem APIs.
595+
pub scope: FsAllowlistScope,
576596
/// Use this flag to enable all file system API features.
577597
#[serde(default)]
578598
pub all: bool,
@@ -609,7 +629,27 @@ pub struct FsAllowlistConfig {
609629
}
610630

611631
impl Allowlist for FsAllowlistConfig {
612-
fn to_features(&self) -> Vec<&str> {
632+
fn all_features() -> Vec<&'static str> {
633+
let allowlist = Self {
634+
scope: Default::default(),
635+
all: false,
636+
read_text_file: true,
637+
read_binary_file: true,
638+
write_file: true,
639+
write_binary_file: true,
640+
read_dir: true,
641+
copy_file: true,
642+
create_dir: true,
643+
remove_dir: true,
644+
remove_file: true,
645+
rename_file: true,
646+
};
647+
let mut features = allowlist.to_features();
648+
features.push("fs-all");
649+
features
650+
}
651+
652+
fn to_features(&self) -> Vec<&'static str> {
613653
if self.all {
614654
vec!["fs-all"]
615655
} else {
@@ -643,7 +683,17 @@ pub struct WindowAllowlistConfig {
643683
}
644684

645685
impl Allowlist for WindowAllowlistConfig {
646-
fn to_features(&self) -> Vec<&str> {
686+
fn all_features() -> Vec<&'static str> {
687+
let allowlist = Self {
688+
all: false,
689+
create: true,
690+
};
691+
let mut features = allowlist.to_features();
692+
features.push("window-all");
693+
features
694+
}
695+
696+
fn to_features(&self) -> Vec<&'static str> {
647697
if self.all {
648698
vec!["window-all"]
649699
} else {
@@ -671,7 +721,18 @@ pub struct ShellAllowlistConfig {
671721
}
672722

673723
impl Allowlist for ShellAllowlistConfig {
674-
fn to_features(&self) -> Vec<&str> {
724+
fn all_features() -> Vec<&'static str> {
725+
let allowlist = Self {
726+
all: false,
727+
execute: true,
728+
open: true,
729+
};
730+
let mut features = allowlist.to_features();
731+
features.push("shell-all");
732+
features
733+
}
734+
735+
fn to_features(&self) -> Vec<&'static str> {
675736
if self.all {
676737
vec!["shell-all"]
677738
} else {
@@ -700,7 +761,18 @@ pub struct DialogAllowlistConfig {
700761
}
701762

702763
impl Allowlist for DialogAllowlistConfig {
703-
fn to_features(&self) -> Vec<&str> {
764+
fn all_features() -> Vec<&'static str> {
765+
let allowlist = Self {
766+
all: false,
767+
open: true,
768+
save: true,
769+
};
770+
let mut features = allowlist.to_features();
771+
features.push("dialog-all");
772+
features
773+
}
774+
775+
fn to_features(&self) -> Vec<&'static str> {
704776
if self.all {
705777
vec!["dialog-all"]
706778
} else {
@@ -726,7 +798,17 @@ pub struct HttpAllowlistConfig {
726798
}
727799

728800
impl Allowlist for HttpAllowlistConfig {
729-
fn to_features(&self) -> Vec<&str> {
801+
fn all_features() -> Vec<&'static str> {
802+
let allowlist = Self {
803+
all: false,
804+
request: true,
805+
};
806+
let mut features = allowlist.to_features();
807+
features.push("http-all");
808+
features
809+
}
810+
811+
fn to_features(&self) -> Vec<&'static str> {
730812
if self.all {
731813
vec!["http-all"]
732814
} else {
@@ -748,7 +830,14 @@ pub struct NotificationAllowlistConfig {
748830
}
749831

750832
impl Allowlist for NotificationAllowlistConfig {
751-
fn to_features(&self) -> Vec<&str> {
833+
fn all_features() -> Vec<&'static str> {
834+
let allowlist = Self { all: false };
835+
let mut features = allowlist.to_features();
836+
features.push("notification-all");
837+
features
838+
}
839+
840+
fn to_features(&self) -> Vec<&'static str> {
752841
if self.all {
753842
vec!["notification-all"]
754843
} else {
@@ -768,7 +857,14 @@ pub struct GlobalShortcutAllowlistConfig {
768857
}
769858

770859
impl Allowlist for GlobalShortcutAllowlistConfig {
771-
fn to_features(&self) -> Vec<&str> {
860+
fn all_features() -> Vec<&'static str> {
861+
let allowlist = Self { all: false };
862+
let mut features = allowlist.to_features();
863+
features.push("global-shortcut-all");
864+
features
865+
}
866+
867+
fn to_features(&self) -> Vec<&'static str> {
772868
if self.all {
773869
vec!["global-shortcut-all"]
774870
} else {
@@ -788,7 +884,14 @@ pub struct OsAllowlistConfig {
788884
}
789885

790886
impl Allowlist for OsAllowlistConfig {
791-
fn to_features(&self) -> Vec<&str> {
887+
fn all_features() -> Vec<&'static str> {
888+
let allowlist = Self { all: false };
889+
let mut features = allowlist.to_features();
890+
features.push("os-all");
891+
features
892+
}
893+
894+
fn to_features(&self) -> Vec<&'static str> {
792895
if self.all {
793896
vec!["os-all"]
794897
} else {
@@ -808,7 +911,14 @@ pub struct PathAllowlistConfig {
808911
}
809912

810913
impl Allowlist for PathAllowlistConfig {
811-
fn to_features(&self) -> Vec<&str> {
914+
fn all_features() -> Vec<&'static str> {
915+
let allowlist = Self { all: false };
916+
let mut features = allowlist.to_features();
917+
features.push("path-all");
918+
features
919+
}
920+
921+
fn to_features(&self) -> Vec<&'static str> {
812922
if self.all {
813923
vec!["path-all"]
814924
} else {
@@ -817,6 +927,44 @@ impl Allowlist for PathAllowlistConfig {
817927
}
818928
}
819929

930+
/// Allowlist for the custom protocols.
931+
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
932+
#[cfg_attr(feature = "schema", derive(JsonSchema))]
933+
#[serde(rename_all = "camelCase", deny_unknown_fields)]
934+
pub struct ProtocolAllowlistConfig {
935+
/// The access scope for the asset protocol.
936+
pub asset_scope: FsAllowlistScope,
937+
/// Use this flag to enable all custom protocols.
938+
#[serde(default)]
939+
pub all: bool,
940+
/// Enables the asset protocol.
941+
#[serde(default)]
942+
pub asset: bool,
943+
}
944+
945+
impl Allowlist for ProtocolAllowlistConfig {
946+
fn all_features() -> Vec<&'static str> {
947+
let allowlist = Self {
948+
asset_scope: Default::default(),
949+
all: false,
950+
asset: true,
951+
};
952+
let mut features = allowlist.to_features();
953+
features.push("protocol-all");
954+
features
955+
}
956+
957+
fn to_features(&self) -> Vec<&'static str> {
958+
if self.all {
959+
vec!["protocol-all"]
960+
} else {
961+
let mut features = Vec::new();
962+
check_feature!(self, features, asset, "protocol-asset");
963+
features
964+
}
965+
}
966+
}
967+
820968
/// Allowlist configuration.
821969
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
822970
#[cfg_attr(feature = "schema", derive(JsonSchema))]
@@ -852,10 +1000,28 @@ pub struct AllowlistConfig {
8521000
/// Path API allowlist.
8531001
#[serde(default)]
8541002
pub path: PathAllowlistConfig,
1003+
/// Custom protocol allowlist.
1004+
#[serde(default)]
1005+
pub protocol: ProtocolAllowlistConfig,
8551006
}
8561007

8571008
impl Allowlist for AllowlistConfig {
858-
fn to_features(&self) -> Vec<&str> {
1009+
fn all_features() -> Vec<&'static str> {
1010+
let mut features = Vec::new();
1011+
features.extend(FsAllowlistConfig::all_features());
1012+
features.extend(WindowAllowlistConfig::all_features());
1013+
features.extend(ShellAllowlistConfig::all_features());
1014+
features.extend(DialogAllowlistConfig::all_features());
1015+
features.extend(HttpAllowlistConfig::all_features());
1016+
features.extend(NotificationAllowlistConfig::all_features());
1017+
features.extend(GlobalShortcutAllowlistConfig::all_features());
1018+
features.extend(OsAllowlistConfig::all_features());
1019+
features.extend(PathAllowlistConfig::all_features());
1020+
features.extend(ProtocolAllowlistConfig::all_features());
1021+
features
1022+
}
1023+
1024+
fn to_features(&self) -> Vec<&'static str> {
8591025
if self.all {
8601026
vec!["api-all"]
8611027
} else {
@@ -869,6 +1035,7 @@ impl Allowlist for AllowlistConfig {
8691035
features.extend(self.global_shortcut.to_features());
8701036
features.extend(self.os.to_features());
8711037
features.extend(self.path.to_features());
1038+
features.extend(self.protocol.to_features());
8721039
features
8731040
}
8741041
}
@@ -1616,6 +1783,37 @@ mod build {
16161783
}
16171784
}
16181785

1786+
impl ToTokens for FsAllowlistScope {
1787+
fn to_tokens(&self, tokens: &mut TokenStream) {
1788+
let allowed_paths = vec_lit(&self.0, path_buf_lit);
1789+
tokens.append_all(quote! { ::tauri::utils::config::FsAllowlistScope(#allowed_paths) })
1790+
}
1791+
}
1792+
1793+
impl ToTokens for FsAllowlistConfig {
1794+
fn to_tokens(&self, tokens: &mut TokenStream) {
1795+
let scope = &self.scope;
1796+
tokens.append_all(quote! { ::tauri::utils::config::FsAllowlistConfig { scope: #scope, ..Default::default() } })
1797+
}
1798+
}
1799+
1800+
impl ToTokens for ProtocolAllowlistConfig {
1801+
fn to_tokens(&self, tokens: &mut TokenStream) {
1802+
let asset_scope = &self.asset_scope;
1803+
tokens.append_all(quote! { ::tauri::utils::config::ProtocolAllowlistConfig { asset_scope: #asset_scope, ..Default::default() } })
1804+
}
1805+
}
1806+
1807+
impl ToTokens for AllowlistConfig {
1808+
fn to_tokens(&self, tokens: &mut TokenStream) {
1809+
let fs = &self.fs;
1810+
let protocol = &self.protocol;
1811+
tokens.append_all(
1812+
quote! { ::tauri::utils::config::AllowlistConfig { fs: #fs, protocol: #protocol, ..Default::default() } },
1813+
)
1814+
}
1815+
}
1816+
16191817
impl ToTokens for TauriConfig {
16201818
fn to_tokens(&self, tokens: &mut TokenStream) {
16211819
let windows = vec_lit(&self.windows, identity);
@@ -1624,7 +1822,7 @@ mod build {
16241822
let updater = &self.updater;
16251823
let security = &self.security;
16261824
let system_tray = opt_lit(self.system_tray.as_ref());
1627-
let allowlist = quote!(Default::default());
1825+
let allowlist = &self.allowlist;
16281826
let macos_private_api = self.macos_private_api;
16291827

16301828
literal_struct!(

core/tauri-utils/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ pub struct Env {
4848
pub appdir: Option<std::ffi::OsString>,
4949
}
5050

51+
#[allow(clippy::derivable_impls)]
5152
impl Default for Env {
5253
fn default() -> Self {
5354
Self {

0 commit comments

Comments
 (0)