public async Task ProcessAuditHappyPath()
        {
            // Arrange
            await using var context = JosekiTestsDb.CreateUniqueContext();
            var parser      = new ConfigurationParser("config.sample.yaml");
            var checksCache = new ChecksCache(parser, context, new MemoryCache(new MemoryCacheOptions()));

            var blobsMock = new Mock <IBlobStorageProcessor>(MockBehavior.Strict);
            var dbMock    = new Mock <IJosekiDatabase>();
            var queueMock = new Mock <IQueue>();

            var ownershipCache = new OwnershipCache(context, new MemoryCache(new MemoryCacheOptions()));
            var postProcessor  = new Mock <ExtractOwnershipProcessor>(context, ownershipCache);
            var processor      = new PolarisAuditProcessor(blobsMock.Object, dbMock.Object, checksCache, queueMock.Object, postProcessor.Object);

            var container = new ScannerContainer(Path.Combine("audits", "samples", "polaris"))
            {
                Metadata = new ScannerMetadata
                {
                    Type = ScannerType.Polaris,
                    Id   = Guid.NewGuid().ToString(),
                },
            };
            var audit = new AuditBlob {
                Name = "meta.json", ParentContainer = container
            };

            blobsMock
            .Setup(i => i.GetUnprocessedAudits(container))
            .ReturnsAsync(new[] { audit })
            .Verifiable();
            blobsMock
            .Setup(i => i.DownloadFile($"{container.Name}/{audit.Name}"))
            .ReturnsAsync(File.OpenRead($"{container.Name}/{audit.Name}"))
            .Verifiable();
            blobsMock
            .Setup(i => i.DownloadFile($"{container.Name}/audit.json"))
            .ReturnsAsync(File.OpenRead($"{container.Name}/audit.json"))
            .Verifiable();
            blobsMock
            .Setup(i => i.DownloadFile($"{container.Name}/k8s-meta.json"))
            .ReturnsAsync(File.OpenRead($"{container.Name}/k8s-meta.json"))
            .Verifiable();
            blobsMock
            .Setup(i => i.MarkAsProcessed(audit))
            .Returns(Task.CompletedTask)
            .Verifiable();

            // Act & Assert
            await processor.Process(container, CancellationToken.None);

            blobsMock.Verify();

            dbMock.Verify(i => i.GetNotExpiredImageScans(It.Is <string[]>(tags => tags.Length == UniqueImageTagCount)));
            dbMock.Verify(i => i.SaveInProgressImageScan(It.IsAny <ImageScanResultWithCVEs>()), Times.Exactly(UniqueImageTagCount));
            queueMock.Verify(i => i.EnqueueImageScanRequest(It.IsAny <ImageScanResultWithCVEs>()), Times.Exactly(UniqueImageTagCount));

            dbMock.Verify(i => i.SaveAuditResult(It.Is <Audit>(a => VerifyHappyPathAudit(a, container))));
        }
        public async Task ProcessAuditHappyPath()
        {
            // Arrange
            await using var context = JosekiTestsDb.CreateUniqueContext();
            var parser = new ConfigurationParser("config.sample.yaml");

            var checksCache = new ChecksCache(parser, context, new MemoryCache(new MemoryCacheOptions()));

            var blobsMock = new Mock <IBlobStorageProcessor>(MockBehavior.Strict);
            var dbMock    = new Mock <IJosekiDatabase>();

            var ownershipCache = new OwnershipCache(context, new MemoryCache(new MemoryCacheOptions()));
            var postProcessor  = new Mock <ExtractOwnershipProcessor>(context, ownershipCache);
            var processor      = new AzskAuditProcessor(blobsMock.Object, dbMock.Object, checksCache, postProcessor.Object);

            var container = new ScannerContainer(Path.Combine("audits", "samples", "azsk"))
            {
                Metadata = new ScannerMetadata
                {
                    Type = ScannerType.Azsk,
                    Id   = Guid.NewGuid().ToString(),
                },
            };
            var audit = new AuditBlob {
                Name = "meta.json", ParentContainer = container
            };

            blobsMock
            .Setup(i => i.GetUnprocessedAudits(container))
            .ReturnsAsync(new[] { audit })
            .Verifiable();
            blobsMock
            .Setup(i => i.DownloadFile($"{container.Name}/{audit.Name}"))
            .ReturnsAsync(File.OpenRead($"{container.Name}/{audit.Name}"))
            .Verifiable();
            blobsMock
            .Setup(i => i.DownloadFile($"{container.Name}/resources.json"))
            .ReturnsAsync(File.OpenRead($"{container.Name}/resources.json"))
            .Verifiable();
            blobsMock
            .Setup(i => i.DownloadFile($"{container.Name}/subscription.json"))
            .ReturnsAsync(File.OpenRead($"{container.Name}/subscription.json"))
            .Verifiable();
            blobsMock
            .Setup(i => i.MarkAsProcessed(audit))
            .Returns(Task.CompletedTask)
            .Verifiable();

            // Act & Assert
            await processor.Process(container, CancellationToken.None);

            blobsMock.Verify();
            dbMock.Verify(i => i.SaveAuditResult(It.Is <Audit>(a => VerifyHappyPathAudit(a, container))));
        }
        public async Task ProcessScanResultWithoutCVEs()
        {
            // Arrange
            await using var context = JosekiTestsDb.CreateUniqueContext();
            var parser   = new ConfigurationParser("config.sample.yaml");
            var cveCache = new CveCache(parser, context, new MemoryCache(new MemoryCacheOptions()));

            var blobsMock = new Mock <IBlobStorageProcessor>(MockBehavior.Strict);
            var dbMock    = new Mock <IJosekiDatabase>();

            var processor = new TrivyAuditProcessor(blobsMock.Object, dbMock.Object, cveCache);

            var container = new ScannerContainer(Path.Combine("audits", "samples", "trivy"))
            {
                Metadata = new ScannerMetadata
                {
                    Type = ScannerType.Trivy,
                    Id   = Guid.NewGuid().ToString(),
                },
            };
            var audit = new AuditBlob {
                Name = "meta_no_cve.json", ParentContainer = container
            };

            blobsMock
            .Setup(i => i.GetUnprocessedAudits(container))
            .ReturnsAsync(new[] { audit })
            .Verifiable();
            blobsMock
            .Setup(i => i.DownloadFile($"{container.Name}/{audit.Name}"))
            .ReturnsAsync(File.OpenRead($"{container.Name}/{audit.Name}"))
            .Verifiable();
            blobsMock
            .Setup(i => i.DownloadFile($"{container.Name}/result_no_cves.json"))
            .ReturnsAsync(File.OpenRead($"{container.Name}/result_no_cves.json"))
            .Verifiable();
            blobsMock
            .Setup(i => i.MarkAsProcessed(audit))
            .Returns(Task.CompletedTask)
            .Verifiable();

            // Act & Assert
            await processor.Process(container, CancellationToken.None);

            blobsMock.Verify();

            dbMock.Verify(i => i.SaveImageScanResult(It.Is <ImageScanResultWithCVEs>(a => VerifyNoCveScan(a))));
        }
        private static bool VerifyHappyPathAudit(Audit audit, ScannerContainer container)
        {
            const string auditId   = "ec783b2d-1da0-4a30-8e52-b72dd3174eae";
            var          auditDate = DateTimeOffset.FromUnixTimeSeconds(1583892106).DateTime;

            audit.Id.Should().Be(auditId);
            audit.Date.Should().BeCloseTo(auditDate, TimeSpan.FromMinutes(1));
            audit.ScannerId.Should().Be($"{container.Metadata.Type}/{container.Metadata.Id}");
            audit.ComponentId.Should().Be("/subscriptions/bc76e4b4-bc3e-46f0-b0c1-955c4c5fbe2a");
            audit.ComponentName.Should().Be("Test Subscription");
            audit.MetadataAzure.AuditId.Should().Be(auditId);
            audit.MetadataAzure.Date.Should().BeCloseTo(auditDate, TimeSpan.FromMinutes(1));
            audit.MetadataAzure.JSON.Should().NotBeNullOrWhiteSpace();

            // TODO: maybe check each check in more details?
            audit.CheckResults.Should().HaveCount(ExpectedCheckResults);
            return(true);
        }
        private static bool VerifyHappyPathAudit(Audit audit, ScannerContainer container)
        {
            const string auditId   = "3aab7985-5756-4cd6-8e3f-3353aa4b98a4";
            var          auditDate = DateTimeOffset.FromUnixTimeSeconds(1595556010).DateTime;

            audit.Id.Should().Be(auditId);
            audit.Date.Should().BeCloseTo(auditDate, TimeSpan.FromMinutes(1));
            audit.ScannerId.Should().Be($"{container.Metadata.Type}/{container.Metadata.Id}");
            audit.ComponentId.Should().Be("/k8s/3aab7985-d1a2-161d-8236-3353aa4b98a4");
            audit.ComponentName.Should().Be("10.0.0.1");
            audit.MetadataKube.AuditId.Should().Be(auditId);
            audit.MetadataKube.Date.Should().BeCloseTo(auditDate, TimeSpan.FromMinutes(1));
            audit.MetadataKube.JSON.Should().NotBeNullOrWhiteSpace();

            // TODO: maybe check each check in more details?
            audit.CheckResults.Should().HaveCount(ExpectedCheckResults);

            return(true);
        }
