Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 58 additions & 58 deletions src/Languages/Data/TranslatedPercentages.json
Original file line number Diff line number Diff line change
@@ -1,61 +1,61 @@
{
"af": "100%",
"ar": "100%",
"be": "100%",
"bg": "100%",
"bn": "100%",
"da": "100%",
"el": "100%",
"eo": "100%",
"es": "100%",
"es-MX": "100%",
"et": "100%",
"fa": "100%",
"fil": "100%",
"gl": "100%",
"gu": "100%",
"he": "100%",
"hi": "100%",
"hr": "100%",
"hu": "100%",
"id": "100%",
"ja": "100%",
"ka": "100%",
"kn": "100%",
"ko": "100%",
"ku": "100%",
"lt": "100%",
"mk": "100%",
"mr": "100%",
"nb": "100%",
"nn": "100%",
"pt_PT": "100%",
"ru": "100%",
"sa": "100%",
"si": "100%",
"sk": "100%",
"sl": "100%",
"sr": "100%",
"ta": "100%",
"tl": "100%",
"th": "100%",
"tr": "100%",
"uk": "100%",
"ur": "100%",
"ca": "100%",
"cs": "100%",
"de": "100%",
"fi": "100%",
"fr": "100%",
"it": "100%",
"nl": "100%",
"pl": "100%",
"pt_BR": "100%",
"ro": "100%",
"sq": "100%",
"sv": "100%",
"vi": "100%",
"zh_CN": "100%",
"zh_TW": "100%",
"af": "99%",
"ar": "99%",
"be": "99%",
"bg": "99%",
"bn": "99%",
"da": "99%",
"el": "99%",
"eo": "99%",
"es": "99%",
"es-MX": "99%",
"et": "99%",
"fa": "99%",
"fil": "99%",
"gl": "99%",
"gu": "99%",
"he": "99%",
"hi": "99%",
"hr": "99%",
"hu": "99%",
"id": "99%",
"ja": "99%",
"ka": "99%",
"kn": "99%",
"ko": "99%",
"ku": "99%",
"lt": "99%",
"mk": "99%",
"mr": "99%",
"nb": "99%",
"nn": "99%",
"pt_PT": "99%",
"ru": "99%",
"sa": "99%",
"si": "99%",
"sk": "99%",
"sl": "99%",
"sr": "99%",
"ta": "99%",
"tl": "99%",
"th": "99%",
"tr": "99%",
"uk": "99%",
"ur": "99%",
"ca": "99%",
"cs": "99%",
"de": "99%",
"fi": "99%",
"fr": "99%",
"it": "99%",
"nl": "99%",
"pl": "99%",
"pt_BR": "99%",
"ro": "99%",
"sq": "99%",
"sv": "99%",
"vi": "99%",
"zh_CN": "99%",
"zh_TW": "99%",
"en": "100%"
}
4 changes: 3 additions & 1 deletion src/Languages/lang_en.json
Original file line number Diff line number Diff line change
Expand Up @@ -808,5 +808,7 @@
"Error": "Error",
"Log in failed: ": "Log in failed: ",
"Log out failed: ": "Log out failed: ",
"Package backup settings": "Package backup settings"
"Package backup settings": "Package backup settings",
"Warn me when the installer URL host changes between the installed version and the new version (WinGet only)": "Warn me when the installer URL host changes between the installed version and the new version (WinGet only)",
"Installer host changed since the installed version.\nOld: {0}\nNew: {1}\n\nThis is usually harmless (the publisher moved hosting), but can also indicate a hijacked package manifest. Verify the new source before upgrading.": "Installer host changed since the installed version.\nOld: {0}\nNew: {1}\n\nThis is usually harmless (the publisher moved hosting), but can also indicate a hijacked package manifest. Verify the new source before upgrading."
}
87 changes: 87 additions & 0 deletions src/UniGetUI.Avalonia/Models/PackageCollections.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@
using Avalonia.Media.Imaging;
using Avalonia.Threading;
using UniGetUI.Avalonia.ViewModels.Pages;
using UniGetUI.Core.Logging;
using UniGetUI.Core.SettingsEngine;
using UniGetUI.Core.Tools;
using UniGetUI.Interface.Enums;
using UniGetUI.PackageEngine.Interfaces;
#if WINDOWS
using UniGetUI.PackageEngine.Managers.WingetManager;
#endif

