Skip to content

Commit acedff0

Browse files
authored
Fix bug causing reduced vulnerabilities version ranges to not flow into V3 (#9229)
Entity Framework clears dependent collections when the parent entity is removed from the DB context. This means it can't be used to do further cleanup. The database was properly updated (based on cascading deletion logic from EF) but the packages did not get their LastEdited timestamp updated meaning Db2Catalog did not flow the latest (no longer vulnerable) state into V3. Progress on NuGet/Engineering#4566 After running VerifyGitHubVulnerabilities and reflowing any affected packages, DEV, INT, and PROD environments are now fully consistent with the GitHub advisory database.
1 parent f635f4b commit acedff0

7 files changed

Lines changed: 28 additions & 13 deletions

File tree

src/GitHubVulnerabilities2Db/Ingest/AdvisoryIngestor.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Linq;
77
using System.Threading.Tasks;
88
using GitHubVulnerabilities2Db.GraphQL;
9+
using Microsoft.Extensions.Logging;
910
using NuGet.Services.Entities;
1011
using NuGetGallery;
1112

@@ -15,19 +16,24 @@ public class AdvisoryIngestor : IAdvisoryIngestor
1516
{
1617
private readonly IPackageVulnerabilitiesManagementService _packageVulnerabilityService;
1718
private readonly IGitHubVersionRangeParser _gitHubVersionRangeParser;
19+
private readonly ILogger<AdvisoryIngestor> _logger;
1820

1921
public AdvisoryIngestor(
2022
IPackageVulnerabilitiesManagementService packageVulnerabilityService,
21-
IGitHubVersionRangeParser gitHubVersionRangeParser)
23+
IGitHubVersionRangeParser gitHubVersionRangeParser,
24+
ILogger<AdvisoryIngestor> logger)
2225
{
2326
_packageVulnerabilityService = packageVulnerabilityService ?? throw new ArgumentNullException(nameof(packageVulnerabilityService));
2427
_gitHubVersionRangeParser = gitHubVersionRangeParser ?? throw new ArgumentNullException(nameof(gitHubVersionRangeParser));
28+
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
2529
}
2630

2731
public async Task IngestAsync(IReadOnlyList<SecurityAdvisory> advisories)
2832
{
29-
foreach (var advisory in advisories)
33+
for (int i = 0; i < advisories.Count; i++)
3034
{
35+
_logger.LogInformation("Processing advisory {Current} of {Total}...", i + 1, advisories.Count);
36+
SecurityAdvisory advisory = advisories[i];
3137
var vulnerabilityTuple = FromAdvisory(advisory);
3238
var vulnerability = vulnerabilityTuple.Item1;
3339
var wasWithdrawn = vulnerabilityTuple.Item2;

src/NuGetGallery.Services/PackageManagement/PackageVulnerabilitiesManagementService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,9 @@ private void UpdateRangesOfPackageVulnerability(PackageVulnerability vulnerabili
212212
existingRange.PackageVersionRange,
213213
vulnerability.GitHubDatabaseKey);
214214

215+
packagesToUpdate.UnionWith(existingRange.Packages);
215216
_entitiesContext.VulnerableRanges.Remove(existingRange);
216217
existingVulnerability.AffectedRanges.Remove(existingRange);
217-
packagesToUpdate.UnionWith(existingRange.Packages);
218218
}
219219
else
220220
{

src/VerifyGitHubVulnerabilities/Job.cs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5+
using System.Linq;
56
using System.Net.Http;
67
using System.Threading;
78
using System.Threading.Tasks;
@@ -28,14 +29,14 @@ class Job : JsonConfigurationJob
2829

2930
public override async Task Run()
3031
{
31-
Console.Write("Fetching vulnerabilities from GitHub...");
32+
Console.WriteLine("Fetching vulnerabilities from GitHub...");
3233
var advisoryQueryService = _serviceProvider.GetRequiredService<IAdvisoryQueryService>();
3334
var advisories = await advisoryQueryService.GetAdvisoriesSinceAsync(DateTimeOffset.MinValue, CancellationToken.None);
3435
Console.WriteLine($" FOUND {advisories.Count} advisories.");
3536

3637
Console.WriteLine("Fetching vulnerabilities from DB...");
3738
var ingestor = _serviceProvider.GetRequiredService<IAdvisoryIngestor>();
38-
await ingestor.IngestAsync(advisories);
39+
await ingestor.IngestAsync(advisories.OrderBy(x => x.DatabaseId).ToList());
3940

4041
var verifier = _serviceProvider.GetRequiredService<IPackageVulnerabilitiesVerifier>();
4142
Console.WriteLine(verifier.HasErrors ?
@@ -82,7 +83,7 @@ protected void ConfigureGalleryServices(ContainerBuilder containerBuilder)
8283
.Register(ctx =>
8384
{
8485
var connection = CreateSqlConnection<GalleryDbConfiguration>();
85-
return new EntitiesContext(connection, false);
86+
return new EntitiesContext(connection, readOnly: true);
8687
})
8788
.As<IEntitiesContext>();
8889

@@ -97,10 +98,6 @@ protected void ConfigureQueryServices(ContainerBuilder containerBuilder)
9798
.RegisterInstance(_client)
9899
.As<HttpClient>();
99100

100-
containerBuilder
101-
.RegisterGeneric(typeof(NullLogger<>))
102-
.As(typeof(ILogger<>));
103-
104101
containerBuilder
105102
.RegisterType<VerifyGitHubVulnerabilities.GraphQL.QueryService>()
106103
.As<IQueryService>();

src/VerifyGitHubVulnerabilities/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ The easiest way to run the tool if you are on the nuget.org team is to use the D
2020
1. Install the certificate used to authenticate as our client AAD app registration into your `CurrentUser` certificate store.
2121
1. Clone our internal [`NuGetDeployment`](https://nuget.visualstudio.com/DefaultCollection/NuGetMicrosoft/_git/NuGetDeploymentp) repository.
2222
1. Take a copy of the [DEV GitHubVulnerabilities2Db appsettings.json](https://nuget.visualstudio.com/NuGetMicrosoft/_git/NuGetDeployment?path=%2Fsrc%2FJobs%2FNuGet.Jobs.Cloud%2FJobs%2FGitHubVulnerabilities2Db%2FDEV%2Fnorthcentralus%2Fappsettings.json) file and place it in the same directory as the `verifygithubvulnerabilities.exe`. This will use our secrets to authenticate to the SQL server (this file also contains a reference to the secret used for the access token to GitHub).
23-
1. Run as per above.
23+
1. Set the following property, in the `appsettings.json`, under the `"Initialization"` object: `"NuGetV3Index": "https://apidev.nugettest.org/v3/index.json"`.
24+
1. Overwrite the Gallery DB `"ConnectionString"` property to be the connection string from [DEV USSC (read-only) gallery config](https://nuget.visualstudio.com/NuGetMicrosoft/_git/NuGetDeployment?path=/src/Gallery/ExpressV2/ServiceSpecs/Parameters.AS/DEV/USSC/NuGet.Gallery.Parameters.json&version=GBmaster). This provides an additional protection to ensure the job runs as read-only.
25+
2. Run as per above.
2426

2527
## Algorithm
2628

src/VerifyGitHubVulnerabilities/Verify/PackageVulnerabilitiesVerifier.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ private async Task VerifyVulnerabilityForRangeAsync(
258258
{
259259
Console.Error.WriteLine(
260260
$@"[Metadata] Vulnerability advisory {advisoryDatabaseKey
261-
}, version {versionMetadata} of package {packageId} is marked vulnerable and is not in a vulnerable range!");
261+
}, version {versionMetadata.Identity.Version} of package {packageId} is marked vulnerable and is not in a vulnerable range!");
262262
HasErrors = true;
263263
}
264264
}

tests/GitHubVulnerabilities2Db.Facts/AdvisoryIngestorFacts.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Threading.Tasks;
77
using GitHubVulnerabilities2Db.GraphQL;
88
using GitHubVulnerabilities2Db.Ingest;
9+
using Microsoft.Extensions.Logging;
910
using Moq;
1011
using NuGet.Services.Entities;
1112
using NuGet.Versioning;
@@ -137,7 +138,8 @@ public MethodFacts()
137138
GitHubVersionRangeParserMock = new Mock<IGitHubVersionRangeParser>();
138139
Ingestor = new AdvisoryIngestor(
139140
PackageVulnerabilityServiceMock.Object,
140-
GitHubVersionRangeParserMock.Object);
141+
GitHubVersionRangeParserMock.Object,
142+
Mock.Of<ILogger<AdvisoryIngestor>>());
141143
}
142144

143145
public Mock<IPackageVulnerabilitiesManagementService> PackageVulnerabilityServiceMock { get; }

tests/NuGetGallery.Facts/Services/PackageVulnerabilitiesManagementServiceFacts.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,14 @@ public MethodFacts()
481481
.Verifiable();
482482

483483
Context.SetupDatabase(_databaseMock.Object);
484+
485+
// Entity Framework clears the collection. We use the Packages collection to know which packages to
486+
// mark as updated so it's important to use the collection prior to removing the range.
487+
// Related to: https://github.com/NuGet/Engineering/issues/4566
488+
Mock
489+
.Get(Context.VulnerableRanges)
490+
.Setup(x => x.Remove(It.IsAny<VulnerablePackageVersionRange>()))
491+
.Callback<VulnerablePackageVersionRange>(x => x.Packages.Clear());
484492
}
485493

486494
private Mock<IDbContextTransaction> _transactionMock { get; }

0 commit comments

Comments
 (0)