public async Task WithNoExistingVulnerability_Withdrawn_DoesNotAdd()
            {
                // Arrange
                var id            = "theId";
                var versionRange  = new VersionRange(NuGetVersion.Parse("1.0.0")).ToNormalizedString();
                var vulnerability = new PackageVulnerability();
                var range         = new VulnerablePackageVersionRange
                {
                    Vulnerability       = vulnerability,
                    PackageId           = id,
                    PackageVersionRange = versionRange
                };

                vulnerability.AffectedRanges.Add(range);

                // Act
                await Service.UpdateVulnerabilityAsync(vulnerability, true);

                // Assert
                Assert.False(Context.Vulnerabilities.AnySafe());
                Assert.False(Context.VulnerableRanges.AnySafe());
                UpdateServiceMock.Verify(
                    x => x.UpdatePackagesAsync(It.IsAny <IReadOnlyList <Package> >(), It.IsAny <bool>()),
                    Times.Never);

                VerifyTransaction();
            }
Esempio n. 2
0
        /// <summary>
        /// Iterates through the <see cref="Package"/>s that could be vulnerable to <paramref name="range"/>.
        /// If any of these packages have not been marked vulnerable and should be, mark them vulnerable and add them to <paramref name="packagesToUpdate"/>.
        /// </summary>
        /// <remarks>
        /// It is not possible to query the database to only return packages satisfying the version range, so we must iterate through all <see cref="Package"/>s in the <see cref="PackageRegistration"/>.
        /// </remarks>
        private void ProcessNewVulnerabilityRange(VulnerablePackageVersionRange range, HashSet <Package> packagesToUpdate)
        {
            _logger.LogInformation(
                "Marking packages that match ID {VulnerablePackageId} and version range {VulnerablePackageVersionRange} vulnerable",
                range.PackageId,
                range.PackageVersionRange);

            var versionRange = VersionRange.Parse(range.PackageVersionRange);
            var packages     = _entitiesContext.PackageRegistrations
                               .Where(pr => pr.Id == range.PackageId)
                               .SelectMany(pr => pr.Packages)
                               .ToList();

            foreach (var package in packages)
            {
                var version = NuGetVersion.Parse(package.NormalizedVersion);
                var satisfiesVersionRange = versionRange.Satisfies(version);
                if (satisfiesVersionRange)
                {
                    _logger.LogInformation(
                        "Package with ID {VulnerablePackageId} and version {VulnerablePackageVersion} satisfies {VulnerablePackageVersionRange} and is vulnerable",
                        range.PackageId,
                        package.NormalizedVersion,
                        range.PackageVersionRange);

                    package.Vulnerabilities.Add(range);
                    range.Packages.Add(package);
                    packagesToUpdate.Add(package);
                }
            }
        }
            public async Task WithExistingVulnerability_Withdrawn_RemovesAndUnmarks(bool hasExistingVulnerablePackages)
            {
                // Arrange
                var key           = 1;
                var vulnerability = new PackageVulnerability {
                    GitHubDatabaseKey = key
                };
                var existingVulnerability = new PackageVulnerability
                {
                    GitHubDatabaseKey = key,
                    Severity          = PackageVulnerabilitySeverity.Moderate
                };

                Context.Vulnerabilities.Add(existingVulnerability);
                var id           = "theId";
                var versionRange = new VersionRange(NuGetVersion.Parse("1.0.0")).ToNormalizedString();
                var range        = new VulnerablePackageVersionRange
                {
                    Vulnerability       = vulnerability,
                    PackageId           = id,
                    PackageVersionRange = versionRange
                };

                vulnerability.AffectedRanges.Add(range);

                if (hasExistingVulnerablePackages)
                {
                    var existingVulnerablePackage = new Package();
                    var existingRange             = new VulnerablePackageVersionRange
                    {
                        Vulnerability = existingVulnerability
                    };

                    Context.VulnerableRanges.Add(existingRange);
                    existingVulnerability.AffectedRanges.Add(existingRange);
                    existingRange.Packages.Add(existingVulnerablePackage);
                    vulnerability.AffectedRanges.Add(existingRange);

                    UpdateServiceMock
                    .Setup(x => x.UpdatePackagesAsync(new[] { existingVulnerablePackage }, true))
                    .Returns(Task.CompletedTask)
                    .Verifiable();
                }

                var service = GetService <PackageVulnerabilitiesManagementService>();

                // Act
                await service.UpdateVulnerabilityAsync(vulnerability, true);

                // Assert
                Assert.False(Context.Vulnerabilities.AnySafe());
                Assert.DoesNotContain(range, Context.VulnerableRanges);
                UpdateServiceMock.Verify();

                VerifyTransaction();
            }
            public async Task WithNoExistingVulnerability_WithRanges_Adds()
            {
                // Arrange
                var id            = "theId";
                var versionRange  = new VersionRange(NuGetVersion.Parse("1.0.0")).ToNormalizedString();
                var vulnerability = new PackageVulnerability();
                var range         = new VulnerablePackageVersionRange
                {
                    Vulnerability       = vulnerability,
                    PackageId           = id,
                    PackageVersionRange = versionRange
                };

                vulnerability.AffectedRanges.Add(range);

                var registration = new PackageRegistration {
                    Id = id
                };

                Context.PackageRegistrations.Add(registration);
                var vulnerablePackage = new Package {
                    PackageRegistration = registration, NormalizedVersion = "1.0.1"
                };

                registration.Packages.Add(vulnerablePackage);

                var notVulnerablePackage = new Package {
                    PackageRegistration = registration, NormalizedVersion = "0.0.0"
                };

                registration.Packages.Add(notVulnerablePackage);

                UpdateServiceMock
                .Setup(x => x.UpdatePackagesAsync(new[] { vulnerablePackage }, true))
                .Returns(Task.CompletedTask)
                .Verifiable();

                // Act
                await Service.UpdateVulnerabilityAsync(vulnerability, false);

                // Assert
                Assert.Single(Context.Vulnerabilities, vulnerability);
                Assert.Single(Context.VulnerableRanges, range);

                UpdateServiceMock.Verify();
                VerifyTransaction();
            }
