Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): Allow http origin on Windows, fixes: #3007 #7645

Merged
merged 12 commits into from
Sep 26, 2023
8 changes: 8 additions & 0 deletions .changes/windows-http-scheme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'tauri': 'patch:enhance'
lucasfernog marked this conversation as resolved.
Show resolved Hide resolved
'tauri-runtime': 'patch:enhance'
'tauri-runtime-wry': 'patch:enhance'
'tauri-utils': 'patch:enhance'
---

Add setting to switch to `http://<scheme>.localhost/` for custom protocols on Windows.
7 changes: 7 additions & 0 deletions core/tauri-config-schema/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@
"security": {
"dangerousDisableAssetCspModification": false,
"dangerousRemoteDomainIpcAccess": [],
"dangerousUseHttpScheme": false,
"freezePrototype": false
},
"updater": {
Expand Down Expand Up @@ -423,6 +424,7 @@
"default": {
"dangerousDisableAssetCspModification": false,
"dangerousRemoteDomainIpcAccess": [],
"dangerousUseHttpScheme": false,
"freezePrototype": false
},
"allOf": [
Expand Down Expand Up @@ -2740,6 +2742,11 @@
"items": {
"$ref": "#/definitions/RemoteDomainAccessScope"
}
},
"dangerousUseHttpScheme": {
"description": "Sets whether the custom protocols should use `http://<scheme>.localhost` instead of the default `https://<scheme>.localhost` on Windows.\n\n**WARNING:** Using a `http` scheme will allow mixed content when trying to fetch `http` endpoints and is therefore less secure but will match the behavior of the `<scheme>://localhost` protocols used on macOS and Linux.",
"default": false,
"type": "boolean"
}
},
"additionalProperties": false
Expand Down
2 changes: 1 addition & 1 deletion core/tauri-runtime-wry/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ exclude = [ "CHANGELOG.md", "/target" ]
readme = "README.md"

[dependencies]
wry = { version = "0.24.1", default-features = false, features = [ "file-drop", "protocol" ] }
wry = { version = "0.24.4", default-features = false, features = [ "file-drop", "protocol" ] }
tauri-runtime = { version = "0.14.0", path = "../tauri-runtime" }
tauri-utils = { version = "1.4.0", path = "../tauri-utils" }
uuid = { version = "1", features = [ "v4" ] }
Expand Down
5 changes: 5 additions & 0 deletions core/tauri-runtime-wry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3194,6 +3194,11 @@ fn create_webview<T: UserEvent>(
});
}

#[cfg(windows)]
{
webview_builder = webview_builder.with_https_scheme(!pending.http_scheme);
}

if let Some(handler) = ipc_handler {
webview_builder = webview_builder.with_ipc_handler(create_ipc_handler(
context,
Expand Down
5 changes: 5 additions & 0 deletions core/tauri-runtime/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,9 @@ pub struct PendingWindow<T: UserEvent, R: Runtime<T>> {

pub uri_scheme_protocols: HashMap<String, Box<UriSchemeProtocol>>,

// Whether custom protocols on windows should use http://<scheme>.localhost/ instead of https://<scheme>.localhost/
pub http_scheme: bool,

/// How to handle IPC calls on the webview window.
pub ipc_handler: Option<WebviewIpcHandler<T, R>>,

Expand Down Expand Up @@ -281,6 +284,7 @@ impl<T: UserEvent, R: Runtime<T>> PendingWindow<T, R> {
navigation_handler: Default::default(),
web_resource_request_handler: Default::default(),
url: "tauri://localhost".to_string(),
http_scheme: false,
})
}
}
Expand Down Expand Up @@ -312,6 +316,7 @@ impl<T: UserEvent, R: Runtime<T>> PendingWindow<T, R> {
navigation_handler: Default::default(),
web_resource_request_handler: Default::default(),
url: "tauri://localhost".to_string(),
http_scheme: false,
})
}
}
Expand Down
10 changes: 9 additions & 1 deletion core/tauri-utils/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1326,6 +1326,11 @@ pub struct SecurityConfig {
/// vulnerable to dangerous Tauri command related attacks otherwise.
#[serde(default, alias = "dangerous-remote-domain-ipc-access")]
pub dangerous_remote_domain_ipc_access: Vec<RemoteDomainAccessScope>,
/// Sets whether the custom protocols should use `http://<scheme>.localhost` instead of the default `https://<scheme>.localhost` on Windows.
///
/// **WARNING:** Using a `http` scheme will allow mixed content when trying to fetch `http` endpoints and is therefore less secure but will match the behavior of the `<scheme>://localhost` protocols used on macOS and Linux.
#[serde(default, alias = "dangerous-use-http-scheme")]
pub dangerous_use_http_scheme: bool,
}

