Skip to content

Commit

Permalink
feat(core): allow configuring remote domains with IPC access, closes #…
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasfernog committed Apr 11, 2023
1 parent 6ff801e commit ee71c31
Show file tree
Hide file tree
Showing 19 changed files with 794 additions and 98 deletions.
6 changes: 6 additions & 0 deletions .changes/remote-urls.md
@@ -0,0 +1,6 @@
---
"tauri": patch
"tauri-utils": patch
---

Added configuration to specify remote URLs allowed to access the IPC.
2 changes: 1 addition & 1 deletion core/tauri-build/src/lib.rs
Expand Up @@ -339,7 +339,7 @@ pub fn try_build(attributes: Attributes) -> Result<()> {

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

Expand Down
2 changes: 1 addition & 1 deletion core/tauri-build/src/static_vcruntime.rs
Expand Up @@ -54,5 +54,5 @@ fn override_msvcrt_lib() {
f.write_all(bytes).unwrap();
}
// Add the output directory to the native library path.
println!("cargo:rustc-link-search=native={}", out_dir);
println!("cargo:rustc-link-search=native={out_dir}");
}
52 changes: 52 additions & 0 deletions core/tauri-config-schema/schema.json
Expand Up @@ -163,6 +163,7 @@
},
"security": {
"dangerousDisableAssetCspModification": false,
"dangerousRemoteDomainIpcAccess": [],
"freezePrototype": false
},
"updater": {
Expand Down Expand Up @@ -415,6 +416,7 @@
"description": "Security configuration.",
"default": {
"dangerousDisableAssetCspModification": false,
"dangerousRemoteDomainIpcAccess": [],
"freezePrototype": false
},
"allOf": [
Expand Down Expand Up @@ -2603,6 +2605,14 @@
"$ref": "#/definitions/DisabledCspModificationKind"
}
]
},
"dangerousRemoteDomainIpcAccess": {
"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.",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/RemoteDomainAccessScope"
}
}
},
"additionalProperties": false
Expand Down Expand Up @@ -2655,6 +2665,48 @@
}
]
},
"RemoteDomainAccessScope": {
"description": "External command access definition.",
"type": "object",
"required": [
"domain",
"windows"
],
"properties": {
"scheme": {
"description": "The URL scheme to allow. By default, all schemas are allowed.",
"type": [
"string",
"null"
]
},
"domain": {
"description": "The domain to allow.",
"type": "string"
},
"windows": {
"description": "The list of window labels this scope applies to.",
"type": "array",
"items": {
"type": "string"
}
},
"plugins": {
"description": "The list of plugins that are allowed in this scope.",
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"enableTauriAPI": {
"description": "Enables access to the Tauri API.",
"default": false,
"type": "boolean"
}
},
"additionalProperties": false
},
"UpdaterConfig": {
"description": "The Updater configuration object.\n\nSee more: https://tauri.app/v1/api/config#updaterconfig",
"type": "object",
Expand Down
11 changes: 9 additions & 2 deletions core/tauri-runtime-wry/src/lib.rs
Expand Up @@ -214,6 +214,7 @@ impl<T: UserEvent> Context<T> {
impl<T: UserEvent> Context<T> {
fn create_webview(&self, pending: PendingWindow<T, Wry<T>>) -> Result<DetachedWindow<T, Wry<T>>> {
let label = pending.label.clone();
let current_url = pending.current_url.clone();
let menu_ids = pending.menu_ids.clone();
let js_event_listeners = pending.js_event_listeners.clone();
let context = self.clone();
Expand All @@ -235,6 +236,7 @@ impl<T: UserEvent> Context<T> {
};
Ok(DetachedWindow {
label,
current_url,
dispatcher,
menu_ids,
js_event_listeners,
Expand Down Expand Up @@ -1985,6 +1987,7 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {

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

Ok(DetachedWindow {
label,
current_url,
dispatcher,
menu_ids,
js_event_listeners,
Expand Down Expand Up @@ -3040,7 +3044,7 @@ fn create_webview<T: UserEvent>(
mut window_builder,
ipc_handler,
label,
url,
current_url,
menu_ids,
js_event_listeners,
..
Expand Down Expand Up @@ -3089,7 +3093,7 @@ fn create_webview<T: UserEvent>(
}
let mut webview_builder = WebViewBuilder::new(window)
.map_err(|e| Error::CreateWebview(Box::new(e)))?
.with_url(&url)
.with_url(current_url.lock().unwrap().as_str())
.unwrap() // safe to unwrap because we validate the URL beforehand
.with_transparent(is_window_transparent)
.with_accept_first_mouse(webview_attributes.accept_first_mouse);
Expand Down Expand Up @@ -3124,6 +3128,7 @@ fn create_webview<T: UserEvent>(
webview_builder = webview_builder.with_ipc_handler(create_ipc_handler(
context,
label.clone(),
current_url,
menu_ids,
js_event_listeners,
handler,
Expand Down Expand Up @@ -3234,6 +3239,7 @@ fn create_webview<T: UserEvent>(
fn create_ipc_handler<T: UserEvent>(
context: Context<T>,
label: String,
current_url: Arc<Mutex<Url>>,
menu_ids: Arc<Mutex<HashMap<MenuHash, MenuId>>>,
js_event_listeners: Arc<Mutex<HashMap<JsEventListenerKey, HashSet<u64>>>>,
handler: WebviewIpcHandler<T, Wry<T>>,
Expand All @@ -3242,6 +3248,7 @@ fn create_ipc_handler<T: UserEvent>(
let window_id = context.webview_id_map.get(&window.id()).unwrap();
handler(
DetachedWindow {
current_url: current_url.clone(),
dispatcher: WryDispatcher {
window_id,
context: context.clone(),
Expand Down
14 changes: 9 additions & 5 deletions core/tauri-runtime/src/window.rs
Expand Up @@ -225,9 +225,6 @@ pub struct PendingWindow<T: UserEvent, R: Runtime<T>> {
/// How to handle IPC calls on the webview window.
pub ipc_handler: Option<WebviewIpcHandler<T, R>>,

/// The resolved URL to load on the webview.
pub url: String,

/// Maps runtime id to a string menu id.
pub menu_ids: Arc<Mutex<HashMap<MenuHash, MenuId>>>,

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

/// A handler to decide if incoming url is allowed to navigate.
pub navigation_handler: Option<Box<dyn Fn(Url) -> bool + Send>>,

/// The current webview URL.
pub current_url: Arc<Mutex<Url>>,
}

pub fn is_label_valid(label: &str) -> bool {
Expand Down Expand Up @@ -272,10 +272,10 @@ impl<T: UserEvent, R: Runtime<T>> PendingWindow<T, R> {
uri_scheme_protocols: Default::default(),
label,
ipc_handler: None,
url: "tauri://localhost".to_string(),
menu_ids: Arc::new(Mutex::new(menu_ids)),
js_event_listeners: Default::default(),
navigation_handler: Default::default(),
current_url: Arc::new(Mutex::new("tauri://localhost".parse().unwrap())),
})
}
}
Expand All @@ -302,10 +302,10 @@ impl<T: UserEvent, R: Runtime<T>> PendingWindow<T, R> {
uri_scheme_protocols: Default::default(),
label,
ipc_handler: None,
url: "tauri://localhost".to_string(),
menu_ids: Arc::new(Mutex::new(menu_ids)),
js_event_listeners: Default::default(),
navigation_handler: Default::default(),
current_url: Arc::new(Mutex::new("tauri://localhost".parse().unwrap())),
})
}
}
Expand Down Expand Up @@ -346,6 +346,9 @@ pub struct JsEventListenerKey {
/// A webview window that is not yet managed by Tauri.
#[derive(Debug)]
pub struct DetachedWindow<T: UserEvent, R: Runtime<T>> {
/// The current webview URL.
pub current_url: Arc<Mutex<Url>>,

/// Name of the window
pub label: String,

Expand All @@ -362,6 +365,7 @@ pub struct DetachedWindow<T: UserEvent, R: Runtime<T>> {
impl<T: UserEvent, R: Runtime<T>> Clone for DetachedWindow<T, R> {
fn clone(&self) -> Self {
Self {
current_url: self.current_url.clone(),
label: self.label.clone(),
dispatcher: self.dispatcher.clone(),
menu_ids: self.menu_ids.clone(),
Expand Down
59 changes: 58 additions & 1 deletion core/tauri-utils/src/config.rs
Expand Up @@ -1196,6 +1196,25 @@ impl Default for DisabledCspModificationKind {
}
}

/// External command access definition.
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct RemoteDomainAccessScope {
/// The URL scheme to allow. By default, all schemas are allowed.
pub scheme: Option<String>,
/// The domain to allow.
pub domain: String,
/// The list of window labels this scope applies to.
pub windows: Vec<String>,
/// The list of plugins that are allowed in this scope.
#[serde(default)]
pub plugins: Vec<String>,
/// Enables access to the Tauri API.
#[serde(default, rename = "enableTauriAPI", alias = "enable-tauri-api")]
pub enable_tauri_api: bool,
}

/// Security configuration.
///
/// See more: https://tauri.app/v1/api/config#securityconfig
Expand Down Expand Up @@ -1233,6 +1252,20 @@ pub struct SecurityConfig {
/// Your application might be vulnerable to XSS attacks without this Tauri protection.
#[serde(default, alias = "dangerous-disable-asset-csp-modification")]
pub dangerous_disable_asset_csp_modification: DisabledCspModificationKind,
/// Allow external domains to send command to Tauri.
///
/// By 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.
///
/// This 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.
///
/// **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.
#[serde(default, alias = "dangerous-remote-domain-ipc-access")]
pub dangerous_remote_domain_ipc_access: Vec<RemoteDomainAccessScope>,
}

/// Defines an allowlist type.
Expand Down Expand Up @@ -3590,20 +3623,43 @@ mod build {
}
}

impl ToTokens for RemoteDomainAccessScope {
fn to_tokens(&self, tokens: &mut TokenStream) {
let scheme = opt_str_lit(self.scheme.as_ref());
let domain = str_lit(&self.domain);
let windows = vec_lit(&self.windows, str_lit);
let plugins = vec_lit(&self.plugins, str_lit);
let enable_tauri_api = self.enable_tauri_api;

literal_struct!(
tokens,
RemoteDomainAccessScope,
scheme,
domain,
windows,
plugins,
enable_tauri_api
);
}
}

impl ToTokens for SecurityConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let csp = opt_lit(self.csp.as_ref());
let dev_csp = opt_lit(self.dev_csp.as_ref());
let freeze_prototype = self.freeze_prototype;
let dangerous_disable_asset_csp_modification = &self.dangerous_disable_asset_csp_modification;
let dangerous_remote_domain_ipc_access =
vec_lit(&self.dangerous_remote_domain_ipc_access, identity);

literal_struct!(
tokens,
SecurityConfig,
csp,
dev_csp,
freeze_prototype,
dangerous_disable_asset_csp_modification
dangerous_disable_asset_csp_modification,
dangerous_remote_domain_ipc_access
);
}
}
Expand Down Expand Up @@ -3868,6 +3924,7 @@ mod test {
dev_csp: None,
freeze_prototype: false,
dangerous_disable_asset_csp_modification: DisabledCspModificationKind::Flag(false),
dangerous_remote_domain_ipc_access: Vec::new(),
},
allowlist: AllowlistConfig::default(),
system_tray: None,
Expand Down
34 changes: 16 additions & 18 deletions core/tauri/scripts/init.js
Expand Up @@ -2,35 +2,33 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

;(function () {
if (window.location.origin.startsWith(__TEMPLATE_origin__)) {
__RAW_freeze_prototype__
; (function () {
__RAW_freeze_prototype__

;(function () {
; (function () {
__RAW_hotkeys__
})()

__RAW_pattern_script__
__RAW_pattern_script__

__RAW_ipc_script__
;(function () {
__RAW_ipc_script__
; (function () {
__RAW_bundle_script__
})()

__RAW_listen_function__
__RAW_listen_function__

__RAW_core_script__
__RAW_core_script__

__RAW_event_initialization_script__
__RAW_event_initialization_script__

if (window.ipc) {
if (window.ipc) {
window.__TAURI_INVOKE__('__initialized', { url: window.location.href })
} else {
window.addEventListener('DOMContentLoaded', function () {
window.__TAURI_INVOKE__('__initialized', { url: window.location.href })
} else {
window.addEventListener('DOMContentLoaded', function () {
window.__TAURI_INVOKE__('__initialized', { url: window.location.href })
})
}

__RAW_plugin_initialization_script__
})
}

__RAW_plugin_initialization_script__
})()

0 comments on commit ee71c31

Please sign in to comment.