Beispiel #1
0
        public async Task <ValidatorStatus> TryUpdateValidationStatusAsync(
            INuGetValidationRequest request,
            ValidatorStatus validatorStatus,
            ValidationStatus desiredState)
        {
            validatorStatus.State = desiredState;

            var result = await SaveStatusAsync(validatorStatus);

            if (result == SaveStatusResult.StaleStatus)
            {
                // The save operation fails if another instance of this service has already modified the status.
                // This may happen due to repeated operations kicked off by the Orchestrator. Return the result
                // from the other update.
                _logger.LogWarning(
                    Error.ValidatorStateServiceFailedToUpdateStatus,
                    "Failed to save validation status for {ValidationId} ({PackageId} {PackageVersion}) as the current status is stale",
                    request.ValidationId,
                    request.PackageId,
                    request.PackageVersion);

                return(await GetStatusAsync(request));
            }
            else if (result != SaveStatusResult.Success)
            {
                throw new NotSupportedException($"Unknown {nameof(SaveStatusResult)}: {result}");
            }

            return(validatorStatus);
        }
Beispiel #2
0
        /// <summary>
        /// The pattern used for the StartAsync:
        /// 1. Check if a validation was already started
        /// 2. Only if a validation was not started queue the message to be processed.
        /// 3. After the message is queued, update the ValidatorStatus for the <paramref name="request"/>.
        /// </summary>
        /// <param name="request">The request to be send to the validator job queue.</param>
        /// <returns>The validation status.</returns>
        public async Task <INuGetValidationResponse> StartAsync(INuGetValidationRequest request)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            var validatorStatus = await _validatorStateService.GetStatusAsync(request);

            // See issue https://github.com/NuGet/NuGetGallery/issues/6249
            validatorStatus.ValidatingType = ValidatingType.SymbolPackage;

            if (validatorStatus.State != ValidationStatus.NotStarted)
            {
                _logger.LogWarning(
                    "Symbol validation for {PackageId} {PackageNormalizedVersion} has already started.",
                    request.PackageId,
                    request.PackageVersion);

                return(validatorStatus.ToNuGetValidationResponse());
            }

            // Due to race conditions or failure of method TryAddValidatorStatusAsync the same message can be enqueued multiple times
            // Log this information to postmortem evaluate this behavior
            _telemetryService.TrackSymbolsMessageEnqueued(request.PackageId, request.PackageVersion, ValidatorName.SymbolsValidator, request.ValidationId);
            await _symbolMessageEnqueuer.EnqueueSymbolsValidationMessageAsync(request);

            var result = await _validatorStateService.TryAddValidatorStatusAsync(request, validatorStatus, ValidationStatus.Incomplete);

            return(result.ToNuGetValidationResponse());
        }
