示例#1
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);
            }
示例#3
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);
            }
        }
示例#4
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);
        }