From 7c19f379683b50d3066f876332bd684a45a66bf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Wed, 18 Mar 2026 11:42:21 -0400 Subject: [PATCH] Harden auto updater source selection --- .../Infrastructure/AvaloniaAutoUpdater.cs | 123 ++++++------- src/UniGetUI/AutoUpdater.cs | 163 +++++++----------- testing/UPDATE-TESTING.md | 81 +++++---- 3 files changed, 156 insertions(+), 211 deletions(-) diff --git a/src/UniGetUI.Avalonia/Infrastructure/AvaloniaAutoUpdater.cs b/src/UniGetUI.Avalonia/Infrastructure/AvaloniaAutoUpdater.cs index 9856d23da..ea2f2be19 100644 --- a/src/UniGetUI.Avalonia/Infrastructure/AvaloniaAutoUpdater.cs +++ b/src/UniGetUI.Avalonia/Infrastructure/AvaloniaAutoUpdater.cs @@ -33,16 +33,6 @@ internal static partial class AvaloniaAutoUpdater private const string REG_SKIP_HASH_VALIDATION = "UpdaterSkipHashValidation"; private const string REG_SKIP_SIGNER_THUMBPRINT_CHECK = "UpdaterSkipSignerThumbprintCheck"; private const string REG_DISABLE_TLS_VALIDATION = "UpdaterDisableTlsValidation"; - private const string REG_USE_LEGACY_GITHUB = "UpdaterUseLegacyGithub"; - - private const string STABLE_ENDPOINT = - "https://www.marticliment.com/versions/unigetui/stable.ver"; - private const string BETA_ENDPOINT = - "https://www.marticliment.com/versions/unigetui/beta.ver"; - private const string STABLE_INSTALLER_URL = - "https://github.com/Devolutions/UniGetUI/releases/latest/download/UniGetUI.Installer.exe"; - private const string BETA_INSTALLER_URL = - "https://github.com/Devolutions/UniGetUI/releases/download/$TAG/UniGetUI.Installer.exe"; private static readonly string[] DEVOLUTIONS_CERT_THUMBPRINTS = [ @@ -51,6 +41,17 @@ internal static partial class AvaloniaAutoUpdater "50f753333811ff11f1920274afde3ffd4468b210", ]; +#if !DEBUG + private static readonly string[] RELEASE_IGNORED_REGISTRY_VALUES = + [ + REG_PRODUCTINFO_KEY, + REG_ALLOW_UNSAFE_URLS, + REG_SKIP_HASH_VALIDATION, + REG_SKIP_SIGNER_THUMBPRINT_CHECK, + REG_DISABLE_TLS_VALIDATION, + ]; +#endif + private static readonly AutoUpdaterJsonContext _jsonContext = new( new JsonSerializerOptions(SerializationHelpers.DefaultOptions) ); @@ -245,21 +246,7 @@ private static async Task LaunchInstallerAndQuitAsync(string installerLocation) // ------------------------------------------------------------------ update check sources private static async Task GetUpdateCandidateAsync(UpdaterOverrides overrides) { - if (overrides.UseLegacyGithub) - { - return await CheckFromLegacyGitHubAsync(overrides); - } - - try - { - return await CheckFromProductInfoAsync(overrides); - } - catch (Exception ex) - { - Logger.Warn("ProductInfo source failed; falling back to legacy GitHub source."); - Logger.Warn(ex); - return await CheckFromLegacyGitHubAsync(overrides); - } + return await CheckFromProductInfoAsync(overrides); } private static async Task CheckFromProductInfoAsync(UpdaterOverrides overrides) @@ -329,40 +316,6 @@ private static async Task CheckFromProductInfoAsync(UpdaterOver return new UpdateCandidate(upgradable, channel.Version, installer.Hash, installer.Url, "ProductInfo"); } - private static async Task CheckFromLegacyGitHubAsync(UpdaterOverrides overrides) - { - bool useBeta = Settings.Get(Settings.K.EnableUniGetUIBeta); - string endpoint = useBeta ? BETA_ENDPOINT : STABLE_ENDPOINT; - string installerUrl = useBeta ? BETA_INSTALLER_URL : STABLE_INSTALLER_URL; - - Logger.Debug($"Checking updates via legacy GitHub endpoint: {endpoint}"); - - string[] parts; - using (HttpClient client = new(CreateHttpClientHandler(overrides))) - { - client.Timeout = TimeSpan.FromSeconds(600); - client.DefaultRequestHeaders.UserAgent.ParseAdd(CoreData.UserAgentString); - parts = (await client.GetStringAsync(endpoint)).Split("////"); - } - - if (parts.Length >= 3) - { - int latestBuild = int.Parse(parts[0].Trim()); - string hash = parts[1].Trim(); - string versionName = parts[2].Trim(); - - return new UpdateCandidate( - latestBuild > CoreData.BuildNumber, - versionName, - hash, - installerUrl.Replace("$TAG", versionName), - "LegacyGitHub" - ); - } - - throw new FormatException("Legacy update file does not follow the expected format."); - } - // ------------------------------------------------------------------ validation helpers private static async Task CheckInstallerHashAsync( string path, @@ -488,9 +441,7 @@ private static bool IsSourceUrlAllowed(string url, bool allowUnsafe) return uri.Host.EndsWith("devolutions.net", StringComparison.OrdinalIgnoreCase) || uri.Host.Equals("github.com", StringComparison.OrdinalIgnoreCase) || uri.Host.Equals("objects.githubusercontent.com", StringComparison.OrdinalIgnoreCase) - || uri.Host.Equals("release-assets.githubusercontent.com", StringComparison.OrdinalIgnoreCase) - || uri.Host.Equals("marticliment.com", StringComparison.OrdinalIgnoreCase) - || uri.Host.EndsWith("marticliment.com", StringComparison.OrdinalIgnoreCase); + || uri.Host.Equals("release-assets.githubusercontent.com", StringComparison.OrdinalIgnoreCase); } private static ProductInfoFile SelectInstallerFile(List files) @@ -531,11 +482,12 @@ private static string NormalizeThumbprint(string thumbprint) => private static UpdaterOverrides LoadUpdaterOverrides() { #pragma warning disable CA1416 - using RegistryKey? key = Registry.CurrentUser.OpenSubKey(REGISTRY_PATH); + using RegistryKey? key = Registry.LocalMachine.OpenSubKey(REGISTRY_PATH); +#if DEBUG if (key is not null) { - Logger.Info($"Updater registry overrides loaded from HKCU\\{REGISTRY_PATH}"); + Logger.Info($"Updater registry overrides loaded from HKLM\\{REGISTRY_PATH}"); } return new UpdaterOverrides( @@ -544,11 +496,45 @@ private static UpdaterOverrides LoadUpdaterOverrides() GetRegistryBool(key, REG_ALLOW_UNSAFE_URLS), GetRegistryBool(key, REG_SKIP_HASH_VALIDATION), GetRegistryBool(key, REG_SKIP_SIGNER_THUMBPRINT_CHECK), - GetRegistryBool(key, REG_DISABLE_TLS_VALIDATION), - GetRegistryBool(key, REG_USE_LEGACY_GITHUB) + GetRegistryBool(key, REG_DISABLE_TLS_VALIDATION) + ); +#else + LogIgnoredReleaseOverrides(key); + string productInfoUrl = GetRegistryString(key, REG_PRODUCTINFO_URL) ?? DEFAULT_PRODUCTINFO_URL; + + return new UpdaterOverrides( + productInfoUrl, + DEFAULT_PRODUCTINFO_KEY, + false, + false, + false, + false ); +#endif +#pragma warning restore CA1416 + } + +#if !DEBUG + private static void LogIgnoredReleaseOverrides(RegistryKey? key) + { +#pragma warning disable CA1416 + if (key is null) + { + return; + } + + foreach (string valueName in RELEASE_IGNORED_REGISTRY_VALUES) + { + if (key.GetValue(valueName) is not null) + { + Logger.Warn( + $"Release build is ignoring updater registry value HKLM\\{REGISTRY_PATH}\\{valueName}." + ); + } + } #pragma warning restore CA1416 } +#endif private static string? GetRegistryString(RegistryKey? key, string valueName) { @@ -558,6 +544,7 @@ private static UpdaterOverrides LoadUpdaterOverrides() return string.IsNullOrWhiteSpace(parsed) ? null : parsed.Trim(); } +#if DEBUG private static bool GetRegistryBool(RegistryKey? key, string valueName) { #pragma warning disable CA1416 @@ -572,6 +559,7 @@ private static bool GetRegistryBool(RegistryKey? key, string valueName) || s.Equals("yes", StringComparison.OrdinalIgnoreCase) || s.Equals("on", StringComparison.OrdinalIgnoreCase); } +#endif // ------------------------------------------------------------------ data types private sealed record UpdateCandidate( @@ -588,8 +576,7 @@ private sealed record UpdaterOverrides( bool AllowUnsafeUrls, bool SkipHashValidation, bool SkipSignerThumbprintCheck, - bool DisableTlsValidation, - bool UseLegacyGithub + bool DisableTlsValidation ); private sealed class ProductInfoProduct diff --git a/src/UniGetUI/AutoUpdater.cs b/src/UniGetUI/AutoUpdater.cs index 9b02284c2..c2204d4ce 100644 --- a/src/UniGetUI/AutoUpdater.cs +++ b/src/UniGetUI/AutoUpdater.cs @@ -30,7 +30,6 @@ public partial class AutoUpdater private const string REG_SKIP_HASH_VALIDATION = "UpdaterSkipHashValidation"; private const string REG_SKIP_SIGNER_THUMBPRINT_CHECK = "UpdaterSkipSignerThumbprintCheck"; private const string REG_DISABLE_TLS_VALIDATION = "UpdaterDisableTlsValidation"; - private const string REG_USE_LEGACY_GITHUB = "UpdaterUseLegacyGithub"; private static readonly string[] DEVOLUTIONS_CERT_THUMBPRINTS = [ @@ -39,6 +38,17 @@ public partial class AutoUpdater "50f753333811ff11f1920274afde3ffd4468b210", ]; +#if !DEBUG + private static readonly string[] RELEASE_IGNORED_REGISTRY_VALUES = + [ + REG_PRODUCTINFO_KEY, + REG_ALLOW_UNSAFE_URLS, + REG_SKIP_HASH_VALIDATION, + REG_SKIP_SIGNER_THUMBPRINT_CHECK, + REG_DISABLE_TLS_VALIDATION, + ]; +#endif + private static readonly AutoUpdaterJsonContext ProductInfoJsonContext = new( new JsonSerializerOptions(SerializationHelpers.DefaultOptions) ); @@ -46,15 +56,6 @@ public partial class AutoUpdater public static Window Window = null!; public static InfoBar Banner = null!; - //------------------------------------------------------------------------------------------------------------------ - private const string STABLE_ENDPOINT = - "https://www.marticliment.com/versions/unigetui/stable.ver"; - private const string BETA_ENDPOINT = "https://www.marticliment.com/versions/unigetui/beta.ver"; - private const string STABLE_INSTALLER_URL = - "https://github.com/Devolutions/UniGetUI/releases/latest/download/UniGetUI.Installer.exe"; - private const string BETA_INSTALLER_URL = - "https://github.com/Devolutions/UniGetUI/releases/download/$TAG/UniGetUI.Installer.exe"; - //------------------------------------------------------------------------------------------------------------------ public static bool ReleaseLockForAutoupdate_Notification; public static bool ReleaseLockForAutoupdate_Window; @@ -227,23 +228,7 @@ await CheckInstallerHash( private static async Task GetUpdateCandidate(UpdaterOverrides updaterOverrides) { - if (updaterOverrides.UseLegacyGithub) - { - return await CheckForUpdatesFromLegacyGitHub(updaterOverrides); - } - - try - { - return await CheckForUpdatesFromProductInfo(updaterOverrides); - } - catch (Exception ex) - { - Logger.Warn( - "Productinfo updater source failed. Falling back to legacy GitHub updater source." - ); - Logger.Warn(ex); - return await CheckForUpdatesFromLegacyGitHub(updaterOverrides); - } + return await CheckForUpdatesFromProductInfo(updaterOverrides); } /// @@ -336,56 +321,6 @@ out ProductInfoProduct? product ); } - /// - /// Legacy updater source. Kept for compatibility and manual fallback testing. - /// - private static async Task CheckForUpdatesFromLegacyGitHub( - UpdaterOverrides updaterOverrides - ) - { - string endpoint = Settings.Get(Settings.K.EnableUniGetUIBeta) - ? BETA_ENDPOINT - : STABLE_ENDPOINT; - string installerDownloadUrl = Settings.Get(Settings.K.EnableUniGetUIBeta) - ? BETA_INSTALLER_URL - : STABLE_INSTALLER_URL; - - Logger.Warn("Using legacy GitHub updater source due to registry override."); - Logger.Debug($"Begin check for updates on endpoint {endpoint}"); - - string[] updateResponse; - using (HttpClient client = new(CreateHttpClientHandler(updaterOverrides))) - { - client.Timeout = TimeSpan.FromSeconds(600); - client.DefaultRequestHeaders.UserAgent.ParseAdd(CoreData.UserAgentString); - updateResponse = (await client.GetStringAsync(endpoint)).Split("////"); - } - - if (updateResponse.Length >= 3) - { - int latestVersion = int.Parse( - updateResponse[0].Replace("\n", "").Replace("\r", "").Trim() - ); - string installerHash = updateResponse[1].Replace("\n", "").Replace("\r", "").Trim(); - string versionName = updateResponse[2].Replace("\n", "").Replace("\r", "").Trim(); - Logger.Debug( - $"Got response from endpoint: ({latestVersion}, {versionName}, {installerHash})" - ); - return new UpdateCandidate( - latestVersion > CoreData.BuildNumber, - versionName, - installerHash, - installerDownloadUrl.Replace("$TAG", versionName), - "LegacyGitHub" - ); - } - - Logger.Warn($"Received update string is {updateResponse[0]}"); - throw new FormatException( - "The updates file does not follow the FloatVersion////Sha256Hash////VersionName format" - ); - } - /// /// Checks whether the downloaded updater matches the hash. /// @@ -699,9 +634,7 @@ private static bool IsSourceUrlAllowed(string url, bool allowUnsafeUrls) || uri.Host.Equals( "release-assets.githubusercontent.com", StringComparison.OrdinalIgnoreCase - ) - || uri.Host.Equals("marticliment.com", StringComparison.OrdinalIgnoreCase) - || uri.Host.EndsWith("marticliment.com", StringComparison.OrdinalIgnoreCase); + ); } private static ProductInfoFile SelectInstallerFile(List files) @@ -769,38 +702,63 @@ private static string NormalizeThumbprint(string thumbprint) private static UpdaterOverrides LoadUpdaterOverrides() { - using RegistryKey? key = Registry.CurrentUser.OpenSubKey(REGISTRY_PATH); - - string productInfoUrl = - GetRegistryString(key, REG_PRODUCTINFO_URL) ?? DEFAULT_PRODUCTINFO_URL; - string productInfoProductKey = - GetRegistryString(key, REG_PRODUCTINFO_KEY) ?? DEFAULT_PRODUCTINFO_KEY; - - bool allowUnsafeUrls = GetRegistryBool(key, REG_ALLOW_UNSAFE_URLS); - bool skipHashValidation = GetRegistryBool(key, REG_SKIP_HASH_VALIDATION); - bool skipSignerThumbprintCheck = GetRegistryBool(key, REG_SKIP_SIGNER_THUMBPRINT_CHECK); - bool disableTlsValidation = GetRegistryBool(key, REG_DISABLE_TLS_VALIDATION); - bool useLegacyGithub = GetRegistryBool(key, REG_USE_LEGACY_GITHUB); + using RegistryKey? key = Registry.LocalMachine.OpenSubKey(REGISTRY_PATH); +#if DEBUG if (key is not null) { - Logger.Info($"Updater registry overrides loaded from HKCU\\{REGISTRY_PATH}"); + Logger.Info($"Updater registry overrides loaded from HKLM\\{REGISTRY_PATH}"); } + return new UpdaterOverrides( + GetRegistryString(key, REG_PRODUCTINFO_URL) ?? DEFAULT_PRODUCTINFO_URL, + GetRegistryString(key, REG_PRODUCTINFO_KEY) ?? DEFAULT_PRODUCTINFO_KEY, + GetRegistryBool(key, REG_ALLOW_UNSAFE_URLS), + GetRegistryBool(key, REG_SKIP_HASH_VALIDATION), + GetRegistryBool(key, REG_SKIP_SIGNER_THUMBPRINT_CHECK), + GetRegistryBool(key, REG_DISABLE_TLS_VALIDATION) + ); +#else + LogIgnoredReleaseOverrides(key); + string productInfoUrl = + GetRegistryString(key, REG_PRODUCTINFO_URL) ?? DEFAULT_PRODUCTINFO_URL; + return new UpdaterOverrides( productInfoUrl, - productInfoProductKey, - allowUnsafeUrls, - skipHashValidation, - skipSignerThumbprintCheck, - disableTlsValidation, - useLegacyGithub + DEFAULT_PRODUCTINFO_KEY, + false, + false, + false, + false ); +#endif + } + +#if !DEBUG + private static void LogIgnoredReleaseOverrides(RegistryKey? key) + { + if (key is null) + { + return; + } + + foreach (string valueName in RELEASE_IGNORED_REGISTRY_VALUES) + { + if (key.GetValue(valueName) is not null) + { + Logger.Warn( + $"Release build is ignoring updater registry value HKLM\\{REGISTRY_PATH}\\{valueName}." + ); + } + } } +#endif private static string? GetRegistryString(RegistryKey? key, string valueName) { +#pragma warning disable CA1416 object? value = key?.GetValue(valueName); +#pragma warning restore CA1416 if (value is null) { return null; @@ -815,9 +773,12 @@ private static UpdaterOverrides LoadUpdaterOverrides() return parsedValue.Trim(); } +#if DEBUG private static bool GetRegistryBool(RegistryKey? key, string valueName) { +#pragma warning disable CA1416 object? value = key?.GetValue(valueName); +#pragma warning restore CA1416 if (value is null) { return false; @@ -839,6 +800,7 @@ private static bool GetRegistryBool(RegistryKey? key, string valueName) || normalized.Equals("yes", StringComparison.OrdinalIgnoreCase) || normalized.Equals("on", StringComparison.OrdinalIgnoreCase); } +#endif private sealed record UpdateCandidate( bool IsUpgradable, @@ -854,8 +816,7 @@ private sealed record UpdaterOverrides( bool AllowUnsafeUrls, bool SkipHashValidation, bool SkipSignerThumbprintCheck, - bool DisableTlsValidation, - bool UseLegacyGithub + bool DisableTlsValidation ); private sealed class ProductInfoProduct diff --git a/testing/UPDATE-TESTING.md b/testing/UPDATE-TESTING.md index 265b2676d..51d753ede 100644 --- a/testing/UPDATE-TESTING.md +++ b/testing/UPDATE-TESTING.md @@ -1,8 +1,8 @@ -# UniGetUI Auto-Update Testing Guide (productinfo.json path) +# UniGetUI Auto-Update Testing Guide (ProductInfo-only path) -This guide validates the new default auto-update flow that reads from `productinfo.json`. +This guide validates the ProductInfo-only auto-update flow that reads from `productinfo.json`. -If `productinfo.json` lookup fails for any reason, UniGetUI now falls back to the legacy updater logic that uses the existing version endpoint and GitHub release download URL. +UniGetUI no longer falls back to the legacy updater logic. If the ProductInfo lookup fails, the update check fails. ## Files used @@ -15,11 +15,22 @@ If `productinfo.json` lookup fails for any reason, UniGetUI now falls back to th ## What is being tested -- Default updater source is productinfo-based. +- Default updater source is ProductInfo-based. - Product key lookup for `Devolutions.UniGetUI`. - Architecture-aware installer selection (`x64`/`arm64`, `exe` preferred). - Hash validation (enabled by default). -- Test-only override behavior via registry keys under `HKCU\Software\Devolutions\UniGetUI`. +- Debug-only override behavior via registry keys under `HKLM\Software\Devolutions\UniGetUI`. + +## Release vs Debug behavior + +- Release builds honor only `UpdaterProductInfoUrl` from `HKLM\Software\Devolutions\UniGetUI`. +- Release builds ignore `UpdaterProductKey` and all validation-bypass flags. +- Debug builds can read updater overrides from `HKLM\Software\Devolutions\UniGetUI` for local testing. +- Dangerous validation bypasses are for Debug/dev testing only: + - `UpdaterAllowUnsafeUrls` + - `UpdaterSkipHashValidation` + - `UpdaterSkipSignerThumbprintCheck` + - `UpdaterDisableTlsValidation` ## 1) Host the test files locally @@ -39,10 +50,12 @@ Make sure these URLs are reachable: ## 2) Configure updater overrides (test mode) +These steps apply to a Debug build only. + Run in PowerShell: ```powershell -$regPath = 'HKCU:\Software\Devolutions\UniGetUI' +$regPath = 'HKLM:\Software\Devolutions\UniGetUI' New-Item -Path $regPath -Force | Out-Null # Point updater to local productinfo @@ -60,9 +73,6 @@ Set-ItemProperty -Path $regPath -Name 'UpdaterSkipHashValidation' -Type DWord -V # Keep signer thumbprint validation enabled for normal test pass Set-ItemProperty -Path $regPath -Name 'UpdaterSkipSignerThumbprintCheck' -Type DWord -Value 0 -# Keep legacy path disabled (productinfo path is default) -Set-ItemProperty -Path $regPath -Name 'UpdaterUseLegacyGithub' -Type DWord -Value 0 - # Optional only for HTTPS cert troubleshooting in test environments Set-ItemProperty -Path $regPath -Name 'UpdaterDisableTlsValidation' -Type DWord -Value 0 ``` @@ -97,7 +107,7 @@ Expected result: Set: ```powershell -Set-ItemProperty -Path 'HKCU:\Software\Devolutions\UniGetUI' -Name 'UpdaterAllowUnsafeUrls' -Type DWord -Value 0 +Set-ItemProperty -Path 'HKLM:\Software\Devolutions\UniGetUI' -Name 'UpdaterAllowUnsafeUrls' -Type DWord -Value 0 ``` Expected result with local `http://127.0.0.1` URLs: @@ -105,70 +115,57 @@ Expected result with local `http://127.0.0.1` URLs: - Updater rejects source/download URL as unsafe. - No installer launch. -## 6) Optional: force legacy GitHub updater path - -```powershell -Set-ItemProperty -Path 'HKCU:\Software\Devolutions\UniGetUI' -Name 'UpdaterUseLegacyGithub' -Type DWord -Value 1 -``` - -Expected result: - -- Legacy endpoint/GitHub code path is used. -- Productinfo path is bypassed for that run. - -## 7) Fallback test: broken productinfo with successful legacy fallback +## 6) Negative test: broken ProductInfo source Use one of these methods: - Point `UpdaterProductInfoUrl` to a missing URL, or - Point `UpdaterProductInfoUrl` to a malformed JSON file, or -- Point `UpdaterProductKey` to a non-existent product. +- In a Debug build only, point `UpdaterProductKey` to a non-existent product. Example: ```powershell -Set-ItemProperty -Path 'HKCU:\Software\Devolutions\UniGetUI' -Name 'UpdaterProductInfoUrl' -Value 'http://127.0.0.1:8080/does-not-exist.json' -Set-ItemProperty -Path 'HKCU:\Software\Devolutions\UniGetUI' -Name 'UpdaterUseLegacyGithub' -Type DWord -Value 0 +Set-ItemProperty -Path 'HKLM:\Software\Devolutions\UniGetUI' -Name 'UpdaterProductInfoUrl' -Value 'http://127.0.0.1:8080/does-not-exist.json' ``` Expected result: - Productinfo check fails. -- UniGetUI logs that it is falling back to the legacy GitHub updater source. -- Legacy updater path is used automatically. -- If the legacy source has a newer version, the update flow continues normally. +- UniGetUI reports the update-check failure. +- No fallback source is used. -## 8) Fallback test: both sources fail +## 7) Optional: disable signer thumbprint check (test-only) -Use a broken productinfo override and also make the legacy source unavailable in your test environment. +Use this only if your local installer is unsigned or signed with a non-Devolutions certificate. -Expected result: +```powershell +Set-ItemProperty -Path 'HKLM:\Software\Devolutions\UniGetUI' -Name 'UpdaterSkipSignerThumbprintCheck' -Type DWord -Value 1 +``` -- Productinfo check fails first. -- UniGetUI attempts the legacy updater path. -- The updater shows the existing terminal error because neither source succeeded. +## 8) Release-build hardening check -## 9) Optional: disable signer thumbprint check (test-only) +Run the same registry override setup against a Release build. -Use this only if your local installer is unsigned or signed with a non-Devolutions certificate. +Expected result: -```powershell -Set-ItemProperty -Path 'HKCU:\Software\Devolutions\UniGetUI' -Name 'UpdaterSkipSignerThumbprintCheck' -Type DWord -Value 1 -``` +- Release build honors `UpdaterProductInfoUrl` only if it still passes normal source validation. +- Release build ignores `UpdaterProductKey`. +- Release build ignores validation bypass flags. +- Updater uses the configured ProductInfo URL and the built-in `Devolutions.UniGetUI` product key. -## 10) Cleanup after testing +## 9) Cleanup after testing Reset to default production behavior: ```powershell -$regPath = 'HKCU:\Software\Devolutions\UniGetUI' +$regPath = 'HKLM:\Software\Devolutions\UniGetUI' Remove-ItemProperty -Path $regPath -Name 'UpdaterProductInfoUrl' -ErrorAction SilentlyContinue Remove-ItemProperty -Path $regPath -Name 'UpdaterProductKey' -ErrorAction SilentlyContinue Remove-ItemProperty -Path $regPath -Name 'UpdaterAllowUnsafeUrls' -ErrorAction SilentlyContinue Remove-ItemProperty -Path $regPath -Name 'UpdaterSkipHashValidation' -ErrorAction SilentlyContinue Remove-ItemProperty -Path $regPath -Name 'UpdaterSkipSignerThumbprintCheck' -ErrorAction SilentlyContinue Remove-ItemProperty -Path $regPath -Name 'UpdaterDisableTlsValidation' -ErrorAction SilentlyContinue -Remove-ItemProperty -Path $regPath -Name 'UpdaterUseLegacyGithub' -ErrorAction SilentlyContinue ``` With all override values removed, UniGetUI uses: