From c077f449270cffbf7956b1af81e1fb237ebf564a Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Sun, 9 Jan 2022 16:40:22 -0300 Subject: [PATCH] feat: force endpoint URL to use https on release [TRI-015] (#41) --- .changes/updater-endpoint-url-https.md | 6 +++ core/tauri-utils/src/config.rs | 49 +++++++++++++++++++++-- core/tauri/src/updater/mod.rs | 8 +++- examples/updater/src-tauri/Cargo.lock | 54 ++++++++++++++++++++++++++ examples/updater/src-tauri/Cargo.toml | 2 +- tooling/cli.rs/Cargo.toml | 2 +- tooling/cli.rs/schema.json | 9 ++++- tooling/cli.rs/src/interface/rust.rs | 4 +- 8 files changed, 125 insertions(+), 9 deletions(-) create mode 100644 .changes/updater-endpoint-url-https.md diff --git a/.changes/updater-endpoint-url-https.md b/.changes/updater-endpoint-url-https.md new file mode 100644 index 000000000..cd998f3bd --- /dev/null +++ b/.changes/updater-endpoint-url-https.md @@ -0,0 +1,6 @@ +--- +"tauri": patch +"tauri-utils": patch +--- + +Force updater endpoint URL to use `https` on release builds. diff --git a/core/tauri-utils/src/config.rs b/core/tauri-utils/src/config.rs index ff5b67595..f5bf2743c 100644 --- a/core/tauri-utils/src/config.rs +++ b/core/tauri-utils/src/config.rs @@ -1359,6 +1359,38 @@ impl TauriConfig { } } +/// A URL to an updater server. +/// +/// The URL must use the `https` scheme on production. +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +pub struct UpdaterEndpoint(pub Url); + +impl std::fmt::Display for UpdaterEndpoint { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl<'de> Deserialize<'de> for UpdaterEndpoint { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let url = Url::deserialize(deserializer)?; + #[cfg(all(not(debug_assertions), not(feature = "schema")))] + { + if url.scheme() != "https" { + return Err(serde::de::Error::custom( + "The configured updater endpoint must use the `https` protocol.", + )); + } + } + Ok(Self(url)) + } +} + /// The Updater configuration object. #[skip_serializing_none] #[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] @@ -1371,8 +1403,8 @@ pub struct UpdaterConfig { /// Display built-in dialog or use event system if disabled. #[serde(default = "default_dialog")] pub dialog: bool, - /// The updater endpoints. - pub endpoints: Option>, + /// The updater endpoints. TLS is enforced on production. + pub endpoints: Option>, /// Signature public key. pub pubkey: String, } @@ -2029,7 +2061,18 @@ mod build { let active = self.active; let dialog = self.dialog; let pubkey = str_lit(&self.pubkey); - let endpoints = opt_vec_str_lit(self.endpoints.as_ref()); + let endpoints = opt_lit( + self + .endpoints + .as_ref() + .map(|list| { + vec_lit(list, |url| { + let url = url.0.as_str(); + quote! { ::tauri::utils::config::UpdaterEndpoint(#url.parse().unwrap()) } + }) + }) + .as_ref(), + ); literal_struct!(tokens, UpdaterConfig, active, dialog, pubkey, endpoints); } diff --git a/core/tauri/src/updater/mod.rs b/core/tauri/src/updater/mod.rs index bde603a4b..b9bc97c43 100644 --- a/core/tauri/src/updater/mod.rs +++ b/core/tauri/src/updater/mod.rs @@ -381,6 +381,10 @@ pub(crate) async fn check_update_with_dialog( window: Window, ) { if let Some(endpoints) = updater_config.endpoints.clone() { + let endpoints = endpoints + .iter() + .map(|e| e.to_string()) + .collect::>(); let env = window.state::().inner().clone(); // check updates match self::core::builder(env) @@ -440,7 +444,9 @@ pub(crate) fn listener( .endpoints .as_ref() .expect("Something wrong with endpoints") - .clone(); + .iter() + .map(|e| e.to_string()) + .collect::>(); let pubkey = updater_config.pubkey.clone(); diff --git a/examples/updater/src-tauri/Cargo.lock b/examples/updater/src-tauri/Cargo.lock index e355ff6cb..1d7ceadfe 100644 --- a/examples/updater/src-tauri/Cargo.lock +++ b/examples/updater/src-tauri/Cargo.lock @@ -3028,6 +3028,7 @@ dependencies = [ name = "tauri-macros" version = "1.0.0-beta.5" dependencies = [ + "heck", "proc-macro2", "quote", "syn", @@ -3611,6 +3612,17 @@ dependencies = [ "windows 0.25.0", ] +[[package]] +name = "webview2-com" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abdc9ca7cebd96a1005d5ba1e9d70c61c0f6c276a41cddaeecb7842d436ab3bc" +dependencies = [ + "webview2-com-macros 0.4.0", + "webview2-com-sys 0.7.0", + "windows 0.25.0", +] + [[package]] name = "webview2-com-macros" version = "0.4.0" @@ -3622,6 +3634,30 @@ dependencies = [ "syn", ] +[[package]] +name = "webview2-com-macros" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bca4b354035275764ea4ca8d6bfa74cc5b0e8126e7cd675ee327574b59e13d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "webview2-com-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73472d7f0e9038b58204cb3f582ee138a8c181719dc6825ea03371ad085c6058" +dependencies = [ + "regex", + "serde", + "serde_json", + "thiserror", + "windows 0.25.0", +] + [[package]] name = "webview2-com-sys" version = "0.7.0" @@ -3773,6 +3809,18 @@ dependencies = [ "windows_reader 0.21.1", ] +[[package]] +name = "windows_macros" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6103bcf1a7396d66f6f08a2d67d8a2ab34efaf4b1d7567301af2c002507c8c3b" +dependencies = [ + "syn", + "windows_gen 0.25.0", + "windows_quote 0.25.0", + "windows_reader 0.25.0", +] + [[package]] name = "windows_quote" version = "0.25.0" @@ -3785,6 +3833,12 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cf987b5288c15e1997226848f78f3ed3ef8b78dcfd71a201c8c8684163a7e4d" +[[package]] +name = "windows_quote" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e414df8d5dd2013f2317fdc414d3ad035effcb7aef1f16bf508ac5743154835a" + [[package]] name = "windows_reader" version = "0.25.0" diff --git a/examples/updater/src-tauri/Cargo.toml b/examples/updater/src-tauri/Cargo.toml index 1cec0f597..c6fc4921d 100644 --- a/examples/updater/src-tauri/Cargo.toml +++ b/examples/updater/src-tauri/Cargo.toml @@ -7,7 +7,7 @@ rust-version = "1.56" license = "Apache-2.0 OR MIT" [build-dependencies] -tauri-build = { path = "../../../core/tauri-build", features = [ "codegen" ] } +tauri-build = { path = "../../../core/tauri-build", features = ["codegen"] } [dependencies] serde_json = "1.0" diff --git a/tooling/cli.rs/Cargo.toml b/tooling/cli.rs/Cargo.toml index bf065051a..854fab540 100644 --- a/tooling/cli.rs/Cargo.toml +++ b/tooling/cli.rs/Cargo.toml @@ -30,7 +30,7 @@ notify = "4.0" shared_child = "1.0" toml_edit = "0.12" json-patch = "0.2" -tauri-utils = { version = "1.0.0-beta.3", path = "../../core/tauri-utils" } +tauri-utils = { version = "1.0.0-beta.3", path = "../../core/tauri-utils", features = ["isolation", "schema"] } schemars = { version = "0.8", features = ["url"] } toml = "0.5" valico = "3.6" diff --git a/tooling/cli.rs/schema.json b/tooling/cli.rs/schema.json index 12c02ae0f..1abcc281b 100644 --- a/tooling/cli.rs/schema.json +++ b/tooling/cli.rs/schema.json @@ -1551,13 +1551,13 @@ "type": "boolean" }, "endpoints": { - "description": "The updater endpoints.", + "description": "The updater endpoints. TLS is enforced on production.", "type": [ "array", "null" ], "items": { - "type": "string" + "$ref": "#/definitions/UpdaterEndpoint" } }, "pubkey": { @@ -1567,6 +1567,11 @@ }, "additionalProperties": false }, + "UpdaterEndpoint": { + "description": "A URL to an updater server.\n\nThe URL must use the `https` scheme on production.", + "type": "string", + "format": "uri" + }, "WindowAllowlistConfig": { "description": "Allowlist for the window APIs.", "type": "object", diff --git a/tooling/cli.rs/src/interface/rust.rs b/tooling/cli.rs/src/interface/rust.rs index 78460159e..5269ef232 100644 --- a/tooling/cli.rs/src/interface/rust.rs +++ b/tooling/cli.rs/src/interface/rust.rs @@ -496,7 +496,9 @@ fn tauri_config_to_bundle_settings( // unwrap_or as we have a default value but used to prevent any failing dialog: updater_config.dialog, pubkey: updater_config.pubkey, - endpoints: updater_config.endpoints, + endpoints: updater_config + .endpoints + .map(|endpoints| endpoints.iter().map(|e| e.to_string()).collect()), }), ..Default::default() })