Beispiel #3
0
        public async Task <ValidatorStatus> TryAddValidatorStatusAsync(INuGetValidationRequest request, ValidatorStatus status, ValidationStatus desiredState)
        {
            status.State = desiredState;

            var result = await AddStatusAsync(status);

            if (result == AddStatusResult.StatusAlreadyExists)
            {
                // The add operation fails if another instance of this service has already created the status.
                // This may happen due to repeated operations kicked off by the Orchestrator. Return the result from
                // the other add operation.
                _logger.LogWarning(
                    Error.ValidatorStateServiceFailedToAddStatus,
                    "Failed to add validation status for {ValidationId} ({PackageId} {PackageVersion}) as a record already exists",
                    request.ValidationId,
                    request.PackageId,
                    request.PackageVersion);

                return(await GetStatusAsync(request));
            }
            else if (result != AddStatusResult.Success)
            {
                throw new NotSupportedException($"Unknown {nameof(AddStatusResult)}: {result}");
            }

            return(status);
        }
        public async Task CleanUpAsync(INuGetValidationRequest request)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            var validatorStatus = await _validatorStateService.GetStatusAsync(request);

            if (validatorStatus.NupkgUrl == null)
            {
                return;
            }

            if (!_configuration.RepositorySigningEnabled)
            {
                _logger.LogWarning(
                    "Skipping cleanup of .nupkg for validation ID {ValidationId} ({PackageId} {PackageVersion})",
                    request.ValidationId,
                    request.PackageId,
                    request.PackageVersion);

                return;
            }

            _logger.LogInformation(
                "Cleaning up the .nupkg URL for validation ID {ValidationId} ({PackageId} {PackageVersion}).",
                request.ValidationId,
                request.PackageId,
                request.PackageVersion);

            var blob = _blobProvider.GetBlobFromUrl(validatorStatus.NupkgUrl);
            await blob.DeleteIfExistsAsync();
        }
        public ScanAndSignEnqueuerFactsBase()
        {
            _topicClientMock           = new Mock <ITopicClient>();
            _serializerMock            = new Mock <IBrokeredMessageSerializer <ScanAndSignMessage> >();
            _logger                    = Mock.Of <ILogger <ScanAndSignEnqueuer> >();
            _configurationAccessorMock = new Mock <IOptionsSnapshot <ScanAndSignEnqueuerConfiguration> >();

            _configuration = new ScanAndSignEnqueuerConfiguration();
            _configurationAccessorMock
            .SetupGet(ca => ca.Value)
            .Returns(_configuration);

            _target = new ScanAndSignEnqueuer(
                _topicClientMock.Object,
                _serializerMock.Object,
                _configurationAccessorMock.Object,
                _logger);

            _validationRequest = new NuGetValidationRequest(Guid.NewGuid(), 42, "somepackage", "someversion", "https://example.com/testpackage.nupkg");
            _owners            = new List <string> {
                "Billy", "Bob"
            };

            _serializedMessage = new BrokeredMessageWrapper("somedata");

            _serializerMock
            .Setup(s => s.Serialize(It.IsAny <ScanAndSignMessage>()))
            .Callback <ScanAndSignMessage>(m => _capturedMessage = m)
            .Returns(_serializedMessage);

            _topicClientMock
            .Setup(tc => tc.SendAsync(It.IsAny <IBrokeredMessage>()))
            .Callback <IBrokeredMessage>(m => _capturedBrokeredMessage = m)
            .Returns(Task.CompletedTask);
        }
 /// <summary>
 /// Find the state of a package's signing.
 /// </summary>
 /// <param name="request">The validation request containing the package whose signing state should be fetched.</param>
 /// <returns>The package's signing state.</returns>
 private Task <PackageSigningState> FindPackageSigningStateAsync(INuGetValidationRequest request)
 {
     return(_validationContext
            .PackageSigningStates
            .Where(p => p.PackageKey == request.PackageKey)
            .FirstAsync());
 }
Beispiel #7
0
        public async Task <INuGetValidationResponse> StartAsync(INuGetValidationRequest request)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            var validatorStatus = await _validatorStateService.GetStatusAsync(request);

            if (validatorStatus.State != ValidationStatus.NotStarted)
            {
                _logger.LogWarning(
                    "Scan only validation with validation Id {ValidationId} ({PackageId} {PackageVersion}) has already started.",
                    request.ValidationId,
                    request.PackageId,
                    request.PackageVersion);

                return(validatorStatus.ToNuGetValidationResponse());
            }

            if (ShouldSkipScan(request))
            {
                return(NuGetValidationResponse.Succeeded);
            }

            await _scanAndSignEnqueuer.EnqueueScanAsync(request.ValidationId, request.NupkgUrl);

            var status = await _validatorStateService.TryAddValidatorStatusAsync(request, validatorStatus, ValidationStatus.Incomplete);

            return(status.ToNuGetValidationResponse());
        }
Beispiel #8
0
        public async Task <ValidatorStatus> GetStatusAsync(INuGetValidationRequest request)
        {
            var status = await GetStatusAsync(request.ValidationId);

            if (status == null)
            {
                return(new ValidatorStatus
                {
                    ValidationId = request.ValidationId,
                    PackageKey = request.PackageKey,
                    ValidatorName = _validatorName,
                    State = ValidationStatus.NotStarted,
                    ValidatorIssues = new List <ValidatorIssue>(),
                });
            }
            else if (status.PackageKey != request.PackageKey)
            {
                throw new ArgumentException(
                          $"Validation expected package key {status.PackageKey}, actual {request.PackageKey}",
                          nameof(request));
            }
            else if (status.ValidatorName != _validatorName)
            {
                throw new ArgumentException(
                          $"Validation expected validator {status.ValidatorName}, actual {_validatorName}",
                          nameof(request));
            }

            return(status);
        }
