Jelajahi Sumber

integration tests related to recent security advisory (#9983)

* test if an uninitialized iframe can call the ipc

* test that calls without __TAURI_INVOKE_KEY__ fail

* fix cargo manifest newline change

* add comment to help unknown error message changes

* license headers

* add identifier and icons to config

* fix clippy error

* Create change-pr-9983.md

* add tag to changefile

* rename changefile
chip 1 tahun lalu
induk
melakukan
51d043209b

+ 6 - 0
.changes/ipc-invoke-key-integration-tests.md

@@ -0,0 +1,6 @@
+---
+"tauri": patch:enhance
+---
+
+tracing for ipc invoke key errors, integration tests related
+to [recent security advisory](https://github.com/tauri-apps/tauri/security/advisories/GHSA-57fm-592m-34r7)

+ 17 - 0
Cargo.lock

@@ -1921,6 +1921,15 @@ dependencies = [
  "cfg-if",
 ]
 
+[[package]]
+name = "invoke-key"
+version = "0.1.0"
+dependencies = [
+ "tauri",
+ "tauri-build",
+ "tracing",
+]
+
 [[package]]
 name = "io-lifetimes"
 version = "1.0.11"
@@ -4670,6 +4679,14 @@ version = "1.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
 
+[[package]]
+name = "uninitialized-ipc"
+version = "0.1.0"
+dependencies = [
+ "tauri",
+ "tauri-build",
+]
+
 [[package]]
 name = "universal-hash"
 version = "0.5.1"

+ 2 - 0
Cargo.toml

@@ -14,6 +14,8 @@ members = [
   # integration tests
   "core/tests/restart",
   "core/tests/app-updater",
+  "core/tests/uninitialized-ipc",
+  "core/tests/invoke-key"
 ]
 
 exclude = [

+ 6 - 2
core/tauri/src/window.rs

@@ -1569,11 +1569,15 @@ impl<R: Runtime> Window<R> {
         return Err(Error::InvokeKey);
       }
       None => {
+        // this specific string is tested against in `core/tests/invoke-key`.
+        // if this error changes then that integration test should be updated accordingly.
+        let error = "received ipc message without a __TAURI_INVOKE_KEY__";
+
         #[cfg(feature = "tracing")]
-        tracing::error!("received ipc message without a __TAURI_INVOKE_KEY__");
+        tracing::error!(error);
 
         #[cfg(not(feature = "tracing"))]
-        eprintln!("received ipc message without a __TAURI_INVOKE_KEY__");
+        eprintln!("{error}");
 
         return Err(Error::InvokeKey);
       }

+ 15 - 0
core/tests/invoke-key/Cargo.toml

@@ -0,0 +1,15 @@
+[package]
+name = "invoke-key"
+version = "0.1.0"
+edition = "2021"
+
+[build-dependencies]
+tauri-build = { path = "../../tauri-build", features = [] }
+
+[dependencies]
+tauri = { path = "../../tauri", features = ["tracing"] }
+tracing = "0.1"
+
+[features]
+default = ["custom-protocol"]
+custom-protocol = ["tauri/custom-protocol"]

+ 7 - 0
core/tests/invoke-key/build.rs

@@ -0,0 +1,7 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+fn main() {
+  tauri_build::build()
+}

+ 7 - 0
core/tests/invoke-key/index.html

@@ -0,0 +1,7 @@
+<script>
+  window.__TAURI_POST_MESSAGE__({
+    cmd: 'error_if_called',
+    callback: 0,
+    error: 0
+  })
+</script>

+ 24 - 0
core/tests/invoke-key/src/main.rs

@@ -0,0 +1,24 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
+
+use tauri::{command, generate_context, generate_handler, Builder};
+
+mod subscriber;
+
+#[command]
+fn error_if_called() {
+  std::process::exit(1)
+}
+
+fn main() {
+  tracing::subscriber::set_global_default(subscriber::InvokeKeyErrorSubscriber)
+    .expect("unable to set tracing global subscriber");
+
+  Builder::default()
+    .invoke_handler(generate_handler![error_if_called])
+    .run(generate_context!())
+    .expect("error while running tauri application");
+}

+ 48 - 0
core/tests/invoke-key/src/subscriber.rs

@@ -0,0 +1,48 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+use std::fmt::Debug;
+
+use tracing::{
+  field::{Field, Visit},
+  span::{Attributes, Record},
+  Event, Id, Level, Metadata, Subscriber,
+};
+
+pub struct InvokeKeyErrorSubscriber;
+
+impl Subscriber for InvokeKeyErrorSubscriber {
+  fn enabled(&self, metadata: &Metadata<'_>) -> bool {
+    metadata.is_event() && *metadata.level() == Level::ERROR
+  }
+
+  fn new_span(&self, _: &Attributes<'_>) -> Id {
+    // shouldn't be called because we only enable events
+    unimplemented!()
+  }
+
+  fn record(&self, _: &Id, _: &Record<'_>) {}
+
+  fn record_follows_from(&self, _: &Id, _: &Id) {}
+
+  fn event(&self, event: &Event<'_>) {
+    event.record(&mut InvokeKeyExitVisit)
+  }
+
+  fn enter(&self, _: &Id) {}
+
+  fn exit(&self, _: &Id) {}
+}
+
+struct InvokeKeyExitVisit;
+
+impl Visit for InvokeKeyExitVisit {
+  fn record_str(&mut self, field: &Field, value: &str) {
+    if field.name() == "error" && value == "received ipc message without a __TAURI_INVOKE_KEY__" {
+      std::process::exit(0)
+    }
+  }
+
+  fn record_debug(&mut self, _: &Field, _: &dyn Debug) {}
+}

+ 22 - 0
core/tests/invoke-key/tauri.conf.json

@@ -0,0 +1,22 @@
+{
+  "$schema": "../../../core/tauri-config-schema/schema.json",
+  "build": {
+    "distDir": "index.html",
+    "devPath": "index.html"
+  },
+  "tauri": {
+    "bundle": {
+      "identifier": "app.tauri.tests.invoke-key",
+      "icon": [
+        "../../../examples/.icons/32x32.png",
+        "../../../examples/.icons/128x128.png",
+        "../../../examples/.icons/128x128@2x.png",
+        "../../../examples/.icons/icon.icns",
+        "../../../examples/.icons/icon.ico"
+      ]
+    },
+    "windows": [
+      {}
+    ]
+  }
+}

+ 14 - 0
core/tests/uninitialized-ipc/Cargo.toml

@@ -0,0 +1,14 @@
+[package]
+name = "uninitialized-ipc"
+version = "0.1.0"
+edition = "2021"
+
+[build-dependencies]
+tauri-build = { path = "../../tauri-build", features = [] }
+
+[dependencies]
+tauri = { path = "../../tauri", features = [] }
+
+[features]
+default = ["custom-protocol"]
+custom-protocol = ["tauri/custom-protocol"]

+ 7 - 0
core/tests/uninitialized-ipc/build.rs

@@ -0,0 +1,7 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+fn main() {
+  tauri_build::build()
+}

+ 3 - 0
core/tests/uninitialized-ipc/iframe.html

@@ -0,0 +1,3 @@
+<script>
+  window.parent.postMessage(typeof window.ipc?.postMessage, '*')
+</script>

+ 17 - 0
core/tests/uninitialized-ipc/index.html

@@ -0,0 +1,17 @@
+<script>
+  const exit = (code) => window.__TAURI_INVOKE__('exit', { code })
+  window.addEventListener('message', ({ data }) => {
+    console.log(data)
+    switch (data) {
+      case 'undefined':
+        exit(0)
+        break
+      case 'function':
+        exit(1)
+        break
+      default:
+        exit(2)
+    }
+  })
+</script>
+<iframe src="uninitialized://localhost/"></iframe>

+ 24 - 0
core/tests/uninitialized-ipc/src/main.rs

@@ -0,0 +1,24 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
+
+use tauri::{command, generate_context, generate_handler, http::Response, Builder};
+
+const IFRAME: &[u8] = include_bytes!("../iframe.html");
+
+#[command]
+fn exit(code: i32) {
+  std::process::exit(code)
+}
+
+fn main() {
+  Builder::default()
+    .invoke_handler(generate_handler![exit])
+    .register_uri_scheme_protocol("uninitialized", |_app, _request| {
+      Ok(Response::new(IFRAME.into()))
+    })
+    .run(generate_context!())
+    .expect("error while running tauri application");
+}

+ 22 - 0
core/tests/uninitialized-ipc/tauri.conf.json

@@ -0,0 +1,22 @@
+{
+  "$schema": "../../../core/tauri-config-schema/schema.json",
+  "build": {
+    "distDir": "index.html",
+    "devPath": "index.html"
+  },
+  "tauri": {
+    "bundle": {
+      "identifier": "app.tauri.tests.uninitialized-ipc",
+      "icon": [
+        "../../../examples/.icons/32x32.png",
+        "../../../examples/.icons/128x128.png",
+        "../../../examples/.icons/128x128@2x.png",
+        "../../../examples/.icons/icon.icns",
+        "../../../examples/.icons/icon.ico"
+      ]
+    },
+    "windows": [
+      {}
+    ]
+  }
+}