示例#1
0
        public async Task HandlesTerminalStatusOnUpdate(ValidationStatus targetStatus)
        {
            UseDefaultValidatorProvider();
            var validator  = AddValidation("validation1", TimeSpan.FromDays(1), validationStatus: ValidationStatus.Incomplete);
            var validation = ValidationSet.PackageValidations.First();

            var validationResponse = new NuGetValidationResponse(targetStatus, new List <IValidationIssue>
            {
                ValidationIssue.PackageIsSigned,
            });

            validator
            .Setup(v => v.GetResponseAsync(It.IsAny <INuGetValidationRequest>()))
            .ReturnsAsync(validationResponse)
            .Verifiable();

            ValidationStorageMock
            .Setup(vs => vs.UpdateValidationStatusAsync(
                       validation,
                       It.Is <INuGetValidationResponse>(r => r.Status == targetStatus && r.Issues.Any())))
            .Returns(Task.FromResult(0));

            var processor = CreateProcessor();
            await processor.ProcessValidationsAsync(ValidationSet);

            ValidationStorageMock
            .Verify(vs => vs.UpdateValidationStatusAsync(
                        validation,
                        It.Is <INuGetValidationResponse>(r => r.Status == targetStatus && r.Issues.Any())),
                    Times.Once());
            validator.Verify(
                x => x.CleanUpAsync(It.Is <INuGetValidationRequest>(y => y != null)),
                Times.Once);
        }
示例#2
0
            public async Task CopiesNupkgUrlBeforeSavingDb()
            {
                // Arrange
                _validatorProvider
                .Setup(x => x.IsNuGetProcessor(It.IsAny <string>()))
                .Returns(true);
                var validationResponse = new NuGetValidationResponse(ValidationStatus.Succeeded, _nupkgUrl);

                var operations = new List <string>();

                _packageFileService
                .Setup(x => x.CopyPackageUrlForValidationSetAsync(It.IsAny <PackageValidationSet>(), It.IsAny <string>()))
                .Returns(Task.CompletedTask)
                .Callback <PackageValidationSet, string>((_, __) => operations.Add(
                                                             nameof(IValidationFileService.CopyPackageUrlForValidationSetAsync)));
                _entitiesContext
                .Setup(x => x.SaveChangesAsync())
                .ReturnsAsync(1)
                .Callback(() => operations.Add(nameof(IValidationEntitiesContext.SaveChangesAsync)));

                // Act
                await ExecuteAsync(validationResponse);

                // Assert
                Assert.Equal(new List <string>
                {
                    nameof(IValidationFileService.CopyPackageUrlForValidationSetAsync),
                    nameof(IValidationEntitiesContext.SaveChangesAsync),
                }, operations);
            }
示例#3
0
            public async Task DoesNotThrowWhenPackageValidationIssuesIsNull(ValidationStatus validationStatus)
            {
                // Arrange
                _packageValidation.PackageValidationIssues = null;
                var validationResponse = new NuGetValidationResponse(validationStatus);

                // Act
                var ex = await Record.ExceptionAsync(async() => await ExecuteAsync(validationResponse));

                // Assert
                Assert.Null(ex);
            }
示例#4
0
            public async Task DoesNotEmitTelemtryForNoStatusChange(ValidationStatus status)
            {
                // Arrange
                _packageValidation.ValidationStatus = status;
                var validationResponse = new NuGetValidationResponse(status);

                _telemetryService = new Mock <ITelemetryService>(MockBehavior.Strict);
                InitializeTarget();

                // Act
                await ExecuteAsync(validationResponse);

                // Assert
                _telemetryService.VerifyAll();
            }
示例#5
0
            public async Task DoesNotEmitTelemetryForIncomplete()
            {
                // Arrange
                _packageValidation.ValidationStatus = ValidationStatus.NotStarted;
                var validationResponse = new NuGetValidationResponse(ValidationStatus.Incomplete);

                _telemetryService = new Mock <ITelemetryService>(MockBehavior.Strict);
                InitializeTarget();

                // Act
                await ExecuteAsync(validationResponse);

                // Assert
                _telemetryService.VerifyAll();
            }
