private async Task SetValidationStatusAsync( PackageValidation packageValidation, INuGetValidationResponse validationResponse, DateTime now) { if (validationResponse.Status != ValidationStatus.Incomplete) { AddValidationIssues(packageValidation, validationResponse.Issues); } if (validationResponse.Status == ValidationStatus.Succeeded && validationResponse.NupkgUrl != null) { if (!_validatorProvider.IsNuGetProcessor(packageValidation.Type)) { throw new InvalidOperationException( $"The validator '{packageValidation.Type}' is not a processor but returned a .nupkg URL as " + $"part of the validation step response."); } await _packageFileService.CopyPackageUrlForValidationSetAsync( packageValidation.PackageValidationSet, validationResponse.NupkgUrl); } packageValidation.ValidationStatus = validationResponse.Status; packageValidation.ValidationStatusTimestamp = now; await _validationContext.SaveChangesAsync(); TrackValidationStatus(packageValidation); }
private INuGetValidationResponse Validate(INuGetValidationRequest request, INuGetValidationResponse response) { /// The package signature validator runs after the <see cref="PackageSignatureProcessor" />. /// All signature validation issues should be caught and handled by the processor. if (response.Status == ValidationStatus.Failed || response.NupkgUrl != null) { if (!_config.RepositorySigningEnabled) { _logger.LogInformation( "Ignoring invalid validation response in package signature validator as repository signing is disabled. " + "Status = {ValidationStatus}, Nupkg URL = {NupkgUrl}, validation issues = {Issues}", response.Status, response.NupkgUrl, response.Issues.Select(i => i.IssueCode)); return(NuGetValidationResponse.Succeeded); } _logger.LogCritical( "Unexpected validation response in package signature validator. This may be caused by an invalid repository " + "signature. Throwing an exception to force this validation to dead-letter. " + "Status = {ValidationStatus}, Nupkg URL = {NupkgUrl}, validation issues = {Issues}", response.Status, response.NupkgUrl, response.Issues.Select(i => i.IssueCode)); throw new InvalidOperationException("Package signature validator has an unexpected validation response"); } /// Suppress all validation issues. The <see cref="PackageSignatureProcessor"/> should /// have already reported any issues related to the author signature. Customers should /// not be notified of validation issues due to the repository signature. if (response.Issues.Count != 0) { _logger.LogWarning( "Ignoring {ValidationIssueCount} validation issues from response. Issues: {Issues}", response.Issues.Count, response.Issues.Select(i => i.IssueCode)); return(new NuGetValidationResponse(response.Status)); } return(response); }
public async Task MarkValidationStartedAsync(PackageValidation packageValidation, INuGetValidationResponse validationResponse) { packageValidation = packageValidation ?? throw new ArgumentNullException(nameof(packageValidation)); _logger.LogInformation("Marking validation {ValidationName} {ValidationId} {PackageId} {PackageVersion} as started with status {ValidationStatus}", packageValidation.Type, packageValidation.PackageValidationSet.ValidationTrackingId, packageValidation.PackageValidationSet.PackageId, packageValidation.PackageValidationSet.PackageNormalizedVersion, validationResponse.Status); if (validationResponse.Status == ValidationStatus.NotStarted) { throw new ArgumentOutOfRangeException( nameof(validationResponse), $"Cannot mark validation {packageValidation.Type} for " + $"{packageValidation.PackageValidationSet.PackageId} " + $"{packageValidation.PackageValidationSet.PackageNormalizedVersion} as started " + $"with status {ValidationStatus.NotStarted}"); } var now = DateTime.UtcNow; packageValidation.Started = now; await SetValidationStatusAsync(packageValidation, validationResponse, now); }
public async Task UpdateValidationStatusAsync(PackageValidation packageValidation, INuGetValidationResponse validationResponse) { packageValidation = packageValidation ?? throw new ArgumentNullException(nameof(packageValidation)); if (packageValidation.ValidationStatus == validationResponse.Status) { _logger.LogInformation("Validation {ValidationName} {ValidationId} {PackageId} {PackageVersion} already has status {ValidationStatus}", packageValidation.Type, packageValidation.PackageValidationSet.ValidationTrackingId, packageValidation.PackageValidationSet.PackageId, packageValidation.PackageValidationSet.PackageNormalizedVersion, validationResponse.Status); return; } _logger.LogInformation("Updating the status of the validation {ValidationName} {ValidationId} {PackageId} {PackageVersion} to {ValidationStatus}", packageValidation.Type, packageValidation.PackageValidationSet.ValidationTrackingId, packageValidation.PackageValidationSet.PackageId, packageValidation.PackageValidationSet.PackageNormalizedVersion, validationResponse.Status); await SetValidationStatusAsync(packageValidation, validationResponse, DateTime.UtcNow); }
private bool IsChecksumMatch(string peFilePath, string packageId, string packageNormalizedVersion, out INuGetValidationResponse validationResponse) { validationResponse = NuGetValidationResponse.Succeeded; using (var peStream = File.OpenRead(peFilePath)) using (var peReader = new PEReader(peStream)) { // This checks if portable PDB is associated with the PE file and opens it for reading. // It also validates that it matches the PE file. // It does not validate that the checksum matches, so we need to do that in the following block. if (peReader.TryOpenAssociatedPortablePdb(peFilePath, File.OpenRead, out var pdbReaderProvider, out var pdbPath) && // No need to validate embedded PDB (pdbPath == null for embedded) pdbPath != null) { // Get all checksum entries. There can be more than one. At least one must match the PDB. var checksumRecords = peReader.ReadDebugDirectory().Where(entry => entry.Type == DebugDirectoryEntryType.PdbChecksum) .Select(e => peReader.ReadPdbChecksumDebugDirectoryData(e)) .ToArray(); if (checksumRecords.Length == 0) { _telemetryService.TrackSymbolsAssemblyValidationResultEvent(packageId, packageNormalizedVersion, ValidationStatus.Failed, nameof(ValidationIssue.SymbolErrorCode_ChecksumDoesNotMatch), assemblyName: Path.GetFileName(peFilePath)); validationResponse = NuGetValidationResponse.FailedWithIssues(ValidationIssue.SymbolErrorCode_ChecksumDoesNotMatch); return(false); } var pdbBytes = File.ReadAllBytes(pdbPath); var hashes = new Dictionary <string, byte[]>(); using (pdbReaderProvider) { var pdbReader = pdbReaderProvider.GetMetadataReader(); int idOffset = pdbReader.DebugMetadataHeader.IdStartOffset; foreach (var checksumRecord in checksumRecords) { if (!hashes.TryGetValue(checksumRecord.AlgorithmName, out var hash)) { HashAlgorithmName han = new HashAlgorithmName(checksumRecord.AlgorithmName); using (var hashAlg = IncrementalHash.CreateHash(han)) { hashAlg.AppendData(pdbBytes, 0, idOffset); hashAlg.AppendData(new byte[20]); int offset = idOffset + 20; int count = pdbBytes.Length - offset; hashAlg.AppendData(pdbBytes, offset, count); hash = hashAlg.GetHashAndReset(); } hashes.Add(checksumRecord.AlgorithmName, hash); } if (checksumRecord.Checksum.ToArray().SequenceEqual(hash)) { // found the right checksum _telemetryService.TrackSymbolsAssemblyValidationResultEvent(packageId, packageNormalizedVersion, ValidationStatus.Succeeded, issue: "", assemblyName: Path.GetFileName(peFilePath)); return(true); } } // Not found any checksum record that matches the PDB. _telemetryService.TrackSymbolsAssemblyValidationResultEvent(packageId, packageNormalizedVersion, ValidationStatus.Failed, nameof(ValidationIssue.SymbolErrorCode_ChecksumDoesNotMatch), assemblyName: Path.GetFileName(peFilePath)); validationResponse = NuGetValidationResponse.FailedWithIssues(ValidationIssue.SymbolErrorCode_ChecksumDoesNotMatch); return(false); } } } _telemetryService.TrackSymbolsAssemblyValidationResultEvent(packageId, packageNormalizedVersion, ValidationStatus.Failed, nameof(ValidationIssue.SymbolErrorCode_MatchingAssemblyNotFound), assemblyName: Path.GetFileName(peFilePath)); validationResponse = NuGetValidationResponse.FailedWithIssues(ValidationIssue.SymbolErrorCode_MatchingAssemblyNotFound); return(false); }
protected override async Task ExecuteAsync(INuGetValidationResponse validationResponse) { await _target.MarkValidationStartedAsync(_packageValidation, validationResponse); }
protected override async Task ExecuteAsync(INuGetValidationResponse response) { await _target.UpdateValidationStatusAsync(_packageValidation, response); }
protected abstract Task ExecuteAsync(INuGetValidationResponse validationResponse);