Beispiel #9
0
        private async Task <ValidatorStatus> StartInternalAsync(INuGetValidationRequest request)
        {
            // Check that this is the first validation for this specific request.
            var validatorStatus = await _validatorStateService.GetStatusAsync(request);

            if (validatorStatus.State != ValidationStatus.NotStarted)
            {
                _logger.LogWarning(
                    "Package Signing validation with validationId {ValidationId} ({PackageId} {PackageVersion}) has already started.",
                    request.ValidationId,
                    request.PackageId,
                    request.PackageVersion);

                return(validatorStatus);
            }

            // Kick off the verification process. Note that the jobs will not verify the package until the
            // state of this validator has been persisted to the database.
            using (_telemetryService.TrackDurationToStartPackageSigningValidator(request.PackageId, request.PackageVersion))
            {
                await _signatureVerificationEnqueuer.EnqueueProcessSignatureAsync(request, RequiresRepositorySignature);

                return(await _validatorStateService.TryAddValidatorStatusAsync(request, validatorStatus, ValidationStatus.Incomplete));
            }
        }
        private async Task <ValidatorStatus> GetStatusAsync(INuGetValidationRequest request)
        {
            // Look up this validator's state in the database.
            var status = await _validatorStateService.GetStatusAsync(request);

            if (status.State != ValidationStatus.Incomplete)
            {
                return(status);
            }

            // Wait until ALL certificate validations kicked off by this validation request have finished.
            if (!await AllCertificateValidationsAreFinishedAsync(request))
            {
                // We know this status is incomplete.
                return(status);
            }

            // All of the requested certificate validations have finished. At this point, the signature
            // may have a status of "Unknown" if the package is at ingestion and its signature has passed
            // all validations, "Invalid" if one or more of the signature's certificates has failed validations,
            // or "InGracePeriod" or "Valid" if this is a revalidation request.
            var signature = await FindAuthorSignatureAsync(request);

            if (signature == null)
            {
                _logger.LogError(
                    "Could not find author signature for {PackageKey} {PackageId} {PackageVersion} {ValidationId}",
                    request.PackageKey,
                    request.PackageId,
                    request.PackageVersion,
                    request.ValidationId);

                throw new InvalidOperationException($"Package with key {request.PackageKey} does not have an author signature");
            }

            if (signature.Status == PackageSignatureStatus.Invalid)
            {
                _logger.LogWarning(
                    "Failing validation {ValidationId} ({PackageId} {PackageVersion}) due to invalidated signature: {SignatureKey}",
                    request.ValidationId,
                    request.PackageId,
                    request.PackageVersion,
                    signature.Key);

                return(await _validatorStateService.TryUpdateValidationStatusAsync(request, status, ValidationStatus.Failed));
            }
            else
            {
                _logger.LogInformation(
                    "Successful validation {ValidationId} ({PackageId} {PackageVersion})",
                    request.ValidationId,
                    request.PackageId,
                    request.PackageVersion);

                PromoteSignature(request, signature);

                return(await _validatorStateService.TryUpdateValidationStatusAsync(request, status, ValidationStatus.Succeeded));
            }
        }
 /// <summary>
 /// Check whether all certificate validations for the given validation request are finished.
 /// </summary>
 /// <param name="request">The validation request that started the certificate validations.</param>
 /// <returns>Whether the certificate validations are ALL finished.</returns>
 private Task <bool> AllCertificateValidationsAreFinishedAsync(INuGetValidationRequest request)
 {
     // Incomplete CertificateValidation have a Status of NULL.
     return(_validationContext
            .CertificateValidations
            .Where(v => v.ValidationId == request.ValidationId)
            .AllAsync(v => v.Status.HasValue));
 }
 /// <summary>
 /// Find the package's author signature, if one exists.
 /// </summary>
 /// <param name="request">The validation request containing the package whose signatures should be fetched.</param>
 /// <returns>The package's author signature with its certificates, or null.</returns>
 private Task <PackageSignature> FindAuthorSignatureAsync(INuGetValidationRequest request)
 {
     return(_validationContext
            .PackageSignatures
            .Include(s => s.EndCertificate)
            .Include(s => s.TrustedTimestamps.Select(t => t.EndCertificate))
            .Where(s => s.Type == PackageSignatureType.Author)
            .SingleOrDefaultAsync(s => s.PackageKey == request.PackageKey));
 }
        /// <summary>
        /// Mark a package's signing state and its revoked signatures as invalid. This method does NOT
        /// persist entity changes to the database.
        /// </summary>
        /// <param name="request">The request to validate a package.</param>
        /// <param name="package">The package's overall signing state that should be invalidated.</param>
        /// <param name="signature">The package's signatures that should be invalidated.</param>
        /// <returns>A task that completes when the entities have been updated.</returns>
        private void InvalidatePackageSignature(INuGetValidationRequest request, PackageSigningState package, PackageSignature signature)
        {
            _logger.LogWarning(
                "Invalidating package {PackageId} {PackageVersion} due to revoked signatures.",
                request.PackageId,
                request.PackageVersion);

            package.SigningStatus = PackageSigningStatus.Invalid;
            signature.Status      = PackageSignatureStatus.Invalid;
        }