示例#6
0
            public async Task EmitsZeroDurationIfStartedPropertyIsNotSet()
            {
                // Arrange
                _packageValidation.Started = null;

                var validationResponse = new NuGetValidationResponse(ValidationStatus.Failed);

                // Act
                await ExecuteAsync(validationResponse);

                // Assert
                var validationSet = _packageValidation.PackageValidationSet;

                _telemetryService.Verify(
                    x => x.TrackValidatorDuration(validationSet.PackageId, validationSet.PackageNormalizedVersion, validationSet.ValidationTrackingId, TimeSpan.Zero, _validatorType, false),
                    Times.Once);
            }
示例#7
0
            public async Task RejectsNupkgUrlWhenValidatorIsNotProcessor()
            {
                // Arrange
                _validatorProvider
                .Setup(x => x.IsNuGetProcessor(It.IsAny <string>()))
                .Returns(false);
                var validationResponse = new NuGetValidationResponse(ValidationStatus.Succeeded, _nupkgUrl);

                // Act & Assert
                var ex = await Assert.ThrowsAsync <InvalidOperationException>(
                    () => ExecuteAsync(validationResponse));

                Assert.Equal(
                    $"The validator '{_validatorType}' is not a processor but returned a .nupkg URL as " +
                    $"part of the validation step response.",
                    ex.Message);
            }
示例#8
0
            public async Task CopiesNupkgUrlWhenValidatorIsProcessor()
            {
                // Arrange
                _validatorProvider
                .Setup(x => x.IsNuGetProcessor(It.IsAny <string>()))
                .Returns(true);
                var validationResponse = new NuGetValidationResponse(ValidationStatus.Succeeded, _nupkgUrl);

                // Act
                await ExecuteAsync(validationResponse);

                // Assert
                _packageFileService.Verify(
                    x => x.CopyPackageUrlForValidationSetAsync(_packageValidation.PackageValidationSet, _nupkgUrl),
                    Times.Once);
                _packageFileService.Verify(
                    x => x.CopyPackageUrlForValidationSetAsync(It.IsAny <PackageValidationSet>(), It.IsAny <string>()),
                    Times.Once);
            }
示例#9
0
            public async Task EmitsTelemetryWhenStatusChanges(ValidationStatus toStatus, bool isSuccess)
            {
                // Arrange
                TimeSpan duration = default(TimeSpan);

                _telemetryService
                .Setup(x => x.TrackValidatorDuration(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <Guid>(), It.IsAny <TimeSpan>(), It.IsAny <string>(), It.IsAny <bool>()))
                .Callback <string, string, Guid, TimeSpan, string, bool>((_1, _2, _3, d, _, __) => duration = d);

                var validationResponse = new NuGetValidationResponse(
                    toStatus,
                    new List <IValidationIssue>
                {
                    ValidationIssue.PackageIsSigned,
                    new ClientSigningVerificationFailure("NU3000", "Issue A"),
                    new ClientSigningVerificationFailure("NU3001", "Issue B"),
                });

                // Act
                var before = DateTime.UtcNow;

                await ExecuteAsync(validationResponse);

                var after = DateTime.UtcNow;

                // Assert
                var validationSet = _packageValidation.PackageValidationSet;

                _telemetryService.Verify(
                    x => x.TrackValidatorDuration(validationSet.PackageId, validationSet.PackageNormalizedVersion, validationSet.ValidationTrackingId, It.IsAny <TimeSpan>(), _validatorType, isSuccess),
                    Times.Once);
                Assert.InRange(duration, before - _packageValidation.Started.Value, after - _packageValidation.Started.Value);

                _telemetryService.Verify(x => x.TrackValidationIssueCount(validationSet.PackageId, validationSet.PackageNormalizedVersion, validationSet.ValidationTrackingId, 3, _validatorType, isSuccess), Times.Once);
                _telemetryService.Verify(x => x.TrackValidationIssue(validationSet.PackageId, validationSet.PackageNormalizedVersion, validationSet.ValidationTrackingId, _validatorType, ValidationIssueCode.PackageIsSigned));
                _telemetryService.Verify(x => x.TrackValidationIssue(validationSet.PackageId, validationSet.PackageNormalizedVersion, validationSet.ValidationTrackingId, _validatorType, ValidationIssueCode.ClientSigningVerificationFailure));
                _telemetryService.Verify(x => x.TrackValidationIssue(validationSet.PackageId, validationSet.PackageNormalizedVersion, validationSet.ValidationTrackingId, _validatorType, ValidationIssueCode.ClientSigningVerificationFailure));
                _telemetryService.Verify(x => x.TrackClientValidationIssue(validationSet.PackageId, validationSet.PackageNormalizedVersion, validationSet.ValidationTrackingId, _validatorType, "NU3000"), Times.Once);
                _telemetryService.Verify(x => x.TrackClientValidationIssue(validationSet.PackageId, validationSet.PackageNormalizedVersion, validationSet.ValidationTrackingId, _validatorType, "NU3001"), Times.Once);
            }