// ReSharper disable once CheckNamespace
namespace UniGetUI.PackageEngine.PackageClasses;
Expand Down Expand Up @@ -64,6 +68,11 @@
public string TagIconPath { get; private set; } = "";
public bool TagIconVisible { get; private set; }

public bool InstallerHostChanged { get; private set; }
public string InstallerHostChangeTooltip { get; private set; } = "";

private CancellationTokenSource? _installerHostCheckCts;

public string SourceIconPath => IconTypeToSvgPath(Package.Source.IconId);

private static string IconTypeToSvgPath(IconType icon)
Expand Down Expand Up @@ -94,6 +103,81 @@

if (!Settings.Get(Settings.K.DisableIconsOnPackageLists))
_ = LoadIconAsync();

MaybeStartInstallerHostCheck();
}

/// <summary>
/// For upgradable WinGet packages, asynchronously fetches the installer URL host for
/// both the installed and the new version, and flags the row when the hosts differ.
/// See issue #4617 — defense-in-depth signal that an upgrade may be redirecting the
/// download to a different domain than the user originally trusted.
/// </summary>
private void MaybeStartInstallerHostCheck()

Check warning on line 116 in src/UniGetUI.Avalonia/Models/PackageCollections.cs

View workflow job for this annotation

GitHub Actions / Linux (Avalonia)

Member 'MaybeStartInstallerHostCheck' does not access instance data and can be marked as static (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1822)
{
#if WINDOWS
if (!Package.IsUpgradable) return;
if (Package.Manager is not WinGet) return;
if (Settings.Get(Settings.K.DisableInstallerHostChangeWarning)) return;

string installedVersion = Package.VersionString;
string newVersion = Package.NewVersionString;
if (string.IsNullOrWhiteSpace(installedVersion) || string.IsNullOrWhiteSpace(newVersion))
return;
if (installedVersion == newVersion) return;

_installerHostCheckCts?.Cancel();
_installerHostCheckCts = new CancellationTokenSource();
CancellationToken token = _installerHostCheckCts.Token;

_ = Task.Run(async () =>
{
try
{
if (token.IsCancellationRequested) return;
var oldHosts = WinGet.TryGetInstallerHostsForVersion(Package, installedVersion);
if (token.IsCancellationRequested) return;
var newHosts = WinGet.TryGetInstallerHostsForVersion(Package, newVersion);
if (token.IsCancellationRequested) return;

if (oldHosts is null || newHosts is null) return;
// Only flag when the two host sets are fully disjoint. If they share even
// one host, the publisher hasn't moved hosting — adding/removing CDN mirrors
// or architectures shouldn't trigger the warning.
if (oldHosts.Overlaps(newHosts)) return;

string tooltip = CoreTools.Translate(
"Installer host changed since the installed version.\n"
+ "Old: {0}\n"
+ "New: {1}\n\n"
+ "This is usually harmless (the publisher moved hosting), "
+ "but can also indicate a hijacked package manifest. "
+ "Verify the new source before upgrading.",
string.Join(", ", oldHosts),
string.Join(", ", newHosts)
);

await Dispatcher.UIThread.InvokeAsync(() =>
{
if (token.IsCancellationRequested) return;
InstallerHostChanged = true;
InstallerHostChangeTooltip = tooltip;
PropertyChanged?.Invoke(
this,
new PropertyChangedEventArgs(nameof(InstallerHostChanged))
);
PropertyChanged?.Invoke(
this,
new PropertyChangedEventArgs(nameof(InstallerHostChangeTooltip))
);
});
}
catch (Exception ex)
{
Logger.Warn($"Installer-host check failed for {Package.Id}: {ex.Message}");
}
}, token);
#endif
}

private async Task LoadIconAsync()
Expand Down Expand Up @@ -187,6 +271,9 @@
public void Dispose()
{
Package.PropertyChanged -= Package_PropertyChanged;
_installerHostCheckCts?.Cancel();
_installerHostCheckCts?.Dispose();
_installerHostCheckCts = null;
}
}

