mirror of
https://github.com/tauri-apps/tauri.git
synced 2025-01-04 09:08:38 +03:00
feat(core): inject invoke key on <script type="module">
(#2120)
This commit is contained in:
parent
94a5848afb
commit
f03eea9c9b
7
.changes/inject-invoke-key-module-script.md
Normal file
7
.changes/inject-invoke-key-module-script.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"tauri": patch
|
||||||
|
"tauri-codegen": patch
|
||||||
|
"tauri-utils": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Inject invoke key on `script` tags with `type="module"`.
|
@ -21,3 +21,4 @@ tauri-utils = { version = "1.0.0-beta.1", path = "../tauri-utils", features = [
|
|||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
walkdir = "2"
|
walkdir = "2"
|
||||||
zstd = "0.9"
|
zstd = "0.9"
|
||||||
|
kuchiki = "0.8"
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
use kuchiki::traits::*;
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{quote, ToTokens, TokenStreamExt};
|
use quote::{quote, ToTokens, TokenStreamExt};
|
||||||
use std::{
|
use std::{
|
||||||
@ -10,7 +11,10 @@ use std::{
|
|||||||
fs::File,
|
fs::File,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
use tauri_utils::{assets::AssetKey, html::inject_csp};
|
use tauri_utils::{
|
||||||
|
assets::AssetKey,
|
||||||
|
html::{inject_csp, inject_invoke_key_token},
|
||||||
|
};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
@ -170,44 +174,46 @@ impl EmbeddedAssets {
|
|||||||
path: path.to_owned(),
|
path: path.to_owned(),
|
||||||
error,
|
error,
|
||||||
})?;
|
})?;
|
||||||
if let Some(csp) = &options.csp {
|
if path.extension() == Some(OsStr::new("html")) {
|
||||||
if path.extension() == Some(OsStr::new("html")) {
|
let mut document = kuchiki::parse_html().one(String::from_utf8_lossy(&input).into_owned());
|
||||||
input = inject_csp(String::from_utf8_lossy(&input).into_owned(), csp)
|
if let Some(csp) = &options.csp {
|
||||||
.as_bytes()
|
inject_csp(&mut document, csp);
|
||||||
.to_vec();
|
}
|
||||||
|
inject_invoke_key_token(&mut document);
|
||||||
|
input = document.to_string().as_bytes().to_vec();
|
||||||
|
} else {
|
||||||
|
let is_javascript = ["js", "cjs", "mjs"]
|
||||||
|
.iter()
|
||||||
|
.any(|e| path.extension() == Some(OsStr::new(e)));
|
||||||
|
if is_javascript {
|
||||||
|
let js = String::from_utf8_lossy(&input).into_owned();
|
||||||
|
input = if [
|
||||||
|
"import{", "import*", "import ", "export{", "export*", "export ",
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.any(|t| js.contains(t))
|
||||||
|
{
|
||||||
|
format!(
|
||||||
|
r#"
|
||||||
|
const __TAURI_INVOKE_KEY__ = __TAURI__INVOKE_KEY_TOKEN__;
|
||||||
|
{}
|
||||||
|
"#,
|
||||||
|
js
|
||||||
|
)
|
||||||
|
.as_bytes()
|
||||||
|
.to_vec()
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
r#"(function () {{
|
||||||
|
const __TAURI_INVOKE_KEY__ = __TAURI__INVOKE_KEY_TOKEN__;
|
||||||
|
{}
|
||||||
|
}})()"#,
|
||||||
|
js
|
||||||
|
)
|
||||||
|
.as_bytes()
|
||||||
|
.to_vec()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
let is_javascript = ["js", "cjs", "mjs"]
|
|
||||||
.iter()
|
|
||||||
.any(|e| path.extension() == Some(OsStr::new(e)));
|
|
||||||
if is_javascript {
|
|
||||||
let js = String::from_utf8_lossy(&input).into_owned();
|
|
||||||
input = if [
|
|
||||||
"import{", "import*", "import ", "export{", "export*", "export ",
|
|
||||||
]
|
|
||||||
.iter()
|
|
||||||
.any(|t| js.contains(t))
|
|
||||||
{
|
|
||||||
format!(
|
|
||||||
r#"
|
|
||||||
const __TAURI_INVOKE_KEY__ = __TAURI__INVOKE_KEY_TOKEN__;
|
|
||||||
{}
|
|
||||||
"#,
|
|
||||||
js
|
|
||||||
)
|
|
||||||
.as_bytes()
|
|
||||||
.to_vec()
|
|
||||||
} else {
|
|
||||||
format!(
|
|
||||||
r#"(function () {{
|
|
||||||
const __TAURI_INVOKE_KEY__ = __TAURI__INVOKE_KEY_TOKEN__;
|
|
||||||
{}
|
|
||||||
}})()"#,
|
|
||||||
js
|
|
||||||
)
|
|
||||||
.as_bytes()
|
|
||||||
.to_vec()
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// we must canonicalize the base of our paths to allow long paths on windows
|
// we must canonicalize the base of our paths to allow long paths on windows
|
||||||
|
@ -2,17 +2,55 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
use html5ever::{
|
use html5ever::{interface::QualName, namespace_url, ns, LocalName};
|
||||||
interface::QualName,
|
use kuchiki::{Attribute, ExpandedName, NodeRef};
|
||||||
namespace_url, ns,
|
|
||||||
tendril::{fmt::UTF8, NonAtomic, Tendril},
|
/// Injects the invoke key token to each script on the document.
|
||||||
LocalName,
|
///
|
||||||
};
|
/// The invoke key token is replaced at runtime with the actual invoke key value.
|
||||||
use kuchiki::{traits::*, Attribute, ExpandedName, NodeRef};
|
pub fn inject_invoke_key_token(document: &mut NodeRef) {
|
||||||
|
let mut targets = vec![];
|
||||||
|
if let Ok(scripts) = document.select("script") {
|
||||||
|
for target in scripts {
|
||||||
|
targets.push(target);
|
||||||
|
}
|
||||||
|
for target in targets {
|
||||||
|
let node = target.as_node();
|
||||||
|
let element = node.as_element().unwrap();
|
||||||
|
|
||||||
|
let attrs = element.attributes.borrow();
|
||||||
|
// if the script is external (has `src`) or its type is not "module", we won't inject the token
|
||||||
|
if attrs.get("src").is_some() || attrs.get("type") != Some("module") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let replacement_node = NodeRef::new_element(
|
||||||
|
QualName::new(None, ns!(html), "script".into()),
|
||||||
|
element
|
||||||
|
.attributes
|
||||||
|
.borrow()
|
||||||
|
.clone()
|
||||||
|
.map
|
||||||
|
.into_iter()
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
|
let script = node.text_contents();
|
||||||
|
replacement_node.append(NodeRef::new_text(format!(
|
||||||
|
r#"
|
||||||
|
const __TAURI_INVOKE_KEY__ = __TAURI__INVOKE_KEY_TOKEN__;
|
||||||
|
{}
|
||||||
|
"#,
|
||||||
|
script
|
||||||
|
)));
|
||||||
|
|
||||||
|
node.insert_after(replacement_node);
|
||||||
|
node.detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Injects a content security policy to the HTML.
|
/// Injects a content security policy to the HTML.
|
||||||
pub fn inject_csp<H: Into<Tendril<UTF8, NonAtomic>>>(html: H, csp: &str) -> String {
|
pub fn inject_csp(document: &mut NodeRef, csp: &str) {
|
||||||
let document = kuchiki::parse_html().one(html);
|
|
||||||
if let Ok(ref head) = document.select_first("head") {
|
if let Ok(ref head) = document.select_first("head") {
|
||||||
head.as_node().append(create_csp_meta_tag(csp));
|
head.as_node().append(create_csp_meta_tag(csp));
|
||||||
} else {
|
} else {
|
||||||
@ -23,7 +61,6 @@ pub fn inject_csp<H: Into<Tendril<UTF8, NonAtomic>>>(html: H, csp: &str) -> Stri
|
|||||||
head.append(create_csp_meta_tag(csp));
|
head.append(create_csp_meta_tag(csp));
|
||||||
document.prepend(head);
|
document.prepend(head);
|
||||||
}
|
}
|
||||||
document.to_string()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_csp_meta_tag(csp: &str) -> NodeRef {
|
fn create_csp_meta_tag(csp: &str) -> NodeRef {
|
||||||
@ -50,6 +87,7 @@ fn create_csp_meta_tag(csp: &str) -> NodeRef {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use kuchiki::traits::*;
|
||||||
#[test]
|
#[test]
|
||||||
fn csp() {
|
fn csp() {
|
||||||
let htmls = vec![
|
let htmls = vec![
|
||||||
@ -57,10 +95,11 @@ mod tests {
|
|||||||
"<html></html>".to_string(),
|
"<html></html>".to_string(),
|
||||||
];
|
];
|
||||||
for html in htmls {
|
for html in htmls {
|
||||||
|
let mut document = kuchiki::parse_html().one(html);
|
||||||
let csp = "default-src 'self'; img-src https://*; child-src 'none';";
|
let csp = "default-src 'self'; img-src https://*; child-src 'none';";
|
||||||
let new = super::inject_csp(html, csp);
|
super::inject_csp(&mut document, csp);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
new,
|
document.to_string(),
|
||||||
format!(
|
format!(
|
||||||
r#"<html><head><meta content="{}" http-equiv="Content-Security-Policy"></head><body></body></html>"#,
|
r#"<html><head><meta content="{}" http-equiv="Content-Security-Policy"></head><body></body></html>"#,
|
||||||
csp
|
csp
|
||||||
|
@ -459,6 +459,7 @@ impl<P: Params> WindowManager<P> {
|
|||||||
};
|
};
|
||||||
let is_javascript =
|
let is_javascript =
|
||||||
path.ends_with(".js") || path.ends_with(".cjs") || path.ends_with(".mjs");
|
path.ends_with(".js") || path.ends_with(".cjs") || path.ends_with(".mjs");
|
||||||
|
let is_html = path.ends_with(".html");
|
||||||
|
|
||||||
let asset_response = assets
|
let asset_response = assets
|
||||||
.get(&path)
|
.get(&path)
|
||||||
@ -471,16 +472,17 @@ impl<P: Params> WindowManager<P> {
|
|||||||
.map(Cow::into_owned);
|
.map(Cow::into_owned);
|
||||||
match asset_response {
|
match asset_response {
|
||||||
Ok(asset) => {
|
Ok(asset) => {
|
||||||
if is_javascript {
|
if is_javascript || is_html {
|
||||||
let js = String::from_utf8_lossy(&asset).into_owned();
|
let contents = String::from_utf8_lossy(&asset).into_owned();
|
||||||
Ok(
|
Ok(
|
||||||
js.replacen(
|
contents
|
||||||
"__TAURI__INVOKE_KEY_TOKEN__",
|
.replacen(
|
||||||
&manager.generate_invoke_key().to_string(),
|
"__TAURI__INVOKE_KEY_TOKEN__",
|
||||||
1,
|
&manager.generate_invoke_key().to_string(),
|
||||||
)
|
1,
|
||||||
.as_bytes()
|
)
|
||||||
.to_vec(),
|
.as_bytes()
|
||||||
|
.to_vec(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Ok(asset)
|
Ok(asset)
|
||||||
|
Loading…
Reference in New Issue
Block a user