Beispiel #14
0
        public async Task <INuGetValidationResponse> GetResponseAsync(INuGetValidationRequest request)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            var validatorStatus = await _validatorStateService.GetStatusAsync(request);

            return(validatorStatus.ToNuGetValidationResponse());
        }
        public async Task <INuGetValidationResponse> GetResponseAsync(INuGetValidationRequest request)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            var result = await GetProcessorStatusAsync(request);

            return(result.ToNuGetValidationResponse());
        }
        /// <summary>
        /// Decide whether the valid signature should be considered "Valid" or "InGracePeriod".
        /// </summary>
        /// <param name="request">The validation request for the package whose signature should be inspected.</param>
        /// <param name="signature">The valid signature whose status should be decided.</param>
        /// <returns>True if the signature should be "Valid", false if it should be "InGracePeriod".</returns>
        private bool IsValidSignatureOutOfGracePeriod(INuGetValidationRequest request, PackageSignature signature)
        {
            bool IsCertificateStatusPastTime(EndCertificate certificate, DateTime time)
            {
                return(certificate.StatusUpdateTime.HasValue && certificate.StatusUpdateTime > time);
            }

            var signingTime = signature.TrustedTimestamps.Max(t => t.Value);

            // Ensure the timestamps' certificate statuses are fresher than the signature.
            foreach (var timestamp in signature.TrustedTimestamps)
            {
                // A valid signature should NEVER have a timestamp whose end certificate is revoked.
                // Note that it is possible for a valid signature to have an invalid certificate as
                // certain certificate statuses, like "NotTimeNested", do not affect signatures.
                if (timestamp.EndCertificate.Status == EndCertificateStatus.Revoked)
                {
                    _logger.LogError(
                        Error.PackageCertificateValidationInvalidSignatureState,
                        "Valid signature cannot have a timestamp whose end certificate is revoked ({ValidationId}, {PackageId} {PackageVersion})",
                        request.ValidationId,
                        request.PackageId,
                        request.PackageVersion,
                        signature.Status);

                    throw new InvalidOperationException(
                              $"ValidationId {request.ValidationId} has valid signature with a timestamp whose end certificate is revoked");
                }

                if (!IsCertificateStatusPastTime(timestamp.EndCertificate, signingTime))
                {
                    return(false);
                }
            }

            // A signature can be valid even if its certificate is revoked as long as the certificate
            // revocation date begins after the signature was created. The validation pipeline does
            // not revalidate revoked certificates, thus, a valid package signature with a revoked
            // certificate is considered out of the grace period regardless of the certificate's
            // status update time.
            if (signature.EndCertificate.Status != EndCertificateStatus.Revoked)
            {
                // Ensure the signature's certificate status is fresher than the signature.
                if (!IsCertificateStatusPastTime(signature.EndCertificate, signingTime))
                {
                    return(false);
                }
            }

            return(true);
        }
 /// <summary>
 /// Creates a <see cref="SymbolsServerRequest"/> from a <see cref="INuGetValidationRequest"/>
 /// </summary>
 /// <param name="validationRequest">The <see cref="INuGetValidationRequest"/>.</param>
 /// <param name="status">The <see cref="SymbolsPackageIngestRequestStatus"/>.</param>
 /// <returns></returns>
 public static SymbolsServerRequest CreateFromValidationRequest(
     INuGetValidationRequest validationRequest,
     SymbolsPackageIngestRequestStatus status,
     string requestName)
 {
     return(new SymbolsServerRequest()
     {
         Created = DateTime.UtcNow,
         LastUpdated = DateTime.UtcNow,
         RequestName = requestName,
         RequestStatusKey = status,
         SymbolsKey = validationRequest.PackageKey
     });
 }
