Skip to content

Commit

Permalink
fix(core): fix raw invoke body for isolation pattern (#10167)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
amrbashir and lucasfernog authored Jul 12, 2024
1 parent c01e87a commit 4c23972
Show file tree
Hide file tree
Showing 28 changed files with 389 additions and 27 deletions.
6 changes: 6 additions & 0 deletions .changes/generate_context-test.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"tauri-macros": "patch"
"tauri-codegen": "patch"
---

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.
5 changes: 5 additions & 0 deletions .changes/isolation-raw-request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tauri": "patch:bug"
---

Fix deserialization of raw invoke requests when using `isolation` pattern.
5 changes: 5 additions & 0 deletions .changes/utils-raw-isolation-payload.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tauri-utils": "patch:feat"
---

Add `RawIsolationPayload::content_type` method.
1 change: 1 addition & 0 deletions core/tauri-build/src/codegen/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ impl CodegenContext {
root: quote::quote!(::tauri),
capabilities: self.capabilities,
assets: None,
test: false,
})?;

// get the full output file path
Expand Down
8 changes: 7 additions & 1 deletion core/tauri-codegen/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ pub struct ContextData {
pub capabilities: Option<Vec<PathBuf>>,
/// The custom assets implementation
pub assets: Option<Expr>,
/// Skip runtime-only types generation for tests (e.g. embed-plist usage).
pub test: bool,
}