Example #6
0
        private async Task <ScannerContainer> DownloadAndParseMetadata(ScannerContainer container)
        {
            const int oneHourSeconds = 3600;

            try
            {
                var stream = await this.blobStorage.DownloadFile(container.MetadataFilePath);

                using var sr = new StreamReader(stream);
                var metadataString = sr.ReadToEnd();

                container.Metadata = JsonConvert.DeserializeObject <ScannerMetadata>(metadataString);

                var unixNow = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
                var delta   = unixNow - container.Metadata.Heartbeat;

                if (delta < container.Metadata.HeartbeatPeriodicity)
                {
                    // do nothing
                }
                else if (delta < oneHourSeconds || delta < container.Metadata.HeartbeatPeriodicity * 2)
                {
                    var lastExecution = DateTimeOffset.FromUnixTimeSeconds(container.Metadata.Heartbeat);
                    Logger.Warning("Scanner {ScannerContainer} keeps silence since {LastExecution}", container.Name, lastExecution);
                }
                else
                {
                    var lastExecution = DateTimeOffset.FromUnixTimeSeconds(container.Metadata.Heartbeat);
                    Logger.Error("Scanner {ScannerContainer} keeps silence since {LastExecution}", container.Name, lastExecution);
                }

                return(container);
            }
            catch (JosekiException ex)
            {
                Logger.Warning(ex, "Failed to download amd parse metadata of {ScannerContainer}", container.Name);
                return(ScannerContainer.Empty);
            }
        }
        /// <inheritdoc />
        public async Task Process(ScannerContainer container, CancellationToken token)
        {
            var audits = await this.blobStorage.GetUnprocessedAudits(container);

            foreach (var audit in audits)
            {
                if (token.IsCancellationRequested)
                {
                    return;
                }

                try
                {
                    Logger.Information("Processing audit result from {AuditName} container was {ProcessingState}", audit.Name, "started");
                    await this.ProcessSingleAudit(audit);

                    Logger.Information("Processing audit result from {AuditName} container was {ProcessingState}", audit.Name, "finished");
                }
                catch (Exception ex)
                {
                    Logger.Error(ex, "Processing audit result from {AuditName} container was {ProcessingState}", audit.Name, "failed");
                }
            }
        }
Example #8
0
 /// <inheritdoc />
 public Task Process(ScannerContainer container, CancellationToken token)
 {
     return(Task.CompletedTask);
 }