Skip to main content
This project handles symbol packages (.snupkg), a NuGet-specific concept that has no equivalent in AI•Pkg. It should not be ported to the AI•Pkg platform and is documented here for NuGetGallery inventory completeness only.

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:
  1. ZIP safety check — ensures the .snupkg archive does not contain path traversal entries that could escape the extraction directory.
  2. Structural match — confirms every .pdb file in the snupkg has a corresponding .dll or .exe in the nupkg (no orphan symbol files).
  3. 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

FileClass / InterfacePurpose
Program.csProgramEntry point; calls JobRunner.RunOnce(new Job(), args)
Job.csJob : SubscriptionProcessorJob<SymbolsValidatorMessage>DI wiring — registers all services, configures two CloudBlobCoreFileStorageService instances (packages + validation containers), and registers ValidatorStateService with the SymbolsValidator name
SymbolsValidatorMessageHandler.csSymbolsValidatorMessageHandlerService Bus message handler; checks idempotency via IValidatorStateService, delegates to ISymbolsValidatorService, persists result, and optionally sends queue-back
SymbolsValidatorService.csSymbolsValidatorServiceCore validation logic: ZIP safety, structural PE/PDB matching, portable PDB format check, and SHA checksum verification using System.Reflection.Metadata
SymbolsFileService.csSymbolsFileServiceDownloads snupkg by URI and nupkg from blob storage (falls back from packages container to validation container)
SymbolsValidatorConfiguration.csSymbolsValidatorConfigurationPOCO config bound from SymbolsConfiguration section; holds three Azure Storage connection strings
TelemetryService.csTelemetryServiceApplication Insights telemetry; emits per-assembly and per-package validation results, duration, and working directory cleanup failures
ITelemetryService.csITelemetryServiceInterface extending ISubscriptionProcessorTelemetryService with symbol-specific tracking methods
ISymbolsFileService.csISymbolsFileServiceAbstracts DownloadSnupkgFileAsync (by URI) and DownloadNupkgFileAsync (by id + version)
ISymbolsValidatorService.csISymbolsValidatorServiceSingle method: ValidateSymbolsAsync(SymbolsValidatorMessage, CancellationToken)

Dependencies

NuGet Package References

PackagePurpose
System.Reflection.MetadataReads PE debug directory, portable PDB metadata, and PdbChecksum entries for cryptographic matching

Internal Project References

ProjectRole
NuGet.Services.Validation.OrchestratorProvides SubscriptionProcessorJob<T>, IValidatorStateService, ValidatorStateService, IFeatureFlagService, IPackageValidationEnqueuer, ValidatorName, IZipArchiveService, and validation response types
Validation.Symbols.CoreProvides 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.
Embedded PDBs are intentionally skipped. When PEReader.TryOpenAssociatedPortablePdb returns pdbPath == null, the PDB is embedded directly in the PE. The validator treats this as no external PDB to verify and moves on. Only external .pdb files are validated against checksums.
Working directory cleanup uses a retry loop, not a finalizer. After extracting files to a temp directory for validation, SymbolsValidatorService retries Directory.Delete in a loop with 1-second sleeps for up to 20 seconds. If the directory still exists after the timeout, a telemetry event is emitted and the directory is left on disk. This is a known operational quirk that can accumulate stale temp directories on the job host if antivirus or file handles interfere.
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 CodeMeaning
SymbolErrorCode_SnupkgContainsEntriesNotSafeForExtractionZIP path traversal detected
SymbolErrorCode_SnupkgDoesNotContainSymbolsNo .pdb files found in snupkg
SymbolErrorCode_MatchingAssemblyNotFoundOne or more .pdb files have no corresponding .dll/.exe
SymbolErrorCode_PdbIsNotPortablePDB magic bytes are not BSJB (not a Portable PDB)
SymbolErrorCode_ChecksumDoesNotMatchPDB hash does not match any PdbChecksum entry in the PE
UnknownBadImageFormatException thrown when opening associated PDB

AI•Pkg Applicability

This entire project is out of scope for AI•Pkg. AI•Pkg packages (.aipkg) do not include native assemblies, PDB files, or debug symbols. The concepts of snupkg, portable PDB validation, and PE checksum matching are specific to the .NET binary package ecosystem and should be deleted when forking NuGetGallery for AI•Pkg. See also: Tech Stack plan which explicitly lists symbol package handling as a deletion target.