Skip to main content

release_hub/
config.rs

1//! Static updater configuration shared across all release-source implementations.
2
3use serde::{Deserialize, Deserializer, de::Error as DeError};
4use std::ffi::OsString;
5use url::Url;
6
7/// Windows-specific installer configuration.
8#[derive(Debug, Clone, Deserialize, Default)]
9#[serde(rename_all = "camelCase")]
10pub struct WindowsConfig {
11    /// Additional arguments forwarded to the Windows installer launch path.
12    #[serde(
13        default,
14        alias = "installer-args",
15        deserialize_with = "deserialize_os_string"
16    )]
17    pub installer_args: Vec<OsString>,
18}
19
20/// Persistent updater configuration shared by all release sources.
21///
22/// This struct is typically assembled once during application startup and then
23/// passed into [`crate::UpdaterBuilder`]. It controls endpoint defaults,
24/// Minisign verification, and a few escape hatches for local development or
25/// unusual networking environments.
26#[derive(Debug, Clone, Default)]
27pub struct Config {
28    /// Allows non-HTTPS update endpoints. Intended for development only.
29    pub dangerous_insecure_transport_protocol: bool,
30    /// Allows invalid TLS certificates during HTTP requests.
31    pub dangerous_accept_invalid_certs: bool,
32    /// Allows TLS hostname mismatches during HTTP requests.
33    pub dangerous_accept_invalid_hostnames: bool,
34    /// Default endpoint list used when no custom release source is provided.
35    pub endpoints: Vec<Url>,
36    /// Minisign public key used to verify downloaded artifacts.
37    pub pubkey: String,
38    /// Optional Windows-specific installer configuration.
39    pub windows: Option<WindowsConfig>,
40}
41
42impl Config {
43    /// Validates the configuration invariants enforced by this crate.
44    ///
45    /// In particular, endpoint URLs must use `https` unless
46    /// [`Self::dangerous_insecure_transport_protocol`] is enabled.
47    pub fn validate(&self) -> crate::Result<()> {
48        validate_endpoints(&self.endpoints, self.dangerous_insecure_transport_protocol)
49    }
50}
51
52fn deserialize_os_string<'de, D>(deserializer: D) -> Result<Vec<OsString>, D::Error>
53where
54    D: Deserializer<'de>,
55{
56    Ok(Vec::<String>::deserialize(deserializer)?
57        .into_iter()
58        .map(OsString::from)
59        .collect())
60}
61
62impl<'de> Deserialize<'de> for Config {
63    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
64    where
65        D: Deserializer<'de>,
66    {
67        #[derive(Deserialize)]
68        #[serde(rename_all = "camelCase")]
69        struct InnerConfig {
70            #[serde(default, alias = "dangerous-insecure-transport-protocol")]
71            dangerous_insecure_transport_protocol: bool,
72            #[serde(default, alias = "dangerous-accept-invalid-certs")]
73            dangerous_accept_invalid_certs: bool,
74            #[serde(default, alias = "dangerous-accept-invalid-hostnames")]
75            dangerous_accept_invalid_hostnames: bool,
76            #[serde(default)]
77            endpoints: Vec<Url>,
78            pubkey: String,
79            windows: Option<WindowsConfig>,
80        }
81
82        let config = InnerConfig::deserialize(deserializer)?;
83        validate_endpoints(
84            &config.endpoints,
85            config.dangerous_insecure_transport_protocol,
86        )
87        .map_err(DeError::custom)?;
88
89        Ok(Self {
90            dangerous_insecure_transport_protocol: config.dangerous_insecure_transport_protocol,
91            dangerous_accept_invalid_certs: config.dangerous_accept_invalid_certs,
92            dangerous_accept_invalid_hostnames: config.dangerous_accept_invalid_hostnames,
93            endpoints: config.endpoints,
94            pubkey: config.pubkey,
95            windows: config.windows,
96        })
97    }
98}
99
100pub(crate) fn validate_endpoints(
101    endpoints: &[Url],
102    dangerous_insecure_transport_protocol: bool,
103) -> crate::Result<()> {
104    if !dangerous_insecure_transport_protocol {
105        for url in endpoints {
106            if url.scheme() != "https" {
107                return Err(crate::Error::InsecureTransportProtocol);
108            }
109        }
110    }
111
112    Ok(())
113}