diff --git a/src/chocolatey/infrastructure.app/services/NugetService.cs b/src/chocolatey/infrastructure.app/services/NugetService.cs index 8a6054dc02..7a97d562d8 100644 --- a/src/chocolatey/infrastructure.app/services/NugetService.cs +++ b/src/chocolatey/infrastructure.app/services/NugetService.cs @@ -26,6 +26,7 @@ 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; @@ -33,6 +34,7 @@ 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; @@ -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 { @@ -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, @@ -1596,7 +1599,7 @@ public virtual ConcurrentDictionary Upgrade(ChocolateyCon NuGetEnvironment.GetFolderPath(NuGetFolderPath.Temp), _nugetLogger, CancellationToken.None).GetAwaiter().GetResult()) { - //TODO, do check on downloadResult + ValidatePackageHash(config, packageDependencyInfo, downloadResult); nugetProject.InstallPackageAsync( packageDependencyInfo, @@ -2971,6 +2974,50 @@ 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); + var metadataFileHashInfo = HashConverter.ConvertHashToHex(metadataFileContents.ContentHash); + if (hashInfo.convertedHash.Equals(metadataFileHashInfo.convertedHash, 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 ensureAction)