/// Defines an allowlist type.
Expand Down Expand Up @@ -3736,6 +3741,7 @@ mod build {
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_use_http_scheme = &self.dangerous_use_http_scheme;
let dangerous_remote_domain_ipc_access =
vec_lit(&self.dangerous_remote_domain_ipc_access, identity);

Expand All @@ -3746,7 +3752,8 @@ mod build {
dev_csp,
freeze_prototype,
dangerous_disable_asset_csp_modification,
dangerous_remote_domain_ipc_access
dangerous_remote_domain_ipc_access,
dangerous_use_http_scheme
);
}
}
Expand Down Expand Up @@ -4013,6 +4020,7 @@ mod test {
freeze_prototype: false,
dangerous_disable_asset_csp_modification: DisabledCspModificationKind::Flag(false),
dangerous_remote_domain_ipc_access: Vec::new(),
dangerous_use_http_scheme: false,
},
allowlist: AllowlistConfig::default(),
system_tray: None,
Expand Down
10 changes: 5 additions & 5 deletions core/tauri/scripts/bundle.global.js

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions core/tauri/scripts/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@
})
}

const osName = __TEMPLATE_os_name__
const protocolScheme = __TEMPLATE_protocol_scheme__

window.__TAURI__.convertFileSrc = function convertFileSrc(filePath, protocol = 'asset') {
const path = encodeURIComponent(filePath)
return osName === 'windows'
? `${protocolScheme}://${protocol}.localhost/${path}`
: `${protocol}://localhost/${path}`
}

