Skip to content

Commit

Permalink
feat(core): add dangerous option to disable compile time CSP injection (
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasfernog committed Mar 28, 2022
1 parent e05d718 commit f6e32ee
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 167 deletions.
7 changes: 7 additions & 0 deletions .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.
35 changes: 20 additions & 15 deletions core/tauri-codegen/src/context.rs
Expand Up @@ -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();
Expand All @@ -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)));
}
}
}

Expand Down Expand Up @@ -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!(),
},
Expand All @@ -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!(),
Expand Down Expand Up @@ -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 {
Expand Down
22 changes: 18 additions & 4 deletions core/tauri-codegen/src/embedded_assets.rs
Expand Up @@ -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
Expand Down Expand Up @@ -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)))
Expand Down Expand Up @@ -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,
}
Expand All @@ -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;
Expand All @@ -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 {
Expand All @@ -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,
Expand Down
21 changes: 20 additions & 1 deletion core/tauri-utils/src/config.rs
Expand Up @@ -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.
Expand Down Expand Up @@ -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
);
}
}

Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit f6e32ee

Please sign in to comment.