Jelajahi Sumber

feat(core): add dangerous option to disable compile time CSP injection (#3775)

Lucas Fernandes Nogueira 3 tahun lalu
induk
melakukan
f6e32ee188

+ 7 - 0
.changes/dangerous-disable-asset-csp.md

@@ -0,0 +1,7 @@
+---
+"tauri": patch
+"tauri-codegen": patch
+"tauri-utils": patch
+---
+
+Added an option to disable the CSP injection of distributable assets nonces and hashes.

+ 20 - 15
core/tauri-codegen/src/context.rs

@@ -27,8 +27,6 @@ pub struct ContextData {
 }
 
 fn load_csp(document: &mut NodeRef, key: &AssetKey, csp_hashes: &mut CspHashes) {
-  #[cfg(target_os = "linux")]
-  ::tauri_utils::html::inject_csp_token(document);
   inject_nonce_token(document);
   if let Ok(inline_script_elements) = document.select("script:not(empty)") {
     let mut scripts = Vec::new();
@@ -53,22 +51,28 @@ fn map_core_assets(
   #[cfg(feature = "isolation")]
   let pattern = tauri_utils::html::PatternObject::from(&options.pattern);
   let csp = options.csp;
+  let dangerous_disable_asset_csp_modification = options.dangerous_disable_asset_csp_modification;
   move |key, path, input, csp_hashes| {
     if path.extension() == Some(OsStr::new("html")) {
       let mut document = parse_html(String::from_utf8_lossy(input).into_owned());
 
       if csp {
-        load_csp(&mut document, key, csp_hashes);
-
-        #[cfg(feature = "isolation")]
-        if let tauri_utils::html::PatternObject::Isolation { .. } = &pattern {
-          // create the csp for the isolation iframe styling now, to make the runtime less complex
-          let mut hasher = Sha256::new();
-          hasher.update(tauri_utils::pattern::isolation::IFRAME_STYLE);
-          let hash = hasher.finalize();
-          csp_hashes
-            .styles
-            .push(format!("'sha256-{}'", base64::encode(&hash)));
+        #[cfg(target_os = "linux")]
+        ::tauri_utils::html::inject_csp_token(&mut document);
+
+        if !dangerous_disable_asset_csp_modification {
+          load_csp(&mut document, key, csp_hashes);
+
+          #[cfg(feature = "isolation")]
+          if let tauri_utils::html::PatternObject::Isolation { .. } = &pattern {
+            // create the csp for the isolation iframe styling now, to make the runtime less complex
+            let mut hasher = Sha256::new();
+            hasher.update(tauri_utils::pattern::isolation::IFRAME_STYLE);
+            let hash = hasher.finalize();
+            csp_hashes
+              .styles
+              .push(format!("'sha256-{}'", base64::encode(&hash)));
+          }
         }
       }
 
@@ -150,7 +154,7 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
             path
           )
         }
-        EmbeddedAssets::new(assets_path, map_core_assets(&options))?
+        EmbeddedAssets::new(assets_path, &options, map_core_assets(&options))?
       }
       _ => unimplemented!(),
     },
@@ -159,6 +163,7 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
         .iter()
         .map(|p| config_parent.join(p))
         .collect::<Vec<_>>(),
+      &options,
       map_core_assets(&options),
     )?,
     _ => unimplemented!(),
@@ -295,7 +300,7 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
       }
 
       let key = uuid::Uuid::new_v4().to_string();
-      let assets = EmbeddedAssets::new(dir.clone(), map_isolation(&options, dir))?;
+      let assets = EmbeddedAssets::new(dir.clone(), &options, map_isolation(&options, dir))?;
       let schema = options.isolation_schema;
 
       quote!(#root::Pattern::Isolation {

+ 18 - 4
core/tauri-codegen/src/embedded_assets.rs

@@ -94,7 +94,7 @@ struct RawEmbeddedAssets {
 
 impl RawEmbeddedAssets {
   /// Creates a new list of (prefix, entry) from a collection of inputs.
-  fn new(input: EmbeddedAssetsInput) -> Result<Self, EmbeddedAssetsError> {
+  fn new(input: EmbeddedAssetsInput, options: &AssetOptions) -> Result<Self, EmbeddedAssetsError> {
     let mut csp_hashes = CspHashes::default();
 
     input
@@ -123,7 +123,9 @@ impl RawEmbeddedAssets {
 
           // compress all files encountered
           Ok(entry) => {
-            if let Err(error) = csp_hashes.add_if_applicable(&entry) {
+            if options.dangerous_disable_asset_csp_modification {
+              Some(Ok((prefix, entry)))
+            } else if let Err(error) = csp_hashes.add_if_applicable(&entry) {
               Some(Err(error))
             } else {
               Some(Ok((prefix, entry)))
@@ -186,6 +188,7 @@ pub struct AssetOptions {
   pub(crate) csp: bool,
   pub(crate) pattern: PatternKind,
   pub(crate) freeze_prototype: bool,
+  pub(crate) dangerous_disable_asset_csp_modification: bool,
   #[cfg(feature = "isolation")]
   pub(crate) isolation_schema: String,
 }
@@ -197,12 +200,13 @@ impl AssetOptions {
       csp: false,
       pattern,
       freeze_prototype: false,
+      dangerous_disable_asset_csp_modification: false,
       #[cfg(feature = "isolation")]
       isolation_schema: format!("isolation-{}", uuid::Uuid::new_v4()),
     }
   }
 
-  /// Instruct the asset handler to inject the CSP token to HTML files.
+  /// Instruct the asset handler to inject the CSP token to HTML files (Linux only) and add asset nonces and hashes to the policy.
   #[must_use]
   pub fn with_csp(mut self) -> Self {
     self.csp = true;
@@ -215,6 +219,15 @@ impl AssetOptions {
     self.freeze_prototype = freeze;
     self
   }
+
+  /// Instruct the asset handler to **NOT** modify the CSP. This is **NOT** recommended.
+  pub fn dangerous_disable_asset_csp_modification(
+    mut self,
+    dangerous_disable_asset_csp_modification: bool,
+  ) -> Self {
+    self.dangerous_disable_asset_csp_modification = dangerous_disable_asset_csp_modification;
+    self
+  }
 }
 
 impl EmbeddedAssets {
@@ -223,10 +236,11 @@ impl EmbeddedAssets {
   /// [`Assets`]: tauri_utils::assets::Assets
   pub fn new(
     input: impl Into<EmbeddedAssetsInput>,
+    options: &AssetOptions,
     map: impl Fn(&AssetKey, &Path, &mut Vec<u8>, &mut CspHashes) -> Result<(), EmbeddedAssetsError>,
   ) -> Result<Self, EmbeddedAssetsError> {
     // we need to pre-compute all files now, so that we can inject data from all files into a few
-    let RawEmbeddedAssets { paths, csp_hashes } = RawEmbeddedAssets::new(input.into())?;
+    let RawEmbeddedAssets { paths, csp_hashes } = RawEmbeddedAssets::new(input.into(), options)?;
 
     struct CompressState {
       csp_hashes: CspHashes,

+ 20 - 1
core/tauri-utils/src/config.rs

@@ -781,6 +781,16 @@ pub struct SecurityConfig {
   /// Freeze the `Object.prototype` when using the custom protocol.
   #[serde(default)]
   pub freeze_prototype: bool,
+  /// Disables the Tauri-injected CSP sources.
+  ///
+  /// At compile time, Tauri parses all the frontend assets and changes the Content-Security-Policy
+  /// to only allow loading of your own scripts and styles by injecting nonce and hash sources.
+  /// This stricts your CSP, which may introduce issues when using along with other flexing sources.
+  ///
+  /// **WARNING:** Only disable this if you know what you are doing and have properly configured the CSP.
+  /// Your application might be vulnerable to XSS attacks without this Tauri protection.
+  #[serde(default)]
+  pub dangerous_disable_asset_csp_modification: bool,
 }
 
 /// Defines an allowlist type.
@@ -2623,8 +2633,16 @@ mod build {
       let csp = opt_lit(self.csp.as_ref());
       let dev_csp = opt_lit(self.dev_csp.as_ref());
       let freeze_prototype = self.freeze_prototype;
+      let dangerous_disable_asset_csp_modification = self.dangerous_disable_asset_csp_modification;
 
-      literal_struct!(tokens, SecurityConfig, csp, dev_csp, freeze_prototype);
+      literal_struct!(
+        tokens,
+        SecurityConfig,
+        csp,
+        dev_csp,
+        freeze_prototype,
+        dangerous_disable_asset_csp_modification
+      );
     }
   }
 
@@ -2874,6 +2892,7 @@ mod test {
         csp: None,
         dev_csp: None,
         freeze_prototype: false,
+        dangerous_disable_asset_csp_modification: false,
       },
       allowlist: AllowlistConfig::default(),
       system_tray: None,

+ 40 - 145
examples/api/src-tauri/Cargo.lock

@@ -58,6 +58,21 @@ dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "alloc-no-stdlib"
+version = "2.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35ef4730490ad1c4eae5c4325b2a95f521d023e5c885853ff7aca0a6a1631db3"
+
+[[package]]
+name = "alloc-stdlib"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "697ed7edc0f1711de49ce108c541623a0af97c6c60b2f6e2b65229847ac843c2"
+dependencies = [
+ "alloc-no-stdlib",
+]
+
 [[package]]
 name = "ansi_term"
 version = "0.12.1"
@@ -95,12 +110,6 @@ version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
 
-[[package]]
-name = "arrayvec"
-version = "0.7.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
-
 [[package]]
 name = "async-broadcast"
 version = "0.3.4"
@@ -280,25 +289,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587"
 dependencies = [
  "arrayref",
- "arrayvec 0.5.2",
+ "arrayvec",
  "constant_time_eq",
 ]
 
-[[package]]
-name = "blake3"
-version = "1.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a08e53fc5a564bb15bfe6fae56bd71522205f1f91893f9c0116edad6496c183f"
-dependencies = [
- "arrayref",
- "arrayvec 0.7.2",
- "cc",
- "cfg-if",
- "constant_time_eq",
- "digest",
- "rayon",
-]
-
 [[package]]
 name = "block"
 version = "0.1.6"
@@ -314,6 +308,27 @@ dependencies = [
  "generic-array",
 ]
 
+[[package]]
+name = "brotli"
+version = "3.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f838e47a451d5a8fa552371f80024dd6ace9b7acdf25c4c3d0f9bc6816fb1c39"
+dependencies = [
+ "alloc-no-stdlib",
+ "alloc-stdlib",
+ "brotli-decompressor",
+]
+
+[[package]]
+name = "brotli-decompressor"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59ad2d4653bf5ca36ae797b1f4bb4dbddb60ce49ca4aed8a2ce4829f60425b80"
+dependencies = [
+ "alloc-no-stdlib",
+ "alloc-stdlib",
+]
+
 [[package]]
 name = "bstr"
 version = "0.2.17"
@@ -387,9 +402,6 @@ name = "cc"
 version = "1.0.73"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
-dependencies = [
- "jobserver",
-]
 
 [[package]]
 name = "cfb"
@@ -598,30 +610,6 @@ dependencies = [
  "crossbeam-utils",
 ]
 
-[[package]]
-name = "crossbeam-deque"
-version = "0.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
-dependencies = [
- "cfg-if",
- "crossbeam-epoch",
- "crossbeam-utils",
-]
-
-[[package]]
-name = "crossbeam-epoch"
-version = "0.9.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9"
-dependencies = [
- "cfg-if",
- "crossbeam-utils",
- "lazy_static",
- "memoffset",
- "scopeguard",
-]
-
 [[package]]
 name = "crossbeam-utils"
 version = "0.8.7"
@@ -815,7 +803,6 @@ checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
 dependencies = [
  "block-buffer",
  "crypto-common",
- "subtle",
 ]
 
 [[package]]
@@ -1633,15 +1620,6 @@ version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
 
-[[package]]
-name = "jobserver"
-version = "0.1.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa"
-dependencies = [
- "libc",
-]
-
 [[package]]
 name = "js-sys"
 version = "0.3.56"
@@ -2554,31 +2532,6 @@ dependencies = [
  "cty",
 ]
 
-[[package]]
-name = "rayon"
-version = "1.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90"
-dependencies = [
- "autocfg",
- "crossbeam-deque",
- "either",
- "rayon-core",
-]
-
-[[package]]
-name = "rayon-core"
-version = "1.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e"
-dependencies = [
- "crossbeam-channel",
- "crossbeam-deque",
- "crossbeam-utils",
- "lazy_static",
- "num_cpus",
-]
-
 [[package]]
 name = "redox_syscall"
 version = "0.1.57"
@@ -2674,21 +2627,6 @@ dependencies = [
  "windows 0.33.0",
 ]
 
-[[package]]
-name = "ring"
-version = "0.16.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
-dependencies = [
- "cc",
- "libc",
- "once_cell",
- "spin",
- "untrusted",
- "web-sys",
- "winapi",
-]
-
 [[package]]
 name = "rust-argon2"
 version = "0.8.3"
@@ -3025,12 +2963,6 @@ dependencies = [
  "system-deps 5.0.0",
 ]
 
-[[package]]
-name = "spin"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
-
 [[package]]
 name = "stable_deref_trait"
 version = "1.2.0"
@@ -3329,7 +3261,7 @@ name = "tauri-codegen"
 version = "1.0.0-rc.3"
 dependencies = [
  "base64",
- "blake3",
+ "brotli",
  "ico",
  "png 0.17.5",
  "proc-macro2",
@@ -3342,7 +3274,6 @@ dependencies = [
  "thiserror",
  "uuid",
  "walkdir",
- "zstd",
 ]
 
 [[package]]
@@ -3393,17 +3324,17 @@ name = "tauri-utils"
 version = "1.0.0-rc.3"
 dependencies = [
  "aes-gcm",
+ "brotli",
  "ctor",
+ "getrandom 0.2.5",
  "glob",
  "heck 0.4.0",
  "html5ever",
  "json-patch",
  "kuchiki",
- "once_cell",
  "phf 0.10.1",
  "proc-macro2",
  "quote",
- "ring",
  "serde",
  "serde_json",
  "serde_with",
@@ -3411,7 +3342,6 @@ dependencies = [
  "thiserror",
  "url",
  "walkdir",
- "zstd",
 ]
 
 [[package]]
@@ -3655,12 +3585,6 @@ dependencies = [
  "subtle",
 ]
 
-[[package]]
-name = "untrusted"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
-
 [[package]]
 name = "url"
 version = "2.2.2"
@@ -4276,35 +4200,6 @@ dependencies = [
  "crc32fast",
 ]
 
-[[package]]
-name = "zstd"
-version = "0.11.1+zstd.1.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77a16b8414fde0414e90c612eba70985577451c4c504b99885ebed24762cb81a"
-dependencies = [
- "zstd-safe",
-]
-
-[[package]]
-name = "zstd-safe"
-version = "5.0.1+zstd.1.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c12659121420dd6365c5c3de4901f97145b79651fb1d25814020ed2ed0585ae"
-dependencies = [
- "libc",
- "zstd-sys",
-]
-
-[[package]]
-name = "zstd-sys"
-version = "2.0.1+zstd.1.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fd07cbbc53846d9145dbffdf6dd09a7a0aa52be46741825f5c97bdd4f73f12b"
-dependencies = [
- "cc",
- "libc",
-]
-
 [[package]]
 name = "zvariant"
 version = "3.1.2"

+ 1 - 2
tooling/cli/Cargo.lock

@@ -2783,15 +2783,14 @@ version = "1.0.0-rc.3"
 dependencies = [
  "aes-gcm",
  "ctor",
+ "getrandom 0.2.5",
  "glob",
  "heck",
  "html5ever",
  "json-patch",
  "json5",
  "kuchiki",
- "once_cell",
  "phf 0.10.1",
- "ring",
  "schemars",
  "serde",
  "serde_json",

+ 7 - 0
tooling/cli/schema.json

@@ -158,6 +158,7 @@
           "use": "brownfield"
         },
         "security": {
+          "dangerousDisableAssetCspModification": false,
           "freezePrototype": false
         },
         "updater": {
@@ -1355,6 +1356,11 @@
             }
           ]
         },
+        "dangerousDisableAssetCspModification": {
+          "description": "Disables the Tauri-injected CSP sources.\n\nAt compile time, Tauri parses all the frontend assets and changes the Content-Security-Policy to only allow loading of your own scripts and styles by injecting nonce and hash sources. This stricts your CSP, which may introduce issues when using along with other flexing sources.\n\n**WARNING:** Only disable this if you know what you are doing and have properly configured the CSP. Your application might be vulnerable to XSS attacks without this Tauri protection.",
+          "default": false,
+          "type": "boolean"
+        },
         "devCsp": {
           "description": "The Content Security Policy that will be injected on all HTML files on development.\n\nThis is a really important part of the configuration since it helps you ensure your WebView is secured. See <https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP>.",
           "anyOf": [
@@ -1686,6 +1692,7 @@
         "security": {
           "description": "Security configuration.",
           "default": {
+            "dangerousDisableAssetCspModification": false,
             "freezePrototype": false
           },
           "allOf": [