Overview
Validation.Symbols is a background job (console executable) that runs as part
of the NuGet.org package validation pipeline. When a publisher uploads a symbol
package (.snupkg) alongside a regular NuGet package (.nupkg), this job is
triggered via an Azure Service Bus message to verify that the symbols are valid
before they are made publicly available for debugger consumption.
The job performs three layers of verification:
- ZIP safety check — ensures the
.snupkgarchive does not contain path traversal entries that could escape the extraction directory. - Structural match — confirms every
.pdbfile in the snupkg has a corresponding.dllor.exein the nupkg (no orphan symbol files). - Portable PDB + checksum validation — reads PE debug directory entries,
verifies the PDB is in Portable PDB format (magic bytes
BSJB), and cryptographically confirms the PDB checksum matches the embedded checksum record in the PE file.
Role in the NuGetGallery Ecosystem
Validation Orchestrator
Receives
SymbolsValidatorMessage from the orchestrator via Azure Service
Bus. Reports back Succeeded or Failed with structured issue codes.Azure Blob Storage
Downloads both the
.snupkg (from a URI) and the corresponding .nupkg
(from the packages or validation container) to perform cross-file checks.Validator State DB
Writes final validation status to the
ValidatorStatuses table via
IValidatorStateService, keyed by ValidatorName.SymbolsValidator.Feature Flags
Conditionally sends a queue-back message to the orchestrator after saving
status, controlled by
IFeatureFlagService.IsQueueBackEnabled().This job is registered as
ValidatorName.SymbolsValidator in the validation
orchestrator. The orchestrator manages retry logic and overall package
publication gating — this job only performs the symbol-specific checks.Key Files and Classes
| File | Class / Interface | Purpose |
|---|---|---|
Program.cs | Program | Entry point; calls JobRunner.RunOnce(new Job(), args) |
Job.cs | Job : SubscriptionProcessorJob<SymbolsValidatorMessage> | DI wiring — registers all services, configures two CloudBlobCoreFileStorageService instances (packages + validation containers), and registers ValidatorStateService with the SymbolsValidator name |
SymbolsValidatorMessageHandler.cs | SymbolsValidatorMessageHandler | Service Bus message handler; checks idempotency via IValidatorStateService, delegates to ISymbolsValidatorService, persists result, and optionally sends queue-back |
SymbolsValidatorService.cs | SymbolsValidatorService | Core validation logic: ZIP safety, structural PE/PDB matching, portable PDB format check, and SHA checksum verification using System.Reflection.Metadata |
SymbolsFileService.cs | SymbolsFileService | Downloads snupkg by URI and nupkg from blob storage (falls back from packages container to validation container) |
SymbolsValidatorConfiguration.cs | SymbolsValidatorConfiguration | POCO config bound from SymbolsConfiguration section; holds three Azure Storage connection strings |
TelemetryService.cs | TelemetryService | Application Insights telemetry; emits per-assembly and per-package validation results, duration, and working directory cleanup failures |
ITelemetryService.cs | ITelemetryService | Interface extending ISubscriptionProcessorTelemetryService with symbol-specific tracking methods |
ISymbolsFileService.cs | ISymbolsFileService | Abstracts DownloadSnupkgFileAsync (by URI) and DownloadNupkgFileAsync (by id + version) |
ISymbolsValidatorService.cs | ISymbolsValidatorService | Single method: ValidateSymbolsAsync(SymbolsValidatorMessage, CancellationToken) |
Dependencies
NuGet Package References
| Package | Purpose |
|---|---|
System.Reflection.Metadata | Reads PE debug directory, portable PDB metadata, and PdbChecksum entries for cryptographic matching |
Internal Project References
| Project | Role |
|---|---|
NuGet.Services.Validation.Orchestrator | Provides SubscriptionProcessorJob<T>, IValidatorStateService, ValidatorStateService, IFeatureFlagService, IPackageValidationEnqueuer, ValidatorName, IZipArchiveService, and validation response types |
Validation.Symbols.Core | Provides SymbolsValidatorMessage, SymbolsValidatorMessageSerializer, IZipArchiveService, ZipArchiveService, Settings (working directory), and ingester message types |
Notable Patterns and Implementation Details
Dual-container fallback for nupkg downloads.
SymbolsFileService first
checks the main packages blob container, then falls back to the validation
container. This handles the case where a symbol package is uploaded alongside
a nupkg that is still awaiting final publication (sitting in the validation
container rather than the live packages container).Checksum algorithm is multi-hash tolerant. A PE file may embed multiple
PdbChecksum debug directory entries using different hash algorithms
(e.g., SHA-256, SHA-384). The validator computes the PDB hash for each
algorithm found and accepts a match from any one of them, following the
Portable PDB checksum spec.
The 20-byte PDB ID region is zeroed before hashing, as required by the spec.DB save has a fixed 5-retry loop without exponential backoff.
SymbolsValidatorMessageHandler.SaveStatusAsync retries up to 4 times
(the loop condition is ++currentRetry < maxRetries where MaxDBSaveRetry = 5,
so the effective max is 4 attempts). If all retries fail, the message is
requeued, which means the validation will re-run. The service is therefore
idempotent by design — duplicate messages with a terminal state are detected
and dropped immediately.Validation Failure Codes
| Issue Code | Meaning |
|---|---|
SymbolErrorCode_SnupkgContainsEntriesNotSafeForExtraction | ZIP path traversal detected |
SymbolErrorCode_SnupkgDoesNotContainSymbols | No .pdb files found in snupkg |
SymbolErrorCode_MatchingAssemblyNotFound | One or more .pdb files have no corresponding .dll/.exe |
SymbolErrorCode_PdbIsNotPortable | PDB magic bytes are not BSJB (not a Portable PDB) |
SymbolErrorCode_ChecksumDoesNotMatch | PDB hash does not match any PdbChecksum entry in the PE |
Unknown | BadImageFormatException thrown when opening associated PDB |