Beispiel #18
0
        public async Task EnqueueSymbolsValidationMessageAsync(INuGetValidationRequest request)
        {
            var message = new SymbolsValidatorMessage(validationId: request.ValidationId,
                                                      symbolPackageKey: request.PackageKey,
                                                      packageId: request.PackageId,
                                                      packageNormalizedVersion: request.PackageVersion,
                                                      snupkgUrl: request.NupkgUrl);
            var brokeredMessage = _serializer.Serialize(message);

            var visibleAt = DateTimeOffset.UtcNow + (_messageDelay ?? TimeSpan.Zero);

            brokeredMessage.ScheduledEnqueueTimeUtc = visibleAt;

            await _topicClient.SendAsync(brokeredMessage);
        }
        /// <summary>
        /// Promote valid signatures from "Unknown" status to either "Valid" or "InGracePeriod".
        /// </summary>
        /// <param name="request">The validation request for the package whose signature should be promoted.</param>
        /// <param name="signatures">The valid signatures that should be promoted.</param>
        private void PromoteSignature(INuGetValidationRequest request, PackageSignature signature)
        {
            var newSignatureStatus = signature.IsPromotable()
                                        ? PackageSignatureStatus.Valid
                                        : PackageSignatureStatus.InGracePeriod;

            _logger.LogInformation(
                "Promoting package {PackageId} {PackageVersion} signature from status {OldSignatureStatus} to status {NewSignatureStatus}",
                request.PackageId,
                request.PackageVersion,
                signature.Status,
                newSignatureStatus);

            signature.Status = newSignatureStatus;
        }
Beispiel #20
0
        /// <summary>
        /// The pattern used for the StartAsync:
        /// 1. Check if an ingestion for the specific symbols package key was already started
        /// 2. Only if a ingestion was not started queue the message to be processed.
        /// 3. After the message is queued, update the SymbolServerRequests table.
        /// </summary>
        /// <param name="request">The request to be sent to the ingester job queue.</param>
        /// <returns>The operation status as <see cref="INuGetValidationResponse"/>.</returns>
        public async Task <INuGetValidationResponse> StartAsync(INuGetValidationRequest request)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            var symbolsRequest = await _symbolsValidationEntitiesService.GetSymbolsServerRequestAsync(request);

            var response = SymbolsValidationEntitiesService.ToValidationResponse(symbolsRequest);

            if (response.Status != ValidationStatus.NotStarted)
            {
                _logger.LogWarning(
                    "Symbol ingestion for {PackageId} {PackageNormalizedVersion} {SymbolsPackageKey} has already started.",
                    request.PackageId,
                    request.PackageVersion,
                    request.PackageKey);

                return(response);
            }

            _telemetryService.TrackSymbolsMessageEnqueued(request.PackageId, request.PackageVersion, ValidatorName.SymbolsIngester, request.ValidationId);
            var message = await _symbolMessageEnqueuer.EnqueueSymbolsIngestionMessageAsync(request);

            var newSymbolsRequest  = SymbolsValidationEntitiesService.CreateFromValidationRequest(request, SymbolsPackageIngestRequestStatus.Ingesting, message.RequestName);
            var savedSymbolRequest = await _symbolsValidationEntitiesService.AddSymbolsServerRequestAsync(newSymbolsRequest);

            if (savedSymbolRequest.RequestStatusKey != SymbolsPackageIngestRequestStatus.Ingesting)
            {
                _logger.LogWarning(
                    "The symbols ingestion request already in the database. RequestStatus:{Status} for {PackageId} {PackageNormalizedVersion} {SymbolsPackageKey}.",
                    newSymbolsRequest.RequestStatusKey,
                    request.PackageId,
                    request.PackageVersion,
                    request.PackageKey);
            }
            else
            {
                _logger.LogInformation(
                    "The symbols ingestion request added to the database. RequestStatus:{Status} for {PackageId} {PackageNormalizedVersion} {SymbolsPackageKey}.",
                    newSymbolsRequest.RequestStatusKey,
                    request.PackageId,
                    request.PackageVersion,
                    request.PackageKey);
            }
            return(SymbolsValidationEntitiesService.ToValidationResponse(savedSymbolRequest));
        }
        public async Task EnqueueVerificationAsync(INuGetValidationRequest request, EndCertificate certificate)
        {
            var message = new CertificateValidationMessage(
                certificate.Key,
                request.ValidationId,
                revalidateRevokedCertificate: false,
                sendCheckValidator: true);

            var brokeredMessage = _serializer.Serialize(message);

            var visibleAt = DateTimeOffset.UtcNow + (_configuration.Value.MessageDelay ?? TimeSpan.Zero);

            brokeredMessage.ScheduledEnqueueTimeUtc = visibleAt;

            await _topicClient.SendAsync(brokeredMessage);
        }