Expand Down Expand Up @@ -235,7 +322,7 @@
? items.OrderBy(GetSortKey, StringComparer.OrdinalIgnoreCase)
: items.OrderByDescending(GetSortKey, StringComparer.OrdinalIgnoreCase);

private string GetSortKey(PackageWrapper w) => CurrentSorter switch

Check warning on line 325 in src/UniGetUI.Avalonia/Models/PackageCollections.cs

View workflow job for this annotation

GitHub Actions / test-codebase

Possible null reference return.

Check warning on line 325 in src/UniGetUI.Avalonia/Models/PackageCollections.cs

View workflow job for this annotation

GitHub Actions / Linux (Avalonia)

Possible null reference return.
{
Sorter.Checked => w.IsChecked ? "0" : "1",
Sorter.Name => w.Package.Name,
Expand Down
5 changes: 5 additions & 0 deletions src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Updates.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@
BorderThickness="1,0,1,1"
CornerRadius="0,0,8,8"/>

<settings:CheckboxCard SettingName="DisableInstallerHostChangeWarning"
Text="{t:Translate Text='Warn me when the installer URL host changes between the installed version and the new version (WinGet only)'}"
CornerRadius="8"
Margin="0,8,0,0"/>

<ContentControl x:Name="ReleaseDateCompatTableHolder"
Margin="0,8,0,0"/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,13 @@
<DataTemplate x:DataType="pkg:PackageWrapper">
<StackPanel Orientation="Horizontal" Spacing="6" VerticalAlignment="Center" Margin="4,0">
<controls:SvgIcon Path="avares://UniGetUI.Avalonia/Assets/Symbols/download.svg"
Width="16" Height="16" VerticalAlignment="Center"/>
Width="16" Height="16" VerticalAlignment="Center"
IsVisible="{Binding !InstallerHostChanged}"/>
<controls:SvgIcon Path="avares://UniGetUI.Avalonia/Assets/Symbols/warning_filled.svg"
Width="16" Height="16" VerticalAlignment="Center"
Foreground="#F59E0B"
IsVisible="{Binding InstallerHostChanged}"
ToolTip.Tip="{Binding InstallerHostChangeTooltip}"/>
<TextBlock Text="{Binding Package.NewVersionString}" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
Expand Down Expand Up @@ -528,6 +534,13 @@
IsVisible="{Binding TagIconVisible}"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"/>
<controls:SvgIcon Path="avares://UniGetUI.Avalonia/Assets/Symbols/warning_filled.svg"
Width="14" Height="14"
Foreground="#F59E0B"
IsVisible="{Binding InstallerHostChanged}"
ToolTip.Tip="{Binding InstallerHostChangeTooltip}"
HorizontalAlignment="Right"
VerticalAlignment="Top"/>
</Panel>
<StackPanel Grid.Column="1" VerticalAlignment="Center" Spacing="1">
<TextBlock Text="{Binding Package.Name}"
Expand Down Expand Up @@ -604,6 +617,13 @@
IsVisible="{Binding TagIconVisible}"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"/>
<controls:SvgIcon Path="avares://UniGetUI.Avalonia/Assets/Symbols/warning_filled.svg"
Width="18" Height="18"
Foreground="#F59E0B"
IsVisible="{Binding InstallerHostChanged}"
ToolTip.Tip="{Binding InstallerHostChangeTooltip}"
HorizontalAlignment="Right"
VerticalAlignment="Top"/>
</Panel>
<TextBlock Grid.Row="2"
Text="{Binding Package.Name}"
Expand Down
2 changes: 2 additions & 0 deletions src/UniGetUI.Core.Settings/SettingsEngine_Names.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ public enum K
WinGetCliToolPreference,
WinGetComApiPolicy,
DisableClassicMode,
DisableInstallerHostChangeWarning,

Test1,
Test2,
Expand Down Expand Up @@ -191,6 +192,7 @@ public static string ResolveKey(K key)
K.WinGetCliToolPreference => "WinGetCliToolPreference",
K.WinGetComApiPolicy => "WinGetComApiPolicy",
K.DisableClassicMode => "DisableClassicMode",
K.DisableInstallerHostChangeWarning => "DisableInstallerHostChangeWarning",

K.Test1 => "TestSetting1",
K.Test2 => "TestSetting2",
Expand Down
Loading
Loading