Esempio n. 5
0
            public async Task WithExistingVulnerability_NotWithdrawn_NoRanges_RemovesAndUnmarks(bool hasExistingVulnerablePackages)
            {
                // Arrange
                var key           = 1;
                var vulnerability = new PackageVulnerability {
                    GitHubDatabaseKey = key
                };
                var existingVulnerability = new PackageVulnerability
                {
                    GitHubDatabaseKey = key,
                    Severity          = PackageVulnerabilitySeverity.Moderate
                };

                Context.Vulnerabilities.Add(existingVulnerability);

                if (hasExistingVulnerablePackages)
                {
                    var existingVulnerablePackage = new Package();
                    var existingRange             = new VulnerablePackageVersionRange
                    {
                        Vulnerability = existingVulnerability
                    };

                    Context.VulnerableRanges.Add(existingRange);
                    existingVulnerability.AffectedRanges.Add(existingRange);
                    existingRange.Packages.Add(existingVulnerablePackage);

                    UpdateServiceMock
                    .Setup(x => x.UpdatePackagesAsync(new[] { existingVulnerablePackage }, true))
                    .Returns(Task.CompletedTask)
                    .Verifiable();
                }

                var service = GetService <PackageVulnerabilityService>();

                // Act
                await service.UpdateVulnerabilityAsync(vulnerability, false);

                // Assert
                Assert.False(Context.Vulnerabilities.AnySafe());
                Assert.False(Context.VulnerableRanges.AnySafe());
                UpdateServiceMock.Verify();

                VerifyTransaction();
            }
            public void AppliesCorrectVulnerabilities()
            {
                // Arrange
                var registration = new PackageRegistration
                {
                    Id = "id"
                };

                var package = new Package
                {
                    PackageRegistration = registration,
                    NormalizedVersion   = "1.0.0"
                };

                var wrongIdRange = new VulnerablePackageVersionRange
                {
                    PackageId           = "wrongId",
                    PackageVersionRange = "(,)"
                };

                var satisfiedRange = new VulnerablePackageVersionRange
                {
                    PackageId           = registration.Id,
                    PackageVersionRange = "[1.0.0, )"
                };

                var unsatisfiedRange = new VulnerablePackageVersionRange
                {
                    PackageId           = registration.Id,
                    PackageVersionRange = "(, 1.0.0)"
                };

                Context.VulnerableRanges.AddRange(
                    new[] { wrongIdRange, satisfiedRange, unsatisfiedRange });

                // Act
                Service.ApplyExistingVulnerabilitiesToPackage(package);

                // Assert
                Assert.Single(package.VulnerablePackageRanges, satisfiedRange);
                Assert.Single(satisfiedRange.Packages, package);
            }
            public async Task WithExistingVulnerability_NotWithdrawn_UpdatesPackages(
                bool hasExistingVulnerablePackages,
                bool wasUpdated)
            {
                // Arrange
                var key           = 1;
                var severity      = PackageVulnerabilitySeverity.Low;
                var newSeverity   = PackageVulnerabilitySeverity.Critical;
                var url           = "http://hi";
                var newUrl        = "https://howdy";
                var vulnerability = new PackageVulnerability
                {
                    GitHubDatabaseKey = key,
                    Severity          = wasUpdated ? newSeverity : severity,
                    AdvisoryUrl       = wasUpdated ? newUrl : url
                };

                var existingVulnerability = new PackageVulnerability
                {
                    GitHubDatabaseKey = key,
                    Severity          = severity,
                    AdvisoryUrl       = url
                };

                Context.Vulnerabilities.Add(existingVulnerability);

                var id           = "theId";
                var versionRange = new VersionRange(NuGetVersion.Parse("1.0.0")).ToNormalizedString();
                var range        = new VulnerablePackageVersionRange
                {
                    Vulnerability       = vulnerability,
                    PackageId           = id,
                    PackageVersionRange = versionRange
                };

                vulnerability.AffectedRanges.Add(range);

                var registration = new PackageRegistration
                {
                    Id = id
                };

                var expectedPackagesToUpdate   = new List <Package>();
                var expectedVulnerablePackages = new List <Package>();

                if (hasExistingVulnerablePackages)
                {
                    // Any packages that are already vulnerable to this vulnerability but not associated with this package vulnerability should be updated if the vulnerability is updated.
                    var existingVulnerablePackage = new Package
                    {
                        PackageRegistration = registration,
                        NormalizedVersion   = "0.0.5"
                    };

                    var existingVulnerablePackageVersion = NuGetVersion.Parse(existingVulnerablePackage.NormalizedVersion);
                    var existingRange = new VulnerablePackageVersionRange
                    {
                        Vulnerability       = existingVulnerability,
                        PackageId           = id,
                        PackageVersionRange = new VersionRange(existingVulnerablePackageVersion, true, existingVulnerablePackageVersion, true).ToNormalizedString()
                    };

                    Context.VulnerableRanges.Add(existingRange);
                    existingRange.Packages.Add(existingVulnerablePackage);
                    existingVulnerability.AffectedRanges.Add(existingRange);
                    vulnerability.AffectedRanges.Add(existingRange);

                    expectedVulnerablePackages.Add(existingVulnerablePackage);
                    if (wasUpdated)
                    {
                        expectedPackagesToUpdate.Add(existingVulnerablePackage);
                    }

                    // If an existing vulnerable range is updated, its packages should be updated.
                    var existingVulnerablePackageWithUpdatedRange = new Package
                    {
                        PackageRegistration = registration,
                        NormalizedVersion   = "0.0.9"
                    };

                    var existingVulnerablePackageVersionWithUpdatedRange = NuGetVersion.Parse(existingVulnerablePackageWithUpdatedRange.NormalizedVersion);
                    var existingRangeWithUpdatedRange = new VulnerablePackageVersionRange
                    {
                        Vulnerability       = existingVulnerability,
                        PackageId           = id,
                        PackageVersionRange = new VersionRange(existingVulnerablePackageVersionWithUpdatedRange, true, existingVulnerablePackageVersionWithUpdatedRange, true).ToNormalizedString()
                    };

                    Context.VulnerableRanges.Add(existingRangeWithUpdatedRange);
                    existingRangeWithUpdatedRange.Packages.Add(existingVulnerablePackageWithUpdatedRange);
                    existingVulnerability.AffectedRanges.Add(existingRangeWithUpdatedRange);

                    var updatedExistingRange = new VulnerablePackageVersionRange
                    {
                        Vulnerability              = existingRangeWithUpdatedRange.Vulnerability,
                        PackageId                  = existingRangeWithUpdatedRange.PackageId,
                        PackageVersionRange        = existingRangeWithUpdatedRange.PackageVersionRange,
                        FirstPatchedPackageVersion = "1.0.0"
                    };

                    vulnerability.AffectedRanges.Add(updatedExistingRange);

                    expectedVulnerablePackages.Add(existingVulnerablePackageWithUpdatedRange);
                    expectedPackagesToUpdate.Add(existingVulnerablePackageWithUpdatedRange);

                    // If a package vulnerability is missing from the new vulnerability, it should be removed.
                    var existingMissingVulnerablePackage = new Package
                    {
                        PackageRegistration = registration,
                        NormalizedVersion   = "0.0.6"
                    };

                    var existingMissingVulnerablePackageVersion = NuGetVersion.Parse(existingMissingVulnerablePackage.NormalizedVersion);
                    var existingMissingRange = new VulnerablePackageVersionRange
                    {
                        Vulnerability       = existingVulnerability,
                        PackageId           = id,
                        PackageVersionRange = new VersionRange(existingMissingVulnerablePackageVersion, true, existingMissingVulnerablePackageVersion, true).ToNormalizedString()
                    };

                    Context.VulnerableRanges.Add(existingMissingRange);
                    existingMissingRange.Packages.Add(existingMissingVulnerablePackage);
                    existingVulnerability.AffectedRanges.Add(existingMissingRange);

                    expectedPackagesToUpdate.Add(existingMissingVulnerablePackage);
                }

                Context.PackageRegistrations.Add(registration);

                // A package that fits in the version range but is not vulnerable yet should be vulnerable and updated.
                var newlyVulnerablePackage = new Package
                {
                    PackageRegistration = registration,
                    NormalizedVersion   = "1.0.2"
                };

                registration.Packages.Add(newlyVulnerablePackage);
                expectedVulnerablePackages.Add(newlyVulnerablePackage);
                expectedPackagesToUpdate.Add(newlyVulnerablePackage);

                // A package that is not vulnerable and does not fit in the version range should not be touched.
                var neverVulnerablePackage = new Package
                {
                    PackageRegistration = registration,
                    NormalizedVersion   = "0.0.1"
                };

                registration.Packages.Add(neverVulnerablePackage);

                if (expectedPackagesToUpdate.Any())
                {
                    UpdateServiceMock
                    .Setup(x => x.UpdatePackagesAsync(
                               It.Is <IReadOnlyList <Package> >(
                                   p => p
                                   .OrderBy(v => v.NormalizedVersion)
                                   .SequenceEqual(
                                       expectedPackagesToUpdate.OrderBy(v => v.NormalizedVersion))),
                               true))
                    .Returns(Task.CompletedTask)
                    .Verifiable();
                }

                var service = GetService <PackageVulnerabilitiesManagementService>();

                // Act
                await service.UpdateVulnerabilityAsync(vulnerability, false);

                // Assert
                Assert.Contains(existingVulnerability, Context.Vulnerabilities);
                Assert.Contains(range, Context.VulnerableRanges);
                Assert.Equal(existingVulnerability, range.Vulnerability);
                Assert.Equal(wasUpdated ? newSeverity : severity, existingVulnerability.Severity);
                Assert.Equal(wasUpdated ? newUrl : url, existingVulnerability.AdvisoryUrl);
                Assert.Equal(
                    expectedVulnerablePackages.OrderBy(p => p.NormalizedVersion),
                    Context.VulnerableRanges.SelectMany(pv => pv.Packages).OrderBy(p => p.NormalizedVersion));

                UpdateServiceMock.Verify();

                VerifyTransaction();
            }
        private void SetUp()
        {
            _registrationVulnerable = new PackageRegistration {
                Id = "Vulnerable"
            };

            _vulnerabilityCritical = new PackageVulnerability
            {
                AdvisoryUrl       = "http://theurl/1234",
                GitHubDatabaseKey = 1234,
                Severity          = PackageVulnerabilitySeverity.Critical
            };
            _vulnerabilityModerate = new PackageVulnerability
            {
                AdvisoryUrl       = "http://theurl/5678",
                GitHubDatabaseKey = 5678,
                Severity          = PackageVulnerabilitySeverity.Moderate
            };

            _versionRangeCritical = new VulnerablePackageVersionRange
            {
                Vulnerability              = _vulnerabilityCritical,
                PackageVersionRange        = "1.1.1",
                FirstPatchedPackageVersion = "1.1.2"
            };
            _versionRangeModerate = new VulnerablePackageVersionRange
            {
                Vulnerability              = _vulnerabilityModerate,
                PackageVersionRange        = "<=1.1.1",
                FirstPatchedPackageVersion = "1.1.2"
            };

            _packageVulnerable100 = new Package
            {
                Key = 0,
                PackageRegistration     = _registrationVulnerable,
                Version                 = "1.0.0",
                VulnerablePackageRanges = new List <VulnerablePackageVersionRange>
                {
                    _versionRangeModerate
                }
            };
            _packageVulnerable110 = new Package
            {
                Key = 1,
                PackageRegistration     = _registrationVulnerable,
                Version                 = "1.1.0",
                VulnerablePackageRanges = new List <VulnerablePackageVersionRange>
                {
                    _versionRangeModerate
                }
            };
            _packageVulnerable111 = new Package
            {
                Key = 3, // simulate a different order in db - create a non-contiguous range of rows, even if the range is contiguous
                PackageRegistration     = _registrationVulnerable,
                Version                 = "1.1.1",
                VulnerablePackageRanges = new List <VulnerablePackageVersionRange>
                {
                    _versionRangeModerate,
                    _versionRangeCritical
                }
            };
            _packageVulnerable112 = new Package
            {
                Key = 2, // simulate a different order in db  - create a non-contiguous range of rows, even if the range is contiguous
                PackageRegistration     = _registrationVulnerable,
                Version                 = "1.1.2",
                VulnerablePackageRanges = new List <VulnerablePackageVersionRange>()
            };
            _packageNotVulnerable = new Package
            {
                Key = 4,
                PackageRegistration = new PackageRegistration {
                    Id = "NotVulnerable"
                },
                VulnerablePackageRanges = new List <VulnerablePackageVersionRange>()
            };
        }