Skip to main content

Overview

VerifyGitHubVulnerabilities is a recurring, read-only monitoring job that ensures the NuGet Gallery’s vulnerability data stays in sync with GitHub’s Security Advisory Database. It fetches every advisory tagged for the NUGET ecosystem via GraphQL, then walks through the same ingestion pipeline used by GitHubVulnerabilities2Db — but instead of writing to the database it compares what should be there against what is there and logs errors for every mismatch.
This job is intentionally side-effect-free. It opens the Gallery database in read-only mode and never writes to SQL, Azure Blob Storage, or any other persistent store. Its only outputs are structured log entries and Application Insights metrics.
The two verification passes are independently toggleable:
  • Database verification — checks that every advisory exists in the PackageVulnerabilities table with the correct severity, advisory URL, and per-version VulnerablePackageRanges linkage.
  • Registration metadata verification — fetches NuGet V3 registration blobs for each affected package and confirms that every version in a vulnerable range carries the correct vulnerabilities array, and that no out-of-range version is incorrectly tagged.

GitHubVulnerabilities2Db

Sibling job that actually ingests GitHub advisories into the Gallery SQL database. VerifyGitHubVulnerabilities reuses its GraphQL query layer and ingestion pipeline but substitutes a verifying visitor for the writer.

NuGet Gallery (Web)

Consumes the vulnerability records that this job monitors. Discrepancies reported here indicate that package detail pages or V3 feeds may be serving incorrect security data.

GitHub Security Advisory API

Authoritative source of truth. Queried in full on every run (since epoch) using the same GraphQL query as the ingest job.

NuGet V3 Registration Blobs

The second verification target. The job uses NuGet.Protocol to download package metadata and inspect the embedded vulnerability arrays directly from CDN-served blobs.

Key Files and Classes

FileClass / TypePurpose
Program.csProgramConsole entry point; bootstraps JobRunner from NuGet.Jobs.
Job.csJob : JsonConfigurationJobOrchestrates the full run: queries GitHub, drives ingestion/verification, emits Application Insights metrics.
Configuration/VerifyGitHubVulnerabilitiesConfiguration.csVerifyGitHubVulnerabilitiesConfigurationExtends GraphQLQueryConfiguration with NuGetV3Index, VerifyDatabase, and VerifyRegistrationMetadata flags.
Verify/IPackageVulnerabilitiesVerifier.csIPackageVulnerabilitiesVerifierExtends IPackageVulnerabilitiesManagementService with a HasErrors flag so the job can emit a binary pass/fail metric.
Verify/PackageVulnerabilitiesVerifier.csPackageVulnerabilitiesVerifierCore verification engine. Implements both DB and registration-metadata checks; registered as SingleInstance in Autofac to accumulate the HasErrors flag across all advisories.
Scripts/Functions.ps1PowerShell helpers (Install-NuGetService / Uninstall-NuGetService) used by deploy scripts to manage the Windows service via NSSM.
Scripts/PreDeploy.ps1 / PostDeploy.ps1NSSM-based Windows service lifecycle hooks for blue/green deployment.

Dependencies

Internal Project References

ProjectRole
GitHubVulnerabilities2DbProvides the shared GitHub GraphQL query layer (IAdvisoryQueryService, IAdvisoryIngestor, AdvisoryQueryBuilder, GalleryDbVulnerabilityWriter, GitHubVersionRangeParser) and Gallery DB access stubs.
NuGet.Jobs.CommonBase class JsonConfigurationJob, JobRunner, Key Vault integration, and GalleryDbConfiguration SQL connection helpers.

NuGet / Framework Dependencies (resolved transitively)

Package / NamespacePurpose
AutofacIoC container; all services registered in ConfigureAutofacServices.
Microsoft.Extensions.ConfigurationReads appsettings.json and Key Vault secrets.
Microsoft.Extensions.LoggingStructured logging throughout; errors are the primary signal.
NuGet.Protocol / NuGet.Protocol.Core.TypesDownloads V3 registration metadata (PackageMetadataResource) to verify vulnerability arrays in blobs.
NuGet.VersioningParses VersionRange and NuGetVersion strings to determine which package versions fall within a vulnerable range.
NuGet.Services.LoggingITelemetryClient used to emit DataIsConsistent / DataIsInconsistent metrics to Application Insights.
System.Data.Entity (EF6)Include-based eager loading of Vulnerabilities and AffectedRanges from the Gallery DB.
Target framework: net472Full .NET Framework 4.7.2, required for compatibility with the shared Gallery EF6 context.

Notable Patterns and Implementation Details

Dual-registration pattern for the verifier. PackageVulnerabilitiesVerifier is registered in Autofac as both IPackageVulnerabilitiesManagementService (the interface the ingestion pipeline calls) and IPackageVulnerabilitiesVerifier (the interface the job checks for HasErrors). The SingleInstance() lifetime ensures the same object accumulates errors across the entire advisory set before Job.Run inspects it.
Full advisory scan on every run. Unlike incremental ingestion jobs, GetAdvisoriesSinceAsync is called with DateTimeOffset.MinValue, meaning the entire GitHub NuGet advisory corpus is fetched and verified on every execution. This is intentional — the goal is a complete consistency check, not delta processing.
Withdrawn advisory check is DB-only. The job explicitly skips registration-metadata verification for withdrawn advisories. Verifying withdrawals in metadata would require downloading every package/version blob to confirm absence — impractical at scale. Database coverage is considered sufficient for withdrawn entries.
ApplyExistingVulnerabilitiesToPackage is not implemented. This method from IPackageVulnerabilitiesManagementService throws NotImplementedException. It is never called during normal verification flow; the interface is implemented solely to satisfy the shared ingestion pipeline contract.
Thread-safe metadata caching via SemaphoreSlim. PackageVulnerabilitiesVerifier fans out verification tasks concurrently per advisory but serializes V3 metadata fetches with a static SemaphoreSlim(1) and a Dictionary cache keyed on packageId. This avoids redundant HTTP calls when multiple advisories affect the same package while remaining safe under Task.WhenAll parallelism.
Incoming package ID whitespace. GitHub advisory data can include leading or trailing spaces in package IDs. The verifier trims these (range.PackageId.Trim(' ')) before grouping ranges for metadata verification, preventing phantom cache misses and false-positive error reports.

Running Locally

VerifyGitHubVulnerabilities.exe \
  -Configuration appsettings.json \
  -InstrumentationKey <key> \
  -HeartbeatIntervalSeconds 60
Minimal appsettings.json:
{
  "GalleryDb": {
    "ConnectionString": "<Gallery SQL connection string>"
  },
  "Initialization": {
    "GitHubPersonalAccessToken": "<GitHub PAT>",
    "NuGetV3Index": "https://api.nuget.org/v3/index.json"
  },
  "KeyVault_VaultName": "<vault name>",
  "KeyVault_UseManagedIdentity": true
}
The Microsoft Entra ID client certificate for the app registration must be installed in the CurrentUser certificate store before running. Key Vault secrets (including the GitHub PAT) are resolved at startup via managed identity or the installed certificate.