fn inject_script_hashes(document: &NodeRef, key: &AssetKey, csp_hashes: &mut CspHashes) {
Expand Down Expand Up @@ -140,8 +142,12 @@ pub fn context_codegen(data: ContextData) -> EmbeddedAssetsResult<TokenStream> {
root,
capabilities: additional_capabilities,
assets,
test,
} = data;

#[allow(unused_variables)]
let running_tests = test;

let target = std::env::var("TAURI_ENV_TARGET_TRIPLE")
.as_deref()
.map(Target::from_triple)
Expand Down Expand Up @@ -291,7 +297,7 @@ pub fn context_codegen(data: ContextData) -> EmbeddedAssetsResult<TokenStream> {
};

#[cfg(target_os = "macos")]
let info_plist = if target == Target::MacOS && dev {
let info_plist = if target == Target::MacOS && dev && !running_tests {
let info_plist_path = config_parent.join("Info.plist");
let mut info_plist = if info_plist_path.exists() {
plist::Value::from_file(&info_plist_path)
Expand Down
19 changes: 18 additions & 1 deletion core/tauri-macros/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::path::PathBuf;
use syn::{
parse::{Parse, ParseBuffer},
punctuated::Punctuated,
Expr, ExprLit, Lit, LitStr, Meta, PathArguments, PathSegment, Token,
Expr, ExprLit, Lit, LitBool, LitStr, Meta, PathArguments, PathSegment, Token,
};
use tauri_codegen::{context_codegen, get_config, ContextData};
use tauri_utils::{config::parse::does_supported_file_name_exist, platform::Target};
Expand All @@ -18,6 +18,7 @@ pub(crate) struct ContextItems {
root: syn::Path,
capabilities: Option<Vec<PathBuf>>,
assets: Option<Expr>,
test: bool,
}

impl Parse for ContextItems {
Expand All @@ -31,6 +32,7 @@ impl Parse for ContextItems {
let mut root = None;
let mut capabilities = None;
let mut assets = None;
let mut test = false;
let config_file = input.parse::<LitStr>().ok().map(|raw| {
let _ = input.parse::<Token![,]>();
let path = PathBuf::from(raw.value());
Expand Down Expand Up @@ -93,6 +95,17 @@ impl Parse for ContextItems {
"assets" => {
assets.replace(v.value);
}
"test" => {
if let Expr::Lit(ExprLit {
lit: Lit::Bool(LitBool { value, .. }),
..
}) = v.value
{
test = value;
} else {
return Err(syn::Error::new(input.span(), "unexpected value for test"));
}
}
name => {
return Err(syn::Error::new(
input.span(),
Expand All @@ -105,6 +118,8 @@ impl Parse for ContextItems {
return Err(syn::Error::new(input.span(), "unexpected list input"));
}
}

let _ = input.parse::<Token![,]>();
}

Ok(Self {
Expand All @@ -128,6 +143,7 @@ impl Parse for ContextItems {
}),
capabilities,
assets,
test,
})
}
}
Expand All @@ -142,6 +158,7 @@ pub(crate) fn generate_context(context: ContextItems) -> TokenStream {
root: context.root.to_token_stream(),
capabilities: context.capabilities,
assets: context.assets,
test: context.test,
})
.and_then(|data| context_codegen(data).map_err(|e| e.to_string()));

Expand Down
2 changes: 1 addition & 1 deletion core/tauri-plugin/src/build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ impl<'a> Builder<'a> {
acl::build::generate_docs(
&permissions,
&autogenerated,
&name.strip_prefix("tauri-plugin-").unwrap_or(&name),
name.strip_prefix("tauri-plugin-").unwrap_or(&name),
)?;
}

Expand Down
19 changes: 14 additions & 5 deletions core/tauri-utils/src/pattern/isolation.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,27 @@
* @param {object} data
* @return {Promise<{nonce: number[], payload: number[]}>}
*/
async function encrypt(data) {
async function encrypt(payload) {
const algorithm = Object.create(null)
algorithm.name = 'AES-GCM'
algorithm.iv = window.crypto.getRandomValues(new Uint8Array(12))

const encoder = new TextEncoder()
const encoded = encoder.encode(__RAW_process_ipc_message_fn__(data).data)
const { contentType, data } = __RAW_process_ipc_message_fn__(payload)

const message =
typeof data === 'string'
? new TextEncoder().encode(data)
: ArrayBuffer.isView(data) || data instanceof ArrayBuffer
? data
: new Uint8Array(data)

return window.crypto.subtle
.encrypt(algorithm, aesGcmKey, encoded)
.encrypt(algorithm, aesGcmKey, message)
.then((payload) => {
const result = Object.create(null)
result.nonce = Array.from(new Uint8Array(algorithm.iv))
result.payload = Array.from(new Uint8Array(payload))
result.contentType = contentType
return result
})
}
Expand All @@ -66,7 +73,9 @@
const keys = data.payload ? Object.keys(data.payload) : []
return (
keys.length > 0 &&
keys.every((key) => key === 'nonce' || key === 'payload')
keys.every(
(key) => key === 'nonce' || key === 'payload' || key === 'contentType'
)
)
}
return false
Expand Down
19 changes: 18 additions & 1 deletion core/tauri-utils/src/pattern/isolation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ impl AesGcmPair {
pub fn key(&self) -> &Aes256Gcm {
&self.key
}

#[doc(hidden)]
pub fn encrypt(&self, nonce: &[u8; 12], payload: &[u8]) -> Result<Vec<u8>, Error> {
self
.key
.encrypt(nonce.into(), payload)
.map_err(|_| self::Error::Aes)
}
}

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

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

/// Raw representation of
#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RawIsolationPayload<'a> {
nonce: Cow<'a, [u8]>,
payload: Cow<'a, [u8]>,
content_type: Cow<'a, str>,
}

impl<'a> RawIsolationPayload<'a> {
/// Content type of this payload.
pub fn content_type(&self) -> &Cow<'a, str> {
&self.content_type
}
}

impl<'a> TryFrom<&'a Vec<u8>> for RawIsolationPayload<'a> {
Expand Down
4 changes: 3 additions & 1 deletion core/tauri/scripts/ipc.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@
const keys = Object.keys(event.data.payload || {})
return (
keys.length > 0 &&
keys.every((key) => key === 'nonce' || key === 'payload')
keys.every(
(key) => key === 'contentType' || key === 'nonce' || key === 'payload'
)
)
}
return false
Expand Down
1 change: 1 addition & 0 deletions core/tauri/src/ipc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub type OwnedInvokeResponder<R> =

/// Possible values of an IPC payload.
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(PartialEq))]
pub enum InvokeBody {
/// Json payload.
Json(JsonValue),
Expand Down
Loading

0 comments on commit 4c23972

Please sign in to comment.