Skip to content

Commit d5d6d2a

Browse files
chipperslucasfernog
authored andcommitted
Isolation Pattern (#43)
Co-authored-by: Ngo Iok Ui (Wu Yu Wei) <wusyong9104@gmail.com> Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.studio>
1 parent c077f44 commit d5d6d2a

110 files changed

Lines changed: 6112 additions & 853 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changes/isolation-pattern.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"tauri": patch
3+
"tauri-utils": patch
4+
"tauri-codegen": patch
5+
---
6+
7+
Added the `isolation` pattern.

.changes/rust-1.57.md

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 minimum Rust version is now 1.57.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ exclude = [
1414
"examples/api/src-tauri",
1515
"examples/updater/src-tauri",
1616
"examples/resources/src-tauri",
17-
"examples/sidecar/src-tauri"
17+
"examples/sidecar/src-tauri",
18+
"examples/isolation/src-tauri"
1819
]
1920

2021
# default to small, optimized workspace release binaries

core/tauri-build/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ homepage = "https://tauri.studio"
88
repository = "https://github.com/tauri-apps/tauri/tree/dev/core/tauri-build"
99
description = "build time code to pair with https://crates.io/crates/tauri"
1010
edition = "2021"
11-
rust-version = "1.56"
11+
rust-version = "1.57"
1212
exclude = [ ".license_template", "CHANGELOG.md", "/target" ]
1313
readme = "README.md"
1414

@@ -29,3 +29,4 @@ winres = "0.1"
2929

3030
[features]
3131
codegen = [ "tauri-codegen", "quote" ]
32+
isolation = ["tauri-codegen/isolation", "tauri-utils/isolation"]

core/tauri-build/src/codegen/context.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ impl CodegenContext {
4242
/// This defaults to a file called `tauri.conf.json` inside of the current working directory of
4343
/// the package compiling; does not need to be set manually if that config file is in the same
4444
/// directory as your `Cargo.toml`.
45+
#[must_use]
4546
pub fn config_path(mut self, config_path: impl Into<PathBuf>) -> Self {
4647
self.config_path = config_path.into();
4748
self
@@ -58,13 +59,15 @@ impl CodegenContext {
5859
/// Defaults to `tauri-build-context.rs`.
5960
///
6061
/// [`tauri::include_codegen_context!`]: https://docs.rs/tauri/0.12/tauri/macro.include_codegen_context.html
62+
#[must_use]
6163
pub fn out_file(mut self, filename: PathBuf) -> Self {
6264
self.out_file = filename;
6365
self
6466
}
6567

6668
/// Run the codegen in a `dev` context, meaning that Tauri is using a dev server or local file for development purposes,
6769
/// usually with the `tauri dev` CLI command.
70+
#[must_use]
6871
pub fn dev(mut self) -> Self {
6972
self.dev = true;
7073
self

core/tauri-build/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,15 @@ impl WindowsAttributes {
4343

4444
/// Sets the icon to use on the window. Currently only used on Windows.
4545
/// It must be in `ico` format. Defaults to `icons/icon.ico`.
46+
#[must_use]
4647
pub fn window_icon_path<P: AsRef<Path>>(mut self, window_icon_path: P) -> Self {
4748
self.window_icon_path = window_icon_path.as_ref().into();
4849
self
4950
}
5051

5152
/// Sets the sdk dir for windows. Currently only used on Windows. This must be a vaild UTF-8
5253
/// path. Defaults to whatever the `winres` crate determines is best.
54+
#[must_use]
5355
pub fn sdk_dir<P: AsRef<Path>>(mut self, sdk_dir: P) -> Self {
5456
self.sdk_dir = Some(sdk_dir.as_ref().into());
5557
self
@@ -70,6 +72,7 @@ impl Attributes {
7072
}
7173

7274
/// Sets the icon to use on the window. Currently only used on Windows.
75+
#[must_use]
7376
pub fn windows_attributes(mut self, windows_attributes: WindowsAttributes) -> Self {
7477
self.windows_attributes = windows_attributes;
7578
self

core/tauri-codegen/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ homepage = "https://tauri.studio"
88
repository = "https://github.com/tauri-apps/tauri/tree/dev/core/tauri-codegen"
99
description = "code generation meant to be consumed inside of `tauri` through `tauri-build` or `tauri-macros`"
1010
edition = "2021"
11-
rust-version = "1.56"
11+
rust-version = "1.57"
1212
exclude = [ ".license_template", "CHANGELOG.md", "/target" ]
1313
readme = "README.md"
1414

@@ -25,7 +25,9 @@ thiserror = "1"
2525
walkdir = "2"
2626
zstd = { version = "0.9", optional = true }
2727
regex = "1"
28+
uuid = { version = "0.8", features = [ "v4" ] }
2829

2930
[features]
3031
default = [ "compression" ]
3132
compression = [ "zstd", "tauri-utils/compression" ]
33+
isolation = ["tauri-utils/isolation"]

core/tauri-codegen/src/context.rs

Lines changed: 115 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,18 @@
22
// SPDX-License-Identifier: Apache-2.0
33
// SPDX-License-Identifier: MIT
44

5-
use crate::embedded_assets::{AssetOptions, EmbeddedAssets, EmbeddedAssetsError};
5+
use std::ffi::OsStr;
6+
use std::path::{Path, PathBuf};
7+
68
use proc_macro2::TokenStream;
79
use quote::quote;
8-
use std::path::{Path, PathBuf};
9-
use tauri_utils::config::{AppUrl, Config, WindowUrl};
10+
use sha2::{Digest, Sha256};
11+
12+
use tauri_utils::assets::AssetKey;
13+
use tauri_utils::config::{AppUrl, Config, PatternKind, WindowUrl};
14+
use tauri_utils::html::{inject_nonce_token, parse as parse_html, NodeRef, PatternObject};
15+
16+
use crate::embedded_assets::{AssetOptions, CspHashes, EmbeddedAssets, EmbeddedAssetsError};
1017

1118
/// Necessary data needed by [`context_codegen`] to generate code for a Tauri application context.
1219
pub struct ContextData {
@@ -16,6 +23,81 @@ pub struct ContextData {
1623
pub root: TokenStream,
1724
}
1825

26+
fn load_csp(document: &mut NodeRef, key: &AssetKey, csp_hashes: &mut CspHashes) {
27+
#[cfg(target_os = "linux")]
28+
::tauri_utils::html::inject_csp_token(document);
29+
inject_nonce_token(document);
30+
if let Ok(inline_script_elements) = document.select("script:not(empty)") {
31+
let mut scripts = Vec::new();
32+
for inline_script_el in inline_script_elements {
33+
let script = inline_script_el.as_node().text_contents();
34+
let mut hasher = Sha256::new();
35+
hasher.update(&script);
36+
let hash = hasher.finalize();
37+
scripts.push(format!("'sha256-{}'", base64::encode(&hash)));
38+
}
39+
csp_hashes
40+
.inline_scripts
41+
.entry(key.clone().into())
42+
.or_default()
43+
.append(&mut scripts);
44+
}
45+
}
46+
47+
fn map_core_assets(
48+
options: &AssetOptions,
49+
) -> impl Fn(&AssetKey, &Path, &mut Vec<u8>, &mut CspHashes) -> Result<(), EmbeddedAssetsError> {
50+
#[allow(unused_variables)]
51+
let pattern = PatternObject::from(&options.pattern);
52+
let csp = options.csp;
53+
move |key, path, input, csp_hashes| {
54+
if path.extension() == Some(OsStr::new("html")) {
55+
let mut document = parse_html(String::from_utf8_lossy(input).into_owned());
56+
57+
if csp {
58+
load_csp(&mut document, key, csp_hashes);
59+
60+
#[cfg(feature = "isolation")]
61+
if let PatternObject::Isolation { .. } = &pattern {
62+
// create the csp for the isolation iframe styling now, to make the runtime less complex
63+
let mut hasher = Sha256::new();
64+
hasher.update(tauri_utils::pattern::isolation::IFRAME_STYLE);
65+
let hash = hasher.finalize();
66+
csp_hashes
67+
.styles
68+
.push(format!("'sha256-{}'", base64::encode(&hash)));
69+
}
70+
}
71+
72+
*input = document.to_string().as_bytes().to_vec();
73+
}
74+
Ok(())
75+
}
76+
}
77+
78+
#[cfg(feature = "isolation")]
79+
fn map_isolation(
80+
_options: &AssetOptions,
81+
dir: PathBuf,
82+
) -> impl Fn(&AssetKey, &Path, &mut Vec<u8>, &mut CspHashes) -> Result<(), EmbeddedAssetsError> {
83+
move |_key, path, input, _csp_hashes| {
84+
if path.extension() == Some(OsStr::new("html")) {
85+
let mut isolation_html =
86+
tauri_utils::html::parse(String::from_utf8_lossy(input).into_owned());
87+
88+
// this is appended, so no need to reverse order it
89+
tauri_utils::html::inject_codegen_isolation_script(&mut isolation_html);
90+
91+
// temporary workaround for windows not loading assets
92+
tauri_utils::html::inline_isolation(&mut isolation_html, &dir);
93+
94+
*input = isolation_html.to_string().as_bytes().to_vec()
95+
}
96+
97+
Ok(())
98+
}
99+
}
100+
19101
/// Build a `tauri::Context` for including in application code.
20102
pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsError> {
21103
let ContextData {
@@ -25,7 +107,8 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
25107
root,
26108
} = data;
27109

28-
let mut options = AssetOptions::new();
110+
let mut options = AssetOptions::new(config.tauri.pattern.clone())
111+
.freeze_prototype(config.tauri.security.freeze_prototype);
29112
let csp = if dev {
30113
config
31114
.tauri
@@ -64,7 +147,7 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
64147
path
65148
)
66149
}
67-
EmbeddedAssets::new(assets_path, options)?
150+
EmbeddedAssets::new(assets_path, map_core_assets(&options))?
68151
}
69152
_ => unimplemented!(),
70153
},
@@ -73,7 +156,7 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
73156
.iter()
74157
.map(|p| config_parent.join(p))
75158
.collect::<Vec<_>>(),
76-
options,
159+
map_core_assets(&options),
77160
)?,
78161
_ => unimplemented!(),
79162
};
@@ -180,14 +263,39 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
180263
#[cfg(not(target_os = "macos"))]
181264
let info_plist = quote!(());
182265

183-
// double braces are purposeful to force the code into a block expression
266+
let pattern = match &options.pattern {
267+
PatternKind::Brownfield => quote!(#root::Pattern::Brownfield(std::marker::PhantomData)),
268+
#[cfg(feature = "isolation")]
269+
PatternKind::Isolation { dir } => {
270+
let dir = config_parent.join(dir);
271+
if !dir.exists() {
272+
panic!(
273+
"The isolation dir configuration is set to `{:?}` but this path doesn't exist",
274+
dir
275+
)
276+
}
277+
278+
let key = uuid::Uuid::new_v4().to_string();
279+
let assets = EmbeddedAssets::new(dir.clone(), map_isolation(&options, dir))?;
280+
let schema = options.isolation_schema;
281+
282+
quote!(#root::Pattern::Isolation {
283+
assets: ::std::sync::Arc::new(#assets),
284+
schema: #schema.into(),
285+
key: #key.into(),
286+
crypto_keys: std::boxed::Box::new(::tauri::utils::pattern::isolation::Keys::new().expect("unable to generate cryptographically secure keys for Tauri \"Isolation\" Pattern")),
287+
})
288+
}
289+
};
290+
184291
Ok(quote!(#root::Context::new(
185292
#config,
186293
::std::sync::Arc::new(#assets),
187294
#default_window_icon,
188295
#system_tray_icon,
189296
#package_info,
190297
#info_plist,
298+
#pattern
191299
)))
192300
}
193301

0 commit comments

Comments
 (0)