microsoft / microsoft/winget-cli
WinGet download hash mismatch cache issue
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 26.4k
- Forks
- 1.8k
- Avg merge
- 1d 11h
- Merged PRs (30d)
- 15
Description
Brief description of your issue
When running a winget download command, if there is a hash mismatch between the manifest's installer SHA256 and the downloaded file’s SHA256, the 'temporary' downloaded installer file (which has a GUID as its name with no extension) remains in the temp directory.
Ex: C:\Users\USERNAME\AppData\Local\Temp\WinGet\Corsair.iCUE.5.5.23.96\ec9d3ed6c05a94dd3e551f6a170136a3e931f659c64bac82885cd88a05d1656c
Note: I was able to resolve the issue by modifying VerifyInstallerHash() inside DownloadFlow.cpp to call RemoveInstallerFile on the file's path during hash failures. Here's my solution. Even though I'm sure y'all will find a more elegant solution, I wanted to give you an idea of how to fix it.
void VerifyInstallerHash(Execution::Context& context)
{
const auto& [expectedHash, downloadResult] = context.Get<Execution::Data::DownloadHashInfo>();
if (!std::equal(
expectedHash.begin(),
expectedHash.end(),
downloadResult.Sha256Hash.begin()))
{
bool overrideHashMismatch = context.Args.Contains(Execution::Args::Type::HashOverride);
const auto& manifest = context.Get<Execution::Data::Manifest>();
Logging::Telemetry().LogInstallerHashMismatch(manifest.Id, manifest.Version, manifest.Channel, expectedHash, downloadResult.Sha256Hash, overrideHashMismatch, downloadResult.SizeInBytes, downloadResult.ContentType);
auto installerPath = GetInstallerBaseDownloadPath(context);
auto installerFilename = GetInstallerPreHashValidationFileName(context);
std::filesystem::path failedInstallerLocation = installerPath / installerFilename;
if (downloadResult.SizeInBytes == 0)
{
context.Reporter.Error() << Resource::String::InstallerZeroByteFile << std::endl;
AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALLER_ZERO_BYTE_FILE);
}
// If running as admin, do not allow the user to override the hash failure.
if (Runtime::IsRunningAsAdmin())
{
context.Reporter.Error() << Resource::String::InstallerHashMismatchAdminBlock << std::endl;
RemoveInstallerFile(failedInstallerLocation);
}
else if (!Settings::IsAdminSettingEnabled(Settings::BoolAdminSetting::InstallerHashOverride))
{
context.Reporter.Error() << Resource::String::InstallerHashMismatchError << std::endl;
RemoveInstallerFile(failedInstallerLocation);
}
else if (overrideHashMismatch)
{
context.Reporter.Warn() << Resource::String::InstallerHashMismatchOverridden << std::endl;
return;
}
else
{
context.Reporter.Error() << Resource::String::InstallerHashMismatchOverrideRequired << std::endl;
RemoveInstallerFile(failedInstallerLocation);
}
AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALLER_HASH_MISMATCH);
}
else
{
AICLI_LOG(CLI, Info, << "Installer hash verified");
context.Reporter.Info() << Resource::String::InstallerHashVerified << std::endl;
context.SetFlags(Execution::ContextFlag::InstallerHashMatched);
if (context.Contains(Execution::Data::PackageVersion) &&
context.Get<Execution::Data::PackageVersion>()->GetSource() &&
WI_IsFlagSet(context.Get<Execution::Data::PackageVersion>()->GetSource().GetDetails().TrustLevel, SourceTrustLevel::Trusted))
{
context.SetFlags(Execution::ContextFlag::InstallerTrusted);
}
}
}
Steps to reproduce
Run a winget download for a package that has a hash mismatch. Currently, the package Corsair.iCUE.5.5.23.96 has a hash mismatch so I'd recommend using that package to test this.
Here is the command I ran exactly:
winget download --id "Corsair.iCUE.5" --source "winget"
Expected behavior
The "temporary" installer file should be deleted on exit of program.
Actual behavior
The "temporary" installer file stays permanently until manually cleaned by user.
Environment
Windows Package Manager v1.10.340
Copyright (c) Microsoft Corporation. All rights reserved.
Windows: Windows.Desktop v10.0.26100.3476
System Architecture: X64
Package: Microsoft.DesktopAppInstaller v1.25.340.0
Winget Directories
-----------------------------------------------------------------------------------------------------------------------
Logs %LOCALAPPDATA%\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\Diag…
User Settings %LOCALAPPDATA%\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\sett…
Portable Links Directory (User) %LOCALAPPDATA%\Microsoft\WinGet\Links
Portable Links Directory (Machine) C:\Program Files\WinGet\Links
Portable Package Root (User) %LOCALAPPDATA%\Microsoft\WinGet\Packages
Portable Package Root C:\Program Files\WinGet\Packages
Portable Package Root (x86) C:\Program Files (x86)\WinGet\Packages
Installer Downloads %USERPROFILE%\Downloads
Configuration Modules %LOCALAPPDATA%\Microsoft\WinGet\Configuration\Modules
Links
---------------------------------------------------------------------------
Privacy Statement https://aka.ms/winget-privacy
License Agreement https://aka.ms/winget-license
Third Party Notices https://aka.ms/winget-3rdPartyNotice
Homepage https://aka.ms/winget
Windows Store Terms https://www.microsoft.com/en-us/storedocs/terms-of-sale
Admin Setting State
--------------------------------------------------
LocalManifestFiles Disabled
BypassCertificatePinningForMicrosoftStore Disabled
InstallerHashOverride Disabled
LocalArchiveMalwareScanOverride Disabled
ProxyCommandLineOptions Disabled
DefaultProxy Disabled
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in DownloadFlow.cpp at VerifyInstallerHash() and inspect how the failed installer path is handled after a hash mismatch. Reproduce the issue with winget download --id "Corsair.iCUE.5" --source "winget", then verify that the temporary GUID-named installer file is removed when the command exits.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- cli, operating-systems
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100