Skip to content

Commit 7b0d4e7

Browse files
authored
fix(core): SHA256 hash for JS scripts CSP on Windows (#14265)
* fix(core): SHA256 hash for JS scripts CSP on Windows we hash JS scripts as SHA256 for the Content-Security-Policy (CSP) header. The isolation pattern is broken on Windows due to the hash including carriage return characters, which are not processed when the webview checks the script hash to see if the CSP allows the script. * fmt, clippy
1 parent c5008b8 commit 7b0d4e7

5 files changed

Lines changed: 61 additions & 5 deletions

File tree

‎.changes/fix-csp-windows.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri-codegen": patch:bug
3+
---
4+
5+
Fix JavaScript SHA256 hash generation on Windows not ignoring carriage return characters.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri-utils": patch:feat
3+
---
4+
5+
Added `html::normalize_script_for_csp`.

‎crates/tauri-codegen/src/context.rs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ fn inject_script_hashes(document: &NodeRef, key: &AssetKey, csp_hashes: &mut Csp
5050
for inline_script_el in inline_script_elements {
5151
let script = inline_script_el.as_node().text_contents();
5252
let mut hasher = Sha256::new();
53-
hasher.update(&script);
53+
hasher.update(tauri_utils::html::normalize_script_for_csp(
54+
script.as_bytes(),
55+
));
5456
let hash = hasher.finalize();
5557
scripts.push(format!(
5658
"'sha256-{}'",

‎crates/tauri-codegen/src/embedded_assets.rs‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,10 +180,12 @@ impl CspHashes {
180180
if dangerous_disable_asset_csp_modification.can_modify("script-src") {
181181
let mut hasher = Sha256::new();
182182
hasher.update(
183-
&std::fs::read(path).map_err(|error| EmbeddedAssetsError::AssetRead {
184-
path: path.to_path_buf(),
185-
error,
186-
})?,
183+
&std::fs::read(path)
184+
.map(|b| tauri_utils::html::normalize_script_for_csp(&b))
185+
.map_err(|error| EmbeddedAssetsError::AssetRead {
186+
path: path.to_path_buf(),
187+
error,
188+
})?,
187189
);
188190
let hash = hasher.finalize();
189191
self.scripts.push(format!(

‎crates/tauri-utils/src/html.rs‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,38 @@ pub fn inline_isolation(document: &NodeRef, dir: &Path) {
286286
}
287287
}
288288

289+
/// Normalize line endings in script content to match what the browser uses for CSP hashing.
290+
///
291+
/// According to the HTML spec, browsers normalize:
292+
/// - `\r\n` → `\n`
293+
/// - `\r` → `\n`
294+
pub fn normalize_script_for_csp(input: &[u8]) -> Vec<u8> {
295+
let mut output = Vec::with_capacity(input.len());
296+
297+
let mut i = 0;
298+
while i < input.len() {
299+
match input[i] {
300+
b'\r' => {
301+
if i + 1 < input.len() && input[i + 1] == b'\n' {
302+
// CRLF → LF
303+
output.push(b'\n');
304+
i += 2;
305+
} else {
306+
// Lone CR → LF
307+
output.push(b'\n');
308+
i += 1;
309+
}
310+
}
311+
_ => {
312+
output.push(input[i]);
313+
i += 1;
314+
}
315+
}
316+
}
317+
318+
output
319+
}
320+
289321
#[cfg(test)]
290322
mod tests {
291323

@@ -307,4 +339,14 @@ mod tests {
307339
);
308340
}
309341
}
342+
343+
#[test]
344+
fn normalize_script_for_csp() {
345+
let js = "// Copyright 2019-2024 Tauri Programme within The Commons Conservancy\r// SPDX-License-Identifier: Apache-2.0\n// SPDX-License-Identifier: MIT\r\n\r\nwindow.__TAURI_ISOLATION_HOOK__ = (payload, options) => {\r\n return payload\r\n}\r\n";
346+
let expected = "// Copyright 2019-2024 Tauri Programme within The Commons Conservancy\n// SPDX-License-Identifier: Apache-2.0\n// SPDX-License-Identifier: MIT\n\nwindow.__TAURI_ISOLATION_HOOK__ = (payload, options) => {\n return payload\n}\n";
347+
assert_eq!(
348+
super::normalize_script_for_csp(js.as_bytes()),
349+
expected.as_bytes()
350+
)
351+
}
310352
}

0 commit comments

Comments
 (0)