示例#10
0
        /// <summary>
        /// The method that performs the actual validation.
        /// More information about checksum algorithm:
        /// https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PE-COFF.md#portable-pdb-checksum
        /// </summary>
        /// <param name="targetDirectory">The directory used during the current validation.</param>
        /// <param name="packageId">Package Id.</param>
        /// <param name="packageNormalizedVersion">PackageNormalized version.</param>
        /// <returns></returns>
        public virtual INuGetValidationResponse ValidateSymbolMatching(string targetDirectory, string packageId, string packageNormalizedVersion)
        {
            foreach (string extension in PEExtensionsPatterns)
            {
                foreach (string peFile in Directory.GetFiles(targetDirectory, extension, SearchOption.AllDirectories))
                {
                    INuGetValidationResponse validationResponse;

                    if (!IsPortable(GetSymbolPath(peFile)))
                    {
                        _telemetryService.TrackSymbolsValidationResultEvent(packageId, packageNormalizedVersion, ValidationStatus.Failed);
                        return(NuGetValidationResponse.FailedWithIssues(ValidationIssue.SymbolErrorCode_PdbIsNotPortable));
                    }
                    if (!IsChecksumMatch(peFile, packageId, packageNormalizedVersion, out validationResponse))
                    {
                        _telemetryService.TrackSymbolsValidationResultEvent(packageId, packageNormalizedVersion, ValidationStatus.Failed);
                        return(validationResponse);
                    }
                }
            }
            _telemetryService.TrackSymbolsValidationResultEvent(packageId, packageNormalizedVersion, ValidationStatus.Succeeded);
            return(NuGetValidationResponse.Succeeded);
        }
            public async Task IncompleteStateIsProcessedAndSavedOnFailed()
            {
                // Arrange
                ValidatorStatus status = new ValidatorStatus()
                {
                    State = ValidationStatus.Incomplete
                };

                _validatorStateService.Setup(s => s.GetStatusAsync(It.IsAny <Guid>())).ReturnsAsync(status);
                _validatorStateService.Setup(s => s.SaveStatusAsync(It.IsAny <ValidatorStatus>())).ReturnsAsync(SaveStatusResult.Success);

                _symbolService
                .Setup(s => s.ValidateSymbolsAsync(It.IsAny <SymbolsValidatorMessage>(), It.IsAny <CancellationToken>()))
                .ReturnsAsync(NuGetValidationResponse.FailedWithIssues(ValidationIssue.Unknown));

                // Act
                var result = await Target.HandleAsync(_message);

                // Assert
                Assert.True(result);
                Assert.Equal(1, status.ValidatorIssues.Count);
                _validatorStateService.Verify(ss => ss.SaveStatusAsync(It.IsAny <ValidatorStatus>()), Times.Once);
            }