Beispiel #22
0
        public Task EnqueueProcessSignatureAsync(INuGetValidationRequest request, bool requireRepositorySignature)
        {
            var message = new SignatureValidationMessage(
                request.PackageId,
                request.PackageVersion,
                new Uri(request.NupkgUrl),
                request.ValidationId,
                requireRepositorySignature);
            var brokeredMessage = _serializer.Serialize(message);

            var visibleAt = DateTimeOffset.UtcNow + (_configuration.Value.MessageDelay ?? TimeSpan.Zero);

            brokeredMessage.ScheduledEnqueueTimeUtc = visibleAt;

            return(_topicClient.SendAsync(brokeredMessage));
        }
        public async Task <INuGetValidationResponse> StartAsync(INuGetValidationRequest request)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            var processorStatus = await GetProcessorStatusAsync(request);

            if (processorStatus.State != ValidationStatus.NotStarted)
            {
                _logger.LogWarning(
                    "Scan and Sign validation with validation Id {ValidationId} ({PackageId} {PackageVersion}) has already started.",
                    request.ValidationId,
                    request.PackageId,
                    request.PackageVersion);

                return(processorStatus.ToNuGetValidationResponse());
            }

            var owners = FindPackageOwners(request);

            if (await ShouldRepositorySignAsync(request))
            {
                _logger.LogInformation(
                    "Repository signing {PackageId} {PackageVersion} with {ServiceIndex} and {Owners}",
                    request.PackageId,
                    request.PackageVersion,
                    _configuration.V3ServiceIndexUrl,
                    owners);

                await _scanAndSignEnqueuer.EnqueueScanAndSignAsync(request.ValidationId, request.NupkgUrl, _configuration.V3ServiceIndexUrl, owners);
            }
            else
            {
                if (ShouldSkipScan(request))
                {
                    return(NuGetValidationResponse.Succeeded);
                }

                await _scanAndSignEnqueuer.EnqueueScanAsync(request.ValidationId, request.NupkgUrl);
            }

            var result = await _validatorStateService.TryAddValidatorStatusAsync(request, processorStatus, ValidationStatus.Incomplete);

            return(result.ToNuGetValidationResponse());
        }
        private async Task <ValidatorStatus> GetProcessorStatusAsync(INuGetValidationRequest request)
        {
            var validatorStatus = await _validatorStateService.GetStatusAsync(request);

            if (!_configuration.RepositorySigningEnabled && validatorStatus.NupkgUrl != null)
            {
                _logger.LogWarning(
                    "Suppressing .nupkg url as repository signing is disabled for {ValidationId} ({PackageId} {PackageVersion})",
                    request.ValidationId,
                    request.PackageId,
                    request.PackageVersion);

                validatorStatus.NupkgUrl = null;
            }

            return(validatorStatus);
        }
        /// <summary>
        /// Enqueue certificate verifications and add <see cref="EndCertificateValidation"/> entities
        /// for each validation. Note that this does NOT save the entity context!
        /// </summary>
        /// <param name="request">The package validation request.</param>
        /// <param name="certificates">The certificates that should be verified.</param>
        /// <returns>A task that completes when all certificate verifications have been enqueued.</returns>
        private Task StartCertificateValidationsAsync(INuGetValidationRequest request, IEnumerable <EndCertificate> certificates)
        {
            var startCertificateVerificationTasks = new List <Task>();

            foreach (var certificate in certificates)
            {
                startCertificateVerificationTasks.Add(_certificateVerificationEnqueuer.EnqueueVerificationAsync(request, certificate));

                _validationContext.CertificateValidations.Add(new EndCertificateValidation
                {
                    ValidationId   = request.ValidationId,
                    EndCertificate = certificate,
                    Status         = null,
                });
            }

            return(Task.WhenAll(startCertificateVerificationTasks));
        }
