| File: VerifyCommand\VerifyCommandRunner.cs | Web Access |
| Project: src\nuget-client\src\NuGet.Core\NuGet.Commands\NuGet.Commands.csproj (NuGet.Commands) |
// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. #nullable disable using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using NuGet.Common; using NuGet.Packaging; using NuGet.Packaging.Signing; using NuGet.Protocol; using static NuGet.Commands.VerifyArgs; namespace NuGet.Commands { /// <summary> /// Command Runner used to run the business logic for nuget verify command /// </summary> public class VerifyCommandRunner : IVerifyCommandRunner { private const int SuccessCode = 0; private const int FailureCode = 1; private const HashAlgorithmName _defaultFingerprintAlgorithm = HashAlgorithmName.SHA256; public async Task<int> ExecuteCommandAsync(VerifyArgs verifyArgs) { if (verifyArgs.Verifications.Count == 0) { verifyArgs.Logger.LogError(string.Format(CultureInfo.CurrentCulture, Strings.VerifyCommand_VerificationTypeNotSupported)); return FailureCode; } var errorCount = 0; if (ShouldExecuteVerification(verifyArgs, Verification.Signatures)) { if (!IsSignatureVerifyCommandSupported()) { verifyArgs.Logger.LogError(string.Format(CultureInfo.CurrentCulture, Strings.VerifyCommand_NotSupported)); return FailureCode; } var packagesToVerify = verifyArgs.PackagePaths.SelectMany(packagePath => { var packages = LocalFolderUtility.ResolvePackageFromPath(packagePath); LocalFolderUtility.EnsurePackageFileExists(packagePath, packages); return packages; }); ClientPolicyContext clientPolicyContext = ClientPolicyContext.GetClientPolicy(verifyArgs.Settings, verifyArgs.Logger); // List of values passed through --certificate-fingerprint option read var allowListEntries = verifyArgs.CertificateFingerprint.Select(fingerprint => new CertificateHashAllowListEntry( VerificationTarget.Author | VerificationTarget.Repository, SignaturePlacement.PrimarySignature, fingerprint, _defaultFingerprintAlgorithm)).ToList(); var verifierSettings = SignedPackageVerifierSettings.GetVerifyCommandDefaultPolicy(); var verificationProviders = new List<ISignatureVerificationProvider>() { new IntegrityVerificationProvider() }; // trustedSigners section >> Owners are considered here. verificationProviders.Add( new AllowListVerificationProvider( clientPolicyContext.AllowList, requireNonEmptyAllowList: clientPolicyContext.Policy == SignatureValidationMode.Require, emptyListErrorMessage: Strings.Error_NoClientAllowList, noMatchErrorMessage: Strings.Error_NoMatchingClientCertificate)); IEnumerable<KeyValuePair<string, HashAlgorithmName>> trustedSignerAllowUntrustedRootList = clientPolicyContext.AllowList? .Where(c => c.AllowUntrustedRoot) .Select(c => new KeyValuePair<string, HashAlgorithmName>(c.Fingerprint, c.FingerprintAlgorithm)); // trustedSigners section >> allowUntrustedRoot set true are considered here. verificationProviders.Add(new SignatureTrustAndValidityVerificationProvider(trustedSignerAllowUntrustedRootList)); // List of values passed through --certificate-fingerprint option are considered here. verificationProviders.Add( new AllowListVerificationProvider( allowListEntries, requireNonEmptyAllowList: false, noMatchErrorMessage: Strings.Error_NoMatchingCertificate)); var verifier = new PackageSignatureVerifier(verificationProviders); foreach (var package in packagesToVerify) { try { errorCount += await VerifySignatureForPackageAsync(package, verifyArgs.Logger, verifier, verifierSettings); } catch (InvalidDataException e) { verifyArgs.Logger.LogError(string.Format(CultureInfo.CurrentCulture, Strings.VerifyCommand_PackageIsNotValid, package)); ExceptionUtilities.LogException(e, verifyArgs.Logger); } } } return errorCount == 0 ? SuccessCode : FailureCode; } private async Task<int> VerifySignatureForPackageAsync(string packagePath, ILogger logger, PackageSignatureVerifier verifier, SignedPackageVerifierSettings verifierSettings) { var result = 0; using (var packageReader = new PackageArchiveReader(packagePath)) { var verificationResult = await verifier.VerifySignaturesAsync(packageReader, verifierSettings, CancellationToken.None); var packageIdentity = packageReader.GetIdentity(); logger.LogMinimal(Environment.NewLine + string.Format(CultureInfo.CurrentCulture, Strings.VerifyCommand_VerifyingPackage, packageIdentity.ToString())); string contentHash = packageReader.GetContentHash(CancellationToken.None); logger.LogMinimal(string.Format(CultureInfo.CurrentCulture, Strings.VerifyCommand_ContentHash, contentHash)); logger.LogInformation($"{packagePath}{Environment.NewLine}"); var logMessages = verificationResult.Results.SelectMany(p => p.Issues).ToList(); //log issues first await logger.LogMessagesAsync(logMessages.Where(m => m.Level < LogLevel.Warning)); if (logMessages.Any(m => m.Level >= LogLevel.Warning)) { logger.LogMinimal(Environment.NewLine); IEnumerable<SignatureLog> warnsanderrors = logMessages.Where(m => m.Level >= LogLevel.Warning); var errors = warnsanderrors.Count(m => m.Level == LogLevel.Error); var warnings = warnsanderrors.Count() - errors; logger.LogInformation(string.Format(CultureInfo.CurrentCulture, Strings.VerifyCommand_FinishedWithErrors, errors, warnings)); // log warnigs and errors at the end await logger.LogMessagesAsync(warnsanderrors); result = errors; } if (verificationResult.IsValid) { logger.LogInformation(Environment.NewLine + string.Format(CultureInfo.CurrentCulture, Strings.VerifyCommand_Success, packageIdentity.ToString())); } else { logger.LogMinimal(Environment.NewLine + Strings.VerifyCommand_Failed); } return result; } } private bool ShouldExecuteVerification(VerifyArgs args, Verification v) { return args.Verifications.Any(verification => verification == Verification.All || verification == v); } private bool IsSignatureVerifyCommandSupported() { #if IS_DESKTOP if (RuntimeEnvironmentHelper.IsMono) { return false; } #endif return true; } } }