window.__TAURI__.transformCallback = function transformCallback(
callback,
once
Expand Down
11 changes: 6 additions & 5 deletions core/tauri/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1573,16 +1573,17 @@ impl<R: Runtime> Builder<R> {
(self.invoke_responder, self.invoke_initialization_script),
);

let http_scheme = manager.config().tauri.security.dangerous_use_http_scheme;

// set up all the windows defined in the config
for config in manager.config().tauri.windows.clone() {
let label = config.label.clone();
let webview_attributes = WebviewAttributes::from(&config);

self.pending_windows.push(PendingWindow::with_config(
config,
webview_attributes,
label,
)?);
let mut pending = PendingWindow::with_config(config, webview_attributes, label)?;
pending.http_scheme = http_scheme;

self.pending_windows.push(pending);
}

#[cfg(any(windows, target_os = "linux"))]
Expand Down
45 changes: 37 additions & 8 deletions core/tauri/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,10 @@ fn set_csp<R: Runtime>(
let default_src = csp
.entry("default-src".into())
.or_insert_with(Default::default);
default_src.push(crate::pattern::format_real_schema(schema));
default_src.push(crate::pattern::format_real_schema(
schema,
manager.config().tauri.security.dangerous_use_http_scheme,
));
}

Csp::DirectiveMap(csp).to_string()
Expand Down Expand Up @@ -388,7 +391,14 @@ impl<R: Runtime> WindowManager<R> {
match self.base_path() {
AppUrl::Url(WindowUrl::External(url)) => Cow::Borrowed(url),
#[cfg(windows)]
_ => Cow::Owned(Url::parse("https://tauri.localhost").unwrap()),
_ => {
let scheme = if self.inner.config.tauri.security.dangerous_use_http_scheme {
"http"
} else {
"https"
};
Cow::Owned(Url::parse(&format!("{scheme}://tauri.localhost")).unwrap())
}
#[cfg(not(windows))]
_ => Cow::Owned(Url::parse("tauri://localhost").unwrap()),
}
Expand Down Expand Up @@ -429,17 +439,19 @@ impl<R: Runtime> WindowManager<R> {
}
.render_default(&Default::default())?;

let mut webview_attributes = pending.webview_attributes;

let ipc_init = IpcJavascript {
isolation_origin: &match self.pattern() {
#[cfg(feature = "isolation")]
Pattern::Isolation { schema, .. } => crate::pattern::format_real_schema(schema),
Pattern::Isolation { schema, .. } => {
crate::pattern::format_real_schema(schema, pending.http_scheme)
}
_ => "".to_string(),
},
}
.render_default(&Default::default())?;

let mut webview_attributes = pending.webview_attributes;

let mut window_labels = window_labels.to_vec();
let l = label.to_string();
if !window_labels.contains(&l) {
Expand All @@ -466,7 +478,7 @@ impl<R: Runtime> WindowManager<R> {
if let Pattern::Isolation { schema, .. } = self.pattern() {
webview_attributes = webview_attributes.initialization_script(
&IsolationJavascript {
isolation_src: &crate::pattern::format_real_schema(schema),
isolation_src: &crate::pattern::format_real_schema(schema, pending.http_scheme),
style: tauri_utils::pattern::isolation::IFRAME_STYLE,
}
.render_default(&Default::default())?
Expand All @@ -491,7 +503,8 @@ impl<R: Runtime> WindowManager<R> {
let window_origin = if window_url.scheme() == "data" {
"null".into()
} else if cfg!(windows) && window_url.scheme() != "http" && window_url.scheme() != "https" {
format!("https://{}.localhost", window_url.scheme())
let scheme = if pending.http_scheme { "http" } else { "https" };
format!("{scheme}://{}.localhost", window_url.scheme())
} else {
format!(
"{}://{}{}",
Expand Down Expand Up @@ -782,6 +795,13 @@ impl<R: Runtime> WindowManager<R> {
hotkeys: &'a str,
}

#[derive(Template)]
#[default_template("../scripts/core.js")]
struct CoreJavascript<'a> {
os_name: &'a str,
protocol_scheme: &'a str,
}

let bundle_script = if with_global_tauri {
include_str!("../scripts/bundle.global.js")
} else {
Expand Down Expand Up @@ -813,7 +833,16 @@ impl<R: Runtime> WindowManager<R> {
"window['_' + window.__TAURI__.transformCallback(cb) ]".into()
)
),
core_script: include_str!("../scripts/core.js"),
core_script: &CoreJavascript {
os_name: std::env::consts::OS,
protocol_scheme: if self.inner.config.tauri.security.dangerous_use_http_scheme {
"http"
} else {
"https"
},
}
.render_default(&Default::default())?
.into_string(),
event_initialization_script: &self.event_initialization_script(),
plugin_initialization_script,
freeze_prototype,
Expand Down
5 changes: 3 additions & 2 deletions core/tauri/src/pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,10 @@ pub(crate) struct PatternJavascript {
}

#[allow(dead_code)]
pub(crate) fn format_real_schema(schema: &str) -> String {
pub(crate) fn format_real_schema(schema: &str, _http: bool) -> String {
if cfg!(windows) {
format!("https://{schema}.{ISOLATION_IFRAME_SRC_DOMAIN}")
let http = if _http { "http" } else { "https" };
format!("{http}://{schema}.{ISOLATION_IFRAME_SRC_DOMAIN}")
} else {
format!("{schema}://{ISOLATION_IFRAME_SRC_DOMAIN}")
}
Expand Down
6 changes: 6 additions & 0 deletions core/tauri/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,12 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> {
)?;
pending.navigation_handler = self.navigation_handler.take();
pending.web_resource_request_handler = self.web_resource_request_handler.take();
pending.http_scheme = self
.manager
.config()
.tauri
.security
.dangerous_use_http_scheme;

let labels = self.manager.labels().into_iter().collect::<Vec<_>>();
let pending = self
Expand Down
4 changes: 2 additions & 2 deletions examples/api/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions tooling/api/src/tauri.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ declare global {
ipc: {
postMessage: (args: string) => void
}
__TAURI__: {
convertFileSrc: (src: string, protocol: string) => string
}
}
}

Expand Down Expand Up @@ -127,10 +130,7 @@ async function invoke<T>(cmd: string, args: InvokeArgs = {}): Promise<T> {
* @since 1.0.0
*/
function convertFileSrc(filePath: string, protocol = 'asset'): string {
const path = encodeURIComponent(filePath)
return navigator.userAgent.includes('Windows')
? `https://${protocol}.localhost/${path}`
: `${protocol}://localhost/${path}`
return window.__TAURI__.convertFileSrc(filePath, protocol)
}

export type { InvokeArgs }
Expand Down
7 changes: 7 additions & 0 deletions tooling/cli/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@
"security": {
"dangerousDisableAssetCspModification": false,
"dangerousRemoteDomainIpcAccess": [],
"dangerousUseHttpScheme": false,
"freezePrototype": false
},
"updater": {
Expand Down Expand Up @@ -423,6 +424,7 @@
"default": {
"dangerousDisableAssetCspModification": false,
"dangerousRemoteDomainIpcAccess": [],
"dangerousUseHttpScheme": false,
"freezePrototype": false
},
"allOf": [
Expand Down Expand Up @@ -2740,6 +2742,11 @@
"items": {
"$ref": "#/definitions/RemoteDomainAccessScope"
}
},
"dangerousUseHttpScheme": {
"description": "Sets whether the custom protocols should use `http://<scheme>.localhost` instead of the default `https://<scheme>.localhost` on Windows.\n\n**WARNING:** Using a `http` scheme will allow mixed content when trying to fetch `http` endpoints and is therefore less secure but will match the behavior of the `<scheme>://localhost` protocols used on macOS and Linux.",
"default": false,
"type": "boolean"
}
},
"additionalProperties": false
Expand Down
Loading