Beispiel #26
0
        public async Task CleanUpAsync(INuGetValidationRequest request)
        {
            var validatorStatus = await _validatorStateService.GetStatusAsync(request);

            if (validatorStatus.NupkgUrl == null)
            {
                return;
            }

            _logger.LogInformation(
                "Cleaning up the .nupkg URL for validation ID {ValidationId} ({PackageId} {PackageVersion}).",
                request.ValidationId,
                request.PackageId,
                request.PackageVersion);

            var blob = _blobProvider.GetBlobFromUrl(validatorStatus.NupkgUrl);
            await blob.DeleteIfExistsAsync();
        }
        private List <string> FindPackageOwners(INuGetValidationRequest request)
        {
            var registration = _packageService.FindPackageRegistrationById(request.PackageId);

            if (registration == null)
            {
                _logger.LogError("Attempted to validate package that has no package registration");

                throw new InvalidOperationException($"Registration for package id {request.PackageId} does not exist");
            }

            return(registration
                   .Owners
                   .Select(o => o.Username)
                   .ToList()
                   .OrderBy(u => u, StringComparer.InvariantCultureIgnoreCase)
                   .ToList());
        }
        private INuGetValidationResponse Validate(INuGetValidationRequest request, INuGetValidationResponse response)
        {
            /// The package signature validator runs after the <see cref="PackageSignatureProcessor" />.
            /// All signature validation issues should be caught and handled by the processor.
            if (response.Status == ValidationStatus.Failed || response.NupkgUrl != null)
            {
                if (!_config.RepositorySigningEnabled)
                {
                    _logger.LogInformation(
                        "Ignoring invalid validation response in package signature validator as repository signing is disabled. " +
                        "Status = {ValidationStatus}, Nupkg URL = {NupkgUrl}, validation issues = {Issues}",
                        response.Status,
                        response.NupkgUrl,
                        response.Issues.Select(i => i.IssueCode));

                    return(NuGetValidationResponse.Succeeded);
                }

                _logger.LogCritical(
                    "Unexpected validation response in package signature validator. This may be caused by an invalid repository " +
                    "signature. Throwing an exception to force this validation to dead-letter. " +
                    "Status = {ValidationStatus}, Nupkg URL = {NupkgUrl}, validation issues = {Issues}",
                    response.Status,
                    response.NupkgUrl,
                    response.Issues.Select(i => i.IssueCode));

                throw new InvalidOperationException("Package signature validator has an unexpected validation response");
            }

            /// Suppress all validation issues. The <see cref="PackageSignatureProcessor"/> should
            /// have already reported any issues related to the author signature. Customers should
            /// not be notified of validation issues due to the repository signature.
            if (response.Issues.Count != 0)
            {
                _logger.LogWarning(
                    "Ignoring {ValidationIssueCount} validation issues from response. Issues: {Issues}",
                    response.Issues.Count,
                    response.Issues.Select(i => i.IssueCode));

                return(new NuGetValidationResponse(response.Status));
            }

            return(response);
        }
        private bool ShouldSkipScan(INuGetValidationRequest request)
        {
            var package = _packageService.FindPackageByIdAndVersionStrict(
                request.PackageId,
                request.PackageVersion);

            if (!_criteriaEvaluator.IsMatch(_configuration.PackageCriteria, package))
            {
                _logger.LogInformation(
                    "The scan for {ValidationId} ({PackageId} {PackageVersion}) was skipped due to package criteria configuration.",
                    request.ValidationId,
                    request.PackageId,
                    request.PackageVersion);

                return(true);
            }

            return(false);
        }
        private async Task <bool> ShouldRepositorySignAsync(INuGetValidationRequest request)
        {
            var hasRepositorySignature = await _validationContext
                                         .PackageSignatures
                                         .Where(s => s.PackageKey == request.PackageKey)
                                         .Where(s => s.Type == PackageSignatureType.Repository)
                                         .AnyAsync();

            if (hasRepositorySignature)
            {
                _logger.LogInformation(
                    "Package {PackageId} {PackageVersion} already has a repository signature. Scanning instead of signing package",
                    request.PackageId,
                    request.PackageVersion);

                return(false);
            }

            return(true);
        }