Skip to content

Commit ee71c31

Browse files
authored
feat(core): allow configuring remote domains with IPC access, closes #5088 (#5918)
1 parent 6ff801e commit ee71c31

File tree

19 files changed

+794
-98
lines changed

19 files changed

+794
-98
lines changed

.changes/remote-urls.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"tauri": patch
3+
"tauri-utils": patch
4+
---
5+
6+
Added configuration to specify remote URLs allowed to access the IPC.

core/tauri-build/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
339339

340340
if target_triple.contains("darwin") {
341341
if let Some(version) = &config.tauri.bundle.macos.minimum_system_version {
342-
println!("cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET={}", version);
342+
println!("cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET={version}");
343343
}
344344
}
345345

core/tauri-build/src/static_vcruntime.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,5 @@ fn override_msvcrt_lib() {
5454
f.write_all(bytes).unwrap();
5555
}
5656
// Add the output directory to the native library path.
57-
println!("cargo:rustc-link-search=native={}", out_dir);
57+
println!("cargo:rustc-link-search=native={out_dir}");
5858
}

core/tauri-config-schema/schema.json

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@
163163
},
164164
"security": {
165165
"dangerousDisableAssetCspModification": false,
166+
"dangerousRemoteDomainIpcAccess": [],
166167
"freezePrototype": false
167168
},
168169
"updater": {
@@ -415,6 +416,7 @@
415416
"description": "Security configuration.",
416417
"default": {
417418
"dangerousDisableAssetCspModification": false,
419+
"dangerousRemoteDomainIpcAccess": [],
418420
"freezePrototype": false
419421
},
420422
"allOf": [
@@ -2603,6 +2605,14 @@
26032605
"$ref": "#/definitions/DisabledCspModificationKind"
26042606
}
26052607
]
2608+
},
2609+
"dangerousRemoteDomainIpcAccess": {
2610+
"description": "Allow external domains to send command to Tauri.\n\nBy default, external domains do not have access to `window.__TAURI__`, which means they cannot communicate with the commands defined in Rust. This prevents attacks where an externally loaded malicious or compromised sites could start executing commands on the user's device.\n\nThis configuration allows a set of external domains to have access to the Tauri commands. When you configure a domain to be allowed to access the IPC, all subpaths are allowed. Subdomains are not allowed.\n\n**WARNING:** Only use this option if you either have internal checks against malicious external sites or you can trust the allowed external sites. You application might be vulnerable to dangerous Tauri command related attacks otherwise.",
2611+
"default": [],
2612+
"type": "array",
2613+
"items": {
2614+
"$ref": "#/definitions/RemoteDomainAccessScope"
2615+
}
26062616
}
26072617
},
26082618
"additionalProperties": false
@@ -2655,6 +2665,48 @@
26552665
}
26562666
]
26572667
},
2668+
"RemoteDomainAccessScope": {
2669+
"description": "External command access definition.",
2670+
"type": "object",
2671+
"required": [
2672+
"domain",
2673+
"windows"
2674+
],
2675+
"properties": {
2676+
"scheme": {
2677+
"description": "The URL scheme to allow. By default, all schemas are allowed.",
2678+
"type": [
2679+
"string",
2680+
"null"
2681+
]
2682+
},
2683+
"domain": {
2684+
"description": "The domain to allow.",
2685+
"type": "string"
2686+
},
2687+
"windows": {
2688+
"description": "The list of window labels this scope applies to.",
2689+
"type": "array",
2690+
"items": {
2691+
"type": "string"
2692+
}
2693+
},
2694+
"plugins": {
2695+
"description": "The list of plugins that are allowed in this scope.",
2696+
"default": [],
2697+
"type": "array",
2698+
"items": {
2699+
"type": "string"
2700+
}
2701+
},
2702+
"enableTauriAPI": {
2703+
"description": "Enables access to the Tauri API.",
2704+
"default": false,
2705+
"type": "boolean"
2706+
}
2707+
},
2708+
"additionalProperties": false
2709+
},
26582710
"UpdaterConfig": {
26592711
"description": "The Updater configuration object.\n\nSee more: https://tauri.app/v1/api/config#updaterconfig",
26602712
"type": "object",

core/tauri-runtime-wry/src/lib.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ impl<T: UserEvent> Context<T> {
214214
impl<T: UserEvent> Context<T> {
215215
fn create_webview(&self, pending: PendingWindow<T, Wry<T>>) -> Result<DetachedWindow<T, Wry<T>>> {
216216
let label = pending.label.clone();
217+
let current_url = pending.current_url.clone();
217218
let menu_ids = pending.menu_ids.clone();
218219
let js_event_listeners = pending.js_event_listeners.clone();
219220
let context = self.clone();
@@ -235,6 +236,7 @@ impl<T: UserEvent> Context<T> {
235236
};
236237
Ok(DetachedWindow {
237238
label,
239+
current_url,
238240
dispatcher,
239241
menu_ids,
240242
js_event_listeners,
@@ -1985,6 +1987,7 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
19851987

19861988
fn create_window(&self, pending: PendingWindow<T, Self>) -> Result<DetachedWindow<T, Self>> {
19871989
let label = pending.label.clone();
1990+
let current_url = pending.current_url.clone();
19881991
let menu_ids = pending.menu_ids.clone();
19891992
let js_event_listeners = pending.js_event_listeners.clone();
19901993
let window_id = rand::random();
@@ -2011,6 +2014,7 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
20112014

20122015
Ok(DetachedWindow {
20132016
label,
2017+
current_url,
20142018
dispatcher,
20152019
menu_ids,
20162020
js_event_listeners,
@@ -3040,7 +3044,7 @@ fn create_webview<T: UserEvent>(
30403044
mut window_builder,
30413045
ipc_handler,
30423046
label,
3043-
url,
3047+
current_url,
30443048
menu_ids,
30453049
js_event_listeners,
30463050
..
@@ -3089,7 +3093,7 @@ fn create_webview<T: UserEvent>(
30893093
}
30903094
let mut webview_builder = WebViewBuilder::new(window)
30913095
.map_err(|e| Error::CreateWebview(Box::new(e)))?
3092-
.with_url(&url)
3096+
.with_url(current_url.lock().unwrap().as_str())
30933097
.unwrap() // safe to unwrap because we validate the URL beforehand
30943098
.with_transparent(is_window_transparent)
30953099
.with_accept_first_mouse(webview_attributes.accept_first_mouse);
@@ -3124,6 +3128,7 @@ fn create_webview<T: UserEvent>(
31243128
webview_builder = webview_builder.with_ipc_handler(create_ipc_handler(
31253129
context,
31263130
label.clone(),
3131+
current_url,
31273132
menu_ids,
31283133
js_event_listeners,
31293134
handler,
@@ -3234,6 +3239,7 @@ fn create_webview<T: UserEvent>(
32343239
fn create_ipc_handler<T: UserEvent>(
32353240
context: Context<T>,
32363241
label: String,
3242+
current_url: Arc<Mutex<Url>>,
32373243
menu_ids: Arc<Mutex<HashMap<MenuHash, MenuId>>>,
32383244
js_event_listeners: Arc<Mutex<HashMap<JsEventListenerKey, HashSet<u64>>>>,
32393245
handler: WebviewIpcHandler<T, Wry<T>>,
@@ -3242,6 +3248,7 @@ fn create_ipc_handler<T: UserEvent>(
32423248
let window_id = context.webview_id_map.get(&window.id()).unwrap();
32433249
handler(
32443250
DetachedWindow {
3251+
current_url: current_url.clone(),
32453252
dispatcher: WryDispatcher {
32463253
window_id,
32473254
context: context.clone(),

core/tauri-runtime/src/window.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,6 @@ pub struct PendingWindow<T: UserEvent, R: Runtime<T>> {
225225
/// How to handle IPC calls on the webview window.
226226
pub ipc_handler: Option<WebviewIpcHandler<T, R>>,
227227

228-
/// The resolved URL to load on the webview.
229-
pub url: String,
230-
231228
/// Maps runtime id to a string menu id.
232229
pub menu_ids: Arc<Mutex<HashMap<MenuHash, MenuId>>>,
233230

@@ -236,6 +233,9 @@ pub struct PendingWindow<T: UserEvent, R: Runtime<T>> {
236233

237234
/// A handler to decide if incoming url is allowed to navigate.
238235
pub navigation_handler: Option<Box<dyn Fn(Url) -> bool + Send>>,
236+
237+
/// The current webview URL.
238+
pub current_url: Arc<Mutex<Url>>,
239239
}
240240

241241
pub fn is_label_valid(label: &str) -> bool {
@@ -272,10 +272,10 @@ impl<T: UserEvent, R: Runtime<T>> PendingWindow<T, R> {
272272
uri_scheme_protocols: Default::default(),
273273
label,
274274
ipc_handler: None,
275-
url: "tauri://localhost".to_string(),
276275
menu_ids: Arc::new(Mutex::new(menu_ids)),
277276
js_event_listeners: Default::default(),
278277
navigation_handler: Default::default(),
278+
current_url: Arc::new(Mutex::new("tauri://localhost".parse().unwrap())),
279279
})
280280
}
281281
}
@@ -302,10 +302,10 @@ impl<T: UserEvent, R: Runtime<T>> PendingWindow<T, R> {
302302
uri_scheme_protocols: Default::default(),
303303
label,
304304
ipc_handler: None,
305-
url: "tauri://localhost".to_string(),
306305
menu_ids: Arc::new(Mutex::new(menu_ids)),
307306
js_event_listeners: Default::default(),
308307
navigation_handler: Default::default(),
308+
current_url: Arc::new(Mutex::new("tauri://localhost".parse().unwrap())),
309309
})
310310
}
311311
}
@@ -346,6 +346,9 @@ pub struct JsEventListenerKey {
346346
/// A webview window that is not yet managed by Tauri.
347347
#[derive(Debug)]
348348
pub struct DetachedWindow<T: UserEvent, R: Runtime<T>> {
349+
/// The current webview URL.
350+
pub current_url: Arc<Mutex<Url>>,
351+
349352
/// Name of the window
350353
pub label: String,
351354

@@ -362,6 +365,7 @@ pub struct DetachedWindow<T: UserEvent, R: Runtime<T>> {
362365
impl<T: UserEvent, R: Runtime<T>> Clone for DetachedWindow<T, R> {
363366
fn clone(&self) -> Self {
364367
Self {
368+
current_url: self.current_url.clone(),
365369
label: self.label.clone(),
366370
dispatcher: self.dispatcher.clone(),
367371
menu_ids: self.menu_ids.clone(),

core/tauri-utils/src/config.rs

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1196,6 +1196,25 @@ impl Default for DisabledCspModificationKind {
11961196
}
11971197
}
11981198

1199+
/// External command access definition.
1200+
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
1201+
#[cfg_attr(feature = "schema", derive(JsonSchema))]
1202+
#[serde(rename_all = "camelCase", deny_unknown_fields)]
1203+
pub struct RemoteDomainAccessScope {
1204+
/// The URL scheme to allow. By default, all schemas are allowed.
1205+
pub scheme: Option<String>,
1206+
/// The domain to allow.
1207+
pub domain: String,
1208+
/// The list of window labels this scope applies to.
1209+
pub windows: Vec<String>,
1210+
/// The list of plugins that are allowed in this scope.
1211+
#[serde(default)]
1212+
pub plugins: Vec<String>,
1213+
/// Enables access to the Tauri API.
1214+
#[serde(default, rename = "enableTauriAPI", alias = "enable-tauri-api")]
1215+
pub enable_tauri_api: bool,
1216+
}
1217+
11991218
/// Security configuration.
12001219
///
12011220
/// See more: https://tauri.app/v1/api/config#securityconfig
@@ -1233,6 +1252,20 @@ pub struct SecurityConfig {
12331252
/// Your application might be vulnerable to XSS attacks without this Tauri protection.
12341253
#[serde(default, alias = "dangerous-disable-asset-csp-modification")]
12351254
pub dangerous_disable_asset_csp_modification: DisabledCspModificationKind,
1255+
/// Allow external domains to send command to Tauri.
1256+
///
1257+
/// By default, external domains do not have access to `window.__TAURI__`, which means they cannot
1258+
/// communicate with the commands defined in Rust. This prevents attacks where an externally
1259+
/// loaded malicious or compromised sites could start executing commands on the user's device.
1260+
///
1261+
/// This configuration allows a set of external domains to have access to the Tauri commands.
1262+
/// When you configure a domain to be allowed to access the IPC, all subpaths are allowed. Subdomains are not allowed.
1263+
///
1264+
/// **WARNING:** Only use this option if you either have internal checks against malicious
1265+
/// external sites or you can trust the allowed external sites. You application might be
1266+
/// vulnerable to dangerous Tauri command related attacks otherwise.
1267+
#[serde(default, alias = "dangerous-remote-domain-ipc-access")]
1268+
pub dangerous_remote_domain_ipc_access: Vec<RemoteDomainAccessScope>,
12361269
}
12371270

12381271
/// Defines an allowlist type.
@@ -3590,20 +3623,43 @@ mod build {
35903623
}
35913624
}
35923625

3626+
impl ToTokens for RemoteDomainAccessScope {
3627+
fn to_tokens(&self, tokens: &mut TokenStream) {
3628+
let scheme = opt_str_lit(self.scheme.as_ref());
3629+
let domain = str_lit(&self.domain);
3630+
let windows = vec_lit(&self.windows, str_lit);
3631+
let plugins = vec_lit(&self.plugins, str_lit);
3632+
let enable_tauri_api = self.enable_tauri_api;
3633+
3634+
literal_struct!(
3635+
tokens,
3636+
RemoteDomainAccessScope,
3637+
scheme,
3638+
domain,
3639+
windows,
3640+
plugins,
3641+
enable_tauri_api
3642+
);
3643+
}
3644+
}
3645+
35933646
impl ToTokens for SecurityConfig {
35943647
fn to_tokens(&self, tokens: &mut TokenStream) {
35953648
let csp = opt_lit(self.csp.as_ref());
35963649
let dev_csp = opt_lit(self.dev_csp.as_ref());
35973650
let freeze_prototype = self.freeze_prototype;
35983651
let dangerous_disable_asset_csp_modification = &self.dangerous_disable_asset_csp_modification;
3652+
let dangerous_remote_domain_ipc_access =
3653+
vec_lit(&self.dangerous_remote_domain_ipc_access, identity);
35993654

36003655
literal_struct!(
36013656
tokens,
36023657
SecurityConfig,
36033658
csp,
36043659
dev_csp,
36053660
freeze_prototype,
3606-
dangerous_disable_asset_csp_modification
3661+
dangerous_disable_asset_csp_modification,
3662+
dangerous_remote_domain_ipc_access
36073663
);
36083664
}
36093665
}
@@ -3868,6 +3924,7 @@ mod test {
38683924
dev_csp: None,
38693925
freeze_prototype: false,
38703926
dangerous_disable_asset_csp_modification: DisabledCspModificationKind::Flag(false),
3927+
dangerous_remote_domain_ipc_access: Vec::new(),
38713928
},
38723929
allowlist: AllowlistConfig::default(),
38733930
system_tray: None,

core/tauri/scripts/init.js

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,33 @@
22
// SPDX-License-Identifier: Apache-2.0
33
// SPDX-License-Identifier: MIT
44

5-
;(function () {
6-
if (window.location.origin.startsWith(__TEMPLATE_origin__)) {
7-
__RAW_freeze_prototype__
5+
; (function () {
6+
__RAW_freeze_prototype__
87

9-
;(function () {
8+
; (function () {
109
__RAW_hotkeys__
1110
})()
1211

13-
__RAW_pattern_script__
12+
__RAW_pattern_script__
1413

15-
__RAW_ipc_script__
16-
;(function () {
14+
__RAW_ipc_script__
15+
; (function () {
1716
__RAW_bundle_script__
1817
})()
1918

20-
__RAW_listen_function__
19+
__RAW_listen_function__
2120

22-
__RAW_core_script__
21+
__RAW_core_script__
2322

24-
__RAW_event_initialization_script__
23+
__RAW_event_initialization_script__
2524

26-
if (window.ipc) {
25+
if (window.ipc) {
26+
window.__TAURI_INVOKE__('__initialized', { url: window.location.href })
27+
} else {
28+
window.addEventListener('DOMContentLoaded', function () {
2729
window.__TAURI_INVOKE__('__initialized', { url: window.location.href })
28-
} else {
29-
window.addEventListener('DOMContentLoaded', function () {
30-
window.__TAURI_INVOKE__('__initialized', { url: window.location.href })
31-
})
32-
}
33-
34-
__RAW_plugin_initialization_script__
30+
})
3531
}
32+
33+
__RAW_plugin_initialization_script__
3634
})()

0 commit comments

Comments
 (0)