示例#12
0
        public async Task <INuGetValidationResponse> ValidateSymbolsAsync(SymbolsValidatorMessage message, CancellationToken token)
        {
            _logger.LogInformation("{ValidatorName} :Start ValidateSymbolsAsync. PackageId: {packageId} PackageNormalizedVersion: {packageNormalizedVersion}",
                                   ValidatorName.SymbolsValidator,
                                   message.PackageId,
                                   message.PackageNormalizedVersion);

            try
            {
                using (Stream snupkgstream = await _symbolFileService.DownloadSnupkgFileAsync(message.SnupkgUrl, token))
                {
                    if (!await _zipArchiveService.ValidateZipAsync(snupkgstream, message.SnupkgUrl, token))
                    {
                        return(NuGetValidationResponse.FailedWithIssues(ValidationIssue.SymbolErrorCode_SnupkgContainsEntriesNotSafeForExtraction));
                    }

                    try
                    {
                        using (Stream nupkgstream = await _symbolFileService.DownloadNupkgFileAsync(message.PackageId, message.PackageNormalizedVersion, token))
                        {
                            var pdbs = _zipArchiveService.ReadFilesFromZipStream(snupkgstream, SymbolExtension);
                            var pes  = _zipArchiveService.ReadFilesFromZipStream(nupkgstream, PEExtensions);

                            if (pdbs.Count == 0)
                            {
                                return(NuGetValidationResponse.FailedWithIssues(ValidationIssue.SymbolErrorCode_SnupkgDoesNotContainSymbols));
                            }

                            using (_telemetryService.TrackSymbolValidationDurationEvent(message.PackageId, message.PackageNormalizedVersion, pdbs.Count))
                            {
                                List <string> orphanSymbolFiles;
                                if (!SymbolsHaveMatchingPEFiles(pdbs, pes, out orphanSymbolFiles))
                                {
                                    orphanSymbolFiles.ForEach((symbol) =>
                                    {
                                        _telemetryService.TrackSymbolsAssemblyValidationResultEvent(message.PackageId, message.PackageNormalizedVersion, ValidationStatus.Failed, nameof(ValidationIssue.SymbolErrorCode_MatchingAssemblyNotFound), assemblyName: symbol);
                                    });
                                    _telemetryService.TrackSymbolsValidationResultEvent(message.PackageId, message.PackageNormalizedVersion, ValidationStatus.Failed);
                                    return(NuGetValidationResponse.FailedWithIssues(ValidationIssue.SymbolErrorCode_MatchingAssemblyNotFound));
                                }
                                var targetDirectory = Settings.GetWorkingDirectory();
                                try
                                {
                                    _logger.LogInformation("Extracting symbols to {TargetDirectory}", targetDirectory);
                                    var symbolFiles = _zipArchiveService.ExtractFilesFromZipStream(snupkgstream, targetDirectory, SymbolExtension);

                                    _logger.LogInformation("Extracting dlls to {TargetDirectory}", targetDirectory);
                                    _zipArchiveService.ExtractFilesFromZipStream(nupkgstream, targetDirectory, PEExtensions, symbolFiles);

                                    var status = ValidateSymbolMatching(targetDirectory, message.PackageId, message.PackageNormalizedVersion);
                                    return(status);
                                }
                                finally
                                {
                                    TryCleanWorkingDirectoryForSeconds(targetDirectory, message.PackageId, message.PackageNormalizedVersion, _cleanWorkingDirectoryTimeSpan);
                                }
                            }
                        }
                    }
                    catch (FileNotFoundException)
                    {
                        _telemetryService.TrackPackageNotFoundEvent(message.PackageId, message.PackageNormalizedVersion);
                        return(NuGetValidationResponse.Failed);
                    }
                }
            }
            catch (InvalidOperationException)
            {
                _telemetryService.TrackSymbolsPackageNotFoundEvent(message.PackageId, message.PackageNormalizedVersion);
                return(NuGetValidationResponse.Failed);
            }
        }
示例#13
0
        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);
        }