Skip to content

Commit

Permalink
(#1144) Implement usePackageHashValidation feature
Browse files Browse the repository at this point in the history
This adds a check to both install and upgrade to validate that the
downloaded .nupkg file has the same hash as the source metadata.
The check is conducted before the package is installed. If the
source does not provide a sha512 checksum or if
usePackageHashValidation is disabled, then the check is skipped.

Only sha512 is supported because it is the only provided package
hash type after download. The provided hash is used because it
accounts for package signing correctly.
  • Loading branch information
TheCakeIsNaOH authored and gep13 committed Apr 26, 2024
1 parent 3e7fdca commit 1f9ffd3
Showing 1 changed file with 49 additions and 3 deletions.
52 changes: 49 additions & 3 deletions src/chocolatey/infrastructure.app/services/NugetService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@
using chocolatey.infrastructure.adapters;
using chocolatey.infrastructure.app.utility;
using chocolatey.infrastructure.commandline;
using chocolatey.infrastructure.cryptography;
using chocolatey.infrastructure.app.configuration;
using chocolatey.infrastructure.app.domain;
using chocolatey.infrastructure.guards;
using chocolatey.infrastructure.logging;
using chocolatey.infrastructure.app.nuget;
using chocolatey.infrastructure.platforms;
using chocolatey.infrastructure.results;
using chocolatey.infrastructure.services;
using chocolatey.infrastructure.tolerance;
using DateTime = chocolatey.infrastructure.adapters.DateTime;
using Environment = System.Environment;
Expand All @@ -47,7 +49,8 @@
using NuGet.Protocol.Core.Types;
using NuGet.Resolver;
using NuGet.Versioning;
using chocolatey.infrastructure.services;
using Newtonsoft.Json.Bson;
using chocolatey.infrastructure.configuration;

namespace chocolatey.infrastructure.app.services
{
Expand Down Expand Up @@ -842,7 +845,7 @@ Version was specified as '{0}'. It is possible that version
NuGetEnvironment.GetFolderPath(NuGetFolderPath.Temp),
_nugetLogger, CancellationToken.None).GetAwaiter().GetResult())
{
//TODO, do check on downloadResult
ValidatePackageHash(config, packageDependencyInfo, downloadResult);

nugetProject.InstallPackageAsync(
packageDependencyInfo,
Expand Down Expand Up @@ -1596,7 +1599,7 @@ public virtual ConcurrentDictionary<string, PackageResult> Upgrade(ChocolateyCon
NuGetEnvironment.GetFolderPath(NuGetFolderPath.Temp),
_nugetLogger, CancellationToken.None).GetAwaiter().GetResult())
{
//TODO, do check on downloadResult
ValidatePackageHash(config, packageDependencyInfo, downloadResult);

nugetProject.InstallPackageAsync(
packageDependencyInfo,
Expand Down Expand Up @@ -2971,6 +2974,49 @@ private void SetRemotePackageNamesIfAllSpecified(ChocolateyConfiguration config,
}
}

private void ValidatePackageHash(ChocolateyConfiguration config, SourcePackageDependencyInfo packageDependencyInfo, DownloadResourceResult downloadResult)
{
if (!config.Features.UsePackageHashValidation)
{
this.Log().Debug("Skipping package hash validation as feature '{0}' is not enabled.".FormatWith(ApplicationParameters.Features.UsePackageHashValidation));
}
else if (packageDependencyInfo.PackageHash is null)
{
// Folder based sources and v3 api based sources do not provide package hashes when getting metadata
this.Log().Debug("Source does not provide a package hash, skipping package hash validation.");
}
else
{
var hashInfo = HashConverter.ConvertHashToHex(packageDependencyInfo.PackageHash);

if (hashInfo.hashType == CryptoHashProviderType.Sha512)
{
using (var metadataFileStream =
downloadResult.PackageReader.GetStream(PackagingCoreConstants.NupkgMetadataFileExtension))
{
var metadataFileContents = NupkgMetadataFileFormat.Read(metadataFileStream, _nugetLogger,
PackagingCoreConstants.NupkgMetadataFileExtension);
if (hashInfo.convertedHash.Equals(metadataFileContents.ContentHash, StringComparison.OrdinalIgnoreCase))
{
this.Log().Debug("Package hash matches expected hash.");
}
else
{
var errorMessage =
"Package hash '{0}' did not match expected hash '{1}'."
.FormatWith(metadataFileContents.ContentHash,
hashInfo.convertedHash);
throw new InvalidDataException(errorMessage);
}
}
}
else
{
this.Log().Warn("Source is not providing a SHA512 hash, cannot validate package hash.");
}
}
}

#pragma warning disable IDE0022, IDE1006
[Obsolete("This overload is deprecated and will be removed in v3.")]
public void ensure_source_app_installed(ChocolateyConfiguration config, Action<PackageResult, ChocolateyConfiguration> ensureAction)
Expand Down

0 comments on commit 1f9ffd3

Please sign in to comment.