Skip to content

Commit 4c23972

Browse files
fix(core): fix raw invoke body for isolation pattern (#10167)
* fix(core): fix raw invoke body for isolation pattern The `isolation` pattern requests are made using JSON but the payload could be raw bytes, so we send the original `Content-Type` from frontend and make sure to deserialize the payload using that one instead of `Content-Type` from request headers * clippy * disable plist embed in generate_context in tests * change file * docs [skip ci] * move unused_variables [skip ci] * last commit regression [skip ci] * fix test * add example, do not text encode raw request * check type instead of contenttype --------- Co-authored-by: Lucas Nogueira <lucas@tauri.app>
1 parent c01e87a commit 4c23972

28 files changed

Lines changed: 389 additions & 27 deletions

File tree

.changes/generate_context-test.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"tauri-macros": "patch"
3+
"tauri-codegen": "patch"
4+
---
5+
6+
Add support for `test = true` in `generate_context!` macro to skip some code generation that could affect some tests, for now it only skips empedding a plist on macOS.

.changes/isolation-raw-request.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri": "patch:bug"
3+
---
4+
5+
Fix deserialization of raw invoke requests when using `isolation` pattern.
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+
Add `RawIsolationPayload::content_type` method.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ impl CodegenContext {
129129
root: quote::quote!(::tauri),
130130
capabilities: self.capabilities,
131131
assets: None,
132+
test: false,
132133
})?;
133134

134135
// get the full output file path

core/tauri-codegen/src/context.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ pub struct ContextData {
4343
pub capabilities: Option<Vec<PathBuf>>,
4444
/// The custom assets implementation
4545
pub assets: Option<Expr>,
46+
/// Skip runtime-only types generation for tests (e.g. embed-plist usage).
47+
pub test: bool,
4648
}
4749

4850
fn inject_script_hashes(document: &NodeRef, key: &AssetKey, csp_hashes: &mut CspHashes) {
@@ -140,8 +142,12 @@ pub fn context_codegen(data: ContextData) -> EmbeddedAssetsResult<TokenStream> {
140142
root,
141143
capabilities: additional_capabilities,
142144
assets,
145+
test,
143146
} = data;
144147

148+
#[allow(unused_variables)]
149+
let running_tests = test;
150+
145151
let target = std::env::var("TAURI_ENV_TARGET_TRIPLE")
146152
.as_deref()
147153
.map(Target::from_triple)
@@ -291,7 +297,7 @@ pub fn context_codegen(data: ContextData) -> EmbeddedAssetsResult<TokenStream> {
291297
};
292298

293299
#[cfg(target_os = "macos")]
294-
let info_plist = if target == Target::MacOS && dev {
300+
let info_plist = if target == Target::MacOS && dev && !running_tests {
295301
let info_plist_path = config_parent.join("Info.plist");
296302
let mut info_plist = if info_plist_path.exists() {
297303
plist::Value::from_file(&info_plist_path)

core/tauri-macros/src/context.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::path::PathBuf;
88
use syn::{
99
parse::{Parse, ParseBuffer},
1010
punctuated::Punctuated,
11-
Expr, ExprLit, Lit, LitStr, Meta, PathArguments, PathSegment, Token,
11+
Expr, ExprLit, Lit, LitBool, LitStr, Meta, PathArguments, PathSegment, Token,
1212
};
1313
use tauri_codegen::{context_codegen, get_config, ContextData};
1414
use tauri_utils::{config::parse::does_supported_file_name_exist, platform::Target};
@@ -18,6 +18,7 @@ pub(crate) struct ContextItems {
1818
root: syn::Path,
1919
capabilities: Option<Vec<PathBuf>>,
2020
assets: Option<Expr>,
21+
test: bool,
2122
}
2223

2324
impl Parse for ContextItems {
@@ -31,6 +32,7 @@ impl Parse for ContextItems {
3132
let mut root = None;
3233
let mut capabilities = None;
3334
let mut assets = None;
35+
let mut test = false;
3436
let config_file = input.parse::<LitStr>().ok().map(|raw| {
3537
let _ = input.parse::<Token![,]>();
3638
let path = PathBuf::from(raw.value());
@@ -93,6 +95,17 @@ impl Parse for ContextItems {
9395
"assets" => {
9496
assets.replace(v.value);
9597
}
98+
"test" => {
99+
if let Expr::Lit(ExprLit {
100+
lit: Lit::Bool(LitBool { value, .. }),
101+
..
102+
}) = v.value
103+
{
104+
test = value;
105+
} else {
106+
return Err(syn::Error::new(input.span(), "unexpected value for test"));
107+
}
108+
}
96109
name => {
97110
return Err(syn::Error::new(
98111
input.span(),
@@ -105,6 +118,8 @@ impl Parse for ContextItems {
105118
return Err(syn::Error::new(input.span(), "unexpected list input"));
106119
}
107120
}
121+
122+
let _ = input.parse::<Token![,]>();
108123
}
109124

110125
Ok(Self {
@@ -128,6 +143,7 @@ impl Parse for ContextItems {
128143
}),
129144
capabilities,
130145
assets,
146+
test,
131147
})
132148
}
133149
}
@@ -142,6 +158,7 @@ pub(crate) fn generate_context(context: ContextItems) -> TokenStream {
142158
root: context.root.to_token_stream(),
143159
capabilities: context.capabilities,
144160
assets: context.assets,
161+
test: context.test,
145162
})
146163
.and_then(|data| context_codegen(data).map_err(|e| e.to_string()));
147164

core/tauri-plugin/src/build/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ impl<'a> Builder<'a> {
124124
acl::build::generate_docs(
125125
&permissions,
126126
&autogenerated,
127-
&name.strip_prefix("tauri-plugin-").unwrap_or(&name),
127+
name.strip_prefix("tauri-plugin-").unwrap_or(&name),
128128
)?;
129129
}
130130

core/tauri-utils/src/pattern/isolation.js

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,27 @@
3737
* @param {object} data
3838
* @return {Promise<{nonce: number[], payload: number[]}>}
3939
*/
40-
async function encrypt(data) {
40+
async function encrypt(payload) {
4141
const algorithm = Object.create(null)
4242
algorithm.name = 'AES-GCM'
4343
algorithm.iv = window.crypto.getRandomValues(new Uint8Array(12))
4444

45-
const encoder = new TextEncoder()
46-
const encoded = encoder.encode(__RAW_process_ipc_message_fn__(data).data)
45+
const { contentType, data } = __RAW_process_ipc_message_fn__(payload)
46+
47+
const message =
48+
typeof data === 'string'
49+
? new TextEncoder().encode(data)
50+
: ArrayBuffer.isView(data) || data instanceof ArrayBuffer
51+
? data
52+
: new Uint8Array(data)
4753

4854
return window.crypto.subtle
49-
.encrypt(algorithm, aesGcmKey, encoded)
55+
.encrypt(algorithm, aesGcmKey, message)
5056
.then((payload) => {
5157
const result = Object.create(null)
5258
result.nonce = Array.from(new Uint8Array(algorithm.iv))
5359
result.payload = Array.from(new Uint8Array(payload))
60+
result.contentType = contentType
5461
return result
5562
})
5663
}
@@ -66,7 +73,9 @@
6673
const keys = data.payload ? Object.keys(data.payload) : []
6774
return (
6875
keys.length > 0 &&
69-
keys.every((key) => key === 'nonce' || key === 'payload')
76+
keys.every(
77+
(key) => key === 'nonce' || key === 'payload' || key === 'contentType'
78+
)
7079
)
7180
}
7281
return false

core/tauri-utils/src/pattern/isolation.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,14 @@ impl AesGcmPair {
7373
pub fn key(&self) -> &Aes256Gcm {
7474
&self.key
7575
}
76+
77+
#[doc(hidden)]
78+
pub fn encrypt(&self, nonce: &[u8; 12], payload: &[u8]) -> Result<Vec<u8>, Error> {
79+
self
80+
.key
81+
.encrypt(nonce.into(), payload)
82+
.map_err(|_| self::Error::Aes)
83+
}
7684
}
7785

7886
/// All cryptographic keys required for Isolation encryption
@@ -97,7 +105,7 @@ impl Keys {
97105

98106
/// Decrypts a message using the generated keys.
99107
pub fn decrypt(&self, raw: RawIsolationPayload<'_>) -> Result<Vec<u8>, Error> {
100-
let RawIsolationPayload { nonce, payload } = raw;
108+
let RawIsolationPayload { nonce, payload, .. } = raw;
101109
let nonce: [u8; 12] = nonce.as_ref().try_into()?;
102110
self
103111
.aes_gcm
@@ -109,9 +117,18 @@ impl Keys {
109117

110118
/// Raw representation of
111119
#[derive(Debug, serde::Deserialize)]
120+
#[serde(rename_all = "camelCase")]
112121
pub struct RawIsolationPayload<'a> {
113122
nonce: Cow<'a, [u8]>,
114123
payload: Cow<'a, [u8]>,
124+
content_type: Cow<'a, str>,
125+
}
126+
127+
impl<'a> RawIsolationPayload<'a> {
128+
/// Content type of this payload.
129+
pub fn content_type(&self) -> &Cow<'a, str> {
130+
&self.content_type
131+
}
115132
}
116133

117134
impl<'a> TryFrom<&'a Vec<u8>> for RawIsolationPayload<'a> {

core/tauri/scripts/ipc.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@
3939
const keys = Object.keys(event.data.payload || {})
4040
return (
4141
keys.length > 0 &&
42-
keys.every((key) => key === 'nonce' || key === 'payload')
42+
keys.every(
43+
(key) => key === 'contentType' || key === 'nonce' || key === 'payload'
44+
)
4345
)
4446
}
4547
return false

0 commit comments

Comments
 (0)