diff --git a/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dockerfiles_Clamav_Dockerfile.xml b/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dockerfiles_Clamav_Dockerfile.xml deleted file mode 100644 index 7ea3be1..0000000 --- a/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dockerfiles_Clamav_Dockerfile.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dockerfiles_Comodo_Dockerfile.xml b/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dockerfiles_Comodo_Dockerfile.xml deleted file mode 100644 index cd33557..0000000 --- a/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dockerfiles_Comodo_Dockerfile.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dockerfiles_DrWeb_Dockerfile.xml b/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dockerfiles_DrWeb_Dockerfile.xml deleted file mode 100644 index 2df7f80..0000000 --- a/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dockerfiles_DrWeb_Dockerfile.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dockerfiles_KES_Dockerfile.xml b/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dockerfiles_KES_Dockerfile.xml deleted file mode 100644 index d634c34..0000000 --- a/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dockerfiles_KES_Dockerfile.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dockerfiles_McAfee_Dockerfile.xml b/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dockerfiles_McAfee_Dockerfile.xml deleted file mode 100644 index 6ad531e..0000000 --- a/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dockerfiles_McAfee_Dockerfile.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dockerfiles_Sophos_Dockerfile.xml b/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dockerfiles_Sophos_Dockerfile.xml deleted file mode 100644 index 72d2c60..0000000 --- a/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dockerfiles_Sophos_Dockerfile.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dockerfiles_WindowsDefender_Dockerfile.xml b/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dockerfiles_WindowsDefender_Dockerfile.xml deleted file mode 100644 index 175e6e3..0000000 --- a/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dockerfiles_WindowsDefender_Dockerfile.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dummy_API.xml b/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dummy_API.xml deleted file mode 100644 index 0c4f862..0000000 --- a/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/Dummy_API.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/MalwareMultiScan_Scanner_Dockerfile.xml b/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/MalwareMultiScan_Scanner_Dockerfile.xml deleted file mode 100644 index f04733b..0000000 --- a/.idea/.idea.MalwareMultiScan/.idea/runConfigurations/MalwareMultiScan_Scanner_Dockerfile.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/MalwareMultiScan.Api/Attributes/MaxFileSizeAttribute.cs b/MalwareMultiScan.Api/Attributes/MaxFileSizeAttribute.cs index 5aaa873..02200a3 100644 --- a/MalwareMultiScan.Api/Attributes/MaxFileSizeAttribute.cs +++ b/MalwareMultiScan.Api/Attributes/MaxFileSizeAttribute.cs @@ -15,7 +15,10 @@ namespace MalwareMultiScan.Api.Attributes { var maxSize = validationContext .GetRequiredService() - .GetValue("MaxFileSize"); + .GetValue("FILE_SIZE_LIMIT"); + + if (maxSize == 0) + return ValidationResult.Success; var formFile = (IFormFile) value; diff --git a/MalwareMultiScan.Api/Controllers/QueueController.cs b/MalwareMultiScan.Api/Controllers/QueueController.cs index bbee862..565f8f3 100644 --- a/MalwareMultiScan.Api/Controllers/QueueController.cs +++ b/MalwareMultiScan.Api/Controllers/QueueController.cs @@ -2,7 +2,7 @@ using System; using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using MalwareMultiScan.Api.Attributes; -using MalwareMultiScan.Api.Data.Models; +using MalwareMultiScan.Api.Data; using MalwareMultiScan.Api.Services.Interfaces; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; diff --git a/MalwareMultiScan.Api/Controllers/ScanResultsController.cs b/MalwareMultiScan.Api/Controllers/ScanResultsController.cs index c75dc4f..d10b146 100644 --- a/MalwareMultiScan.Api/Controllers/ScanResultsController.cs +++ b/MalwareMultiScan.Api/Controllers/ScanResultsController.cs @@ -1,5 +1,5 @@ using System.Threading.Tasks; -using MalwareMultiScan.Api.Data.Models; +using MalwareMultiScan.Api.Data; using MalwareMultiScan.Api.Services.Interfaces; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; diff --git a/MalwareMultiScan.Api/Data/Configuration/ScanBackend.cs b/MalwareMultiScan.Api/Data/Configuration/ScanBackend.cs deleted file mode 100644 index f75111c..0000000 --- a/MalwareMultiScan.Api/Data/Configuration/ScanBackend.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace MalwareMultiScan.Api.Data.Configuration -{ - /// - /// Scan backend. - /// - public class ScanBackend - { - /// - /// Backend id. - /// - public string Id { get; set; } - - /// - /// Backend state. - /// - public bool Enabled { get; set; } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Api/Data/Models/ScanResultEntry.cs b/MalwareMultiScan.Api/Data/Models/ScanResultEntry.cs deleted file mode 100644 index eca493d..0000000 --- a/MalwareMultiScan.Api/Data/Models/ScanResultEntry.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace MalwareMultiScan.Api.Data.Models -{ - /// - /// Scan result entry. - /// - public class ScanResultEntry - { - /// - /// Completion status. - /// - public bool Completed { get; set; } - - /// - /// Indicates that scanning completed without error. - /// - public bool? Succeeded { get; set; } - - /// - /// Scanning duration in seconds. - /// - public long Duration { get; set; } - - /// - /// Detected names of threats. - /// - public string[] Threats { get; set; } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Api/Data/Models/ScanResult.cs b/MalwareMultiScan.Api/Data/ScanResult.cs similarity index 71% rename from MalwareMultiScan.Api/Data/Models/ScanResult.cs rename to MalwareMultiScan.Api/Data/ScanResult.cs index 4a9ddbe..ec97462 100644 --- a/MalwareMultiScan.Api/Data/Models/ScanResult.cs +++ b/MalwareMultiScan.Api/Data/ScanResult.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; +using MalwareMultiScan.Shared.Message; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; -namespace MalwareMultiScan.Api.Data.Models +namespace MalwareMultiScan.Api.Data { /// /// Scan result. @@ -23,9 +24,9 @@ namespace MalwareMultiScan.Api.Data.Models public Uri CallbackUrl { get; set; } /// - /// Result entries where key is backend id and value is . + /// Result entries where key is backend id and value is . /// - public Dictionary Results { get; set; } = - new Dictionary(); + public Dictionary Results { get; set; } = + new Dictionary(); } } \ No newline at end of file diff --git a/MalwareMultiScan.Api/Dockerfile b/MalwareMultiScan.Api/Dockerfile index 8db137f..e89945a 100644 --- a/MalwareMultiScan.Api/Dockerfile +++ b/MalwareMultiScan.Api/Dockerfile @@ -4,6 +4,7 @@ WORKDIR /src COPY MalwareMultiScan.Api /src/MalwareMultiScan.Api COPY MalwareMultiScan.Backends /src/MalwareMultiScan.Backends +COPY MalwareMultiScan.Shared /src/MalwareMultiScan.Shared RUN dotnet publish -c Release -r linux-x64 -o ./publish MalwareMultiScan.Api/MalwareMultiScan.Api.csproj diff --git a/MalwareMultiScan.Api/Extensions/ServiceCollectionExtensions.cs b/MalwareMultiScan.Api/Extensions/ServiceCollectionExtensions.cs index 5c18425..06a9f4a 100644 --- a/MalwareMultiScan.Api/Extensions/ServiceCollectionExtensions.cs +++ b/MalwareMultiScan.Api/Extensions/ServiceCollectionExtensions.cs @@ -14,8 +14,8 @@ namespace MalwareMultiScan.Api.Extensions { internal static void AddMongoDb(this IServiceCollection services, IConfiguration configuration) { - var client = new MongoClient(configuration.GetConnectionString("Mongo")); - var db = client.GetDatabase(configuration.GetValue("DatabaseName")); + var client = new MongoClient(configuration.GetValue("MONGO_ADDRESS")); + var db = client.GetDatabase(configuration.GetValue("MONGO_DATABASE")); services.AddSingleton(client); services.AddSingleton(db); diff --git a/MalwareMultiScan.Api/MalwareMultiScan.Api.csproj b/MalwareMultiScan.Api/MalwareMultiScan.Api.csproj index ff27e1b..5300da5 100644 --- a/MalwareMultiScan.Api/MalwareMultiScan.Api.csproj +++ b/MalwareMultiScan.Api/MalwareMultiScan.Api.csproj @@ -4,18 +4,14 @@ netcoreapp3.1 Volodymyr Smirnov MalwareMultiScan Api - 1.0.1 - 1.0.1 + 1.5.0 + 1.5.0 true - - Always - - - - + + @@ -24,4 +20,11 @@ + + + + Never + Never + + diff --git a/MalwareMultiScan.Api/Program.cs b/MalwareMultiScan.Api/Program.cs index d8993a8..ef1fcd1 100644 --- a/MalwareMultiScan.Api/Program.cs +++ b/MalwareMultiScan.Api/Program.cs @@ -1,4 +1,5 @@ -using EasyNetQ.LightInject; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; @@ -7,15 +8,19 @@ namespace MalwareMultiScan.Api [ExcludeFromCodeCoverage] internal static class Program { - public static void Main(string[] args) + public static async Task Main(string[] args) { - CreateHostBuilder(args).Build().Run(); - } + await Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(builder => + { + builder.ConfigureKestrel( + options => options.Limits.MaxRequestBodySize = long.MaxValue); - private static IHostBuilder CreateHostBuilder(string[] args) - { - return Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup()); + builder.UseStartup(); + }) + .UseConsoleLifetime() + .Build() + .RunAsync(); } } } \ No newline at end of file diff --git a/MalwareMultiScan.Api/Properties/launchSettings.json b/MalwareMultiScan.Api/Properties/launchSettings.json new file mode 100644 index 0000000..11e87fd --- /dev/null +++ b/MalwareMultiScan.Api/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "MalwareMultiScan.Api": { + "commandName": "Project" + } + } +} diff --git a/MalwareMultiScan.Api/Services/Implementations/ReceiverHostedService.cs b/MalwareMultiScan.Api/Services/Implementations/ReceiverHostedService.cs deleted file mode 100644 index 02b3452..0000000 --- a/MalwareMultiScan.Api/Services/Implementations/ReceiverHostedService.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System; -using System.Net.Http; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using EasyNetQ; -using MalwareMultiScan.Api.Services.Interfaces; -using MalwareMultiScan.Backends.Messages; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json; - -namespace MalwareMultiScan.Api.Services.Implementations -{ - /// - public class ReceiverHostedService : IReceiverHostedService - { - private readonly IBus _bus; - private readonly IConfiguration _configuration; - private readonly IHttpClientFactory _httpClientFactory; - private readonly ILogger _logger; - private readonly IScanResultService _scanResultService; - - /// - /// Initialize receiver hosted service. - /// - /// EasyNetQ bus. - /// Configuration. - /// Scan result service. - /// Logger. - /// HTTP client factory. - public ReceiverHostedService(IBus bus, IConfiguration configuration, IScanResultService scanResultService, - ILogger logger, IHttpClientFactory httpClientFactory) - { - _bus = bus; - _configuration = configuration; - _scanResultService = scanResultService; - _logger = logger; - _httpClientFactory = httpClientFactory; - } - - - /// - public Task StartAsync(CancellationToken cancellationToken) - { - _bus.Receive( - _configuration.GetValue("ResultsSubscriptionId"), StoreScanResult); - - _logger.LogInformation( - "Started hosted service for receiving scan results"); - - return Task.CompletedTask; - } - - /// - public Task StopAsync(CancellationToken cancellationToken) - { - _bus?.Dispose(); - - _logger.LogInformation( - "Stopped hosted service for receiving scan results"); - - return Task.CompletedTask; - } - - private async Task StoreScanResult(ScanResultMessage message) - { - message.Threats ??= new string[] { }; - - _logger.LogInformation( - $"Received a result from {message.Backend} for {message.Id} " + - $"with threats {string.Join(",", message.Threats)}"); - - await _scanResultService.UpdateScanResultForBackend( - message.Id, message.Backend, message.Duration, true, - message.Succeeded, message.Threats); - - var result = await _scanResultService.GetScanResult(message.Id); - - if (result?.CallbackUrl == null) - return; - - var cancellationTokenSource = new CancellationTokenSource( - TimeSpan.FromSeconds(3)); - - using var httpClient = _httpClientFactory.CreateClient(); - - try - { - var response = await httpClient.PostAsync( - result.CallbackUrl, - new StringContent(JsonConvert.SerializeObject(result), Encoding.UTF8, "application/json"), - cancellationTokenSource.Token); - - response.EnsureSuccessStatusCode(); - } - catch (Exception exception) - { - _logger.LogError(exception, $"Failed to POST to callback URL {result.CallbackUrl}"); - } - } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Api/Services/Implementations/ScanBackendService.cs b/MalwareMultiScan.Api/Services/Implementations/ScanBackendService.cs deleted file mode 100644 index 1e5cc33..0000000 --- a/MalwareMultiScan.Api/Services/Implementations/ScanBackendService.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.IO; -using System.Threading.Tasks; -using EasyNetQ; -using MalwareMultiScan.Api.Data.Configuration; -using MalwareMultiScan.Api.Data.Models; -using MalwareMultiScan.Api.Services.Interfaces; -using MalwareMultiScan.Backends.Messages; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using YamlDotNet.Serialization; -using YamlDotNet.Serialization.NamingConventions; - -namespace MalwareMultiScan.Api.Services.Implementations -{ - /// - public class ScanBackendService : IScanBackendService - { - private readonly IBus _bus; - private readonly ILogger _logger; - - /// - /// Initialise scan backend service. - /// - /// Configuration. - /// EasyNetQ bus. - /// Logger. - /// Missing backends.yaml configuration. - public ScanBackendService(IConfiguration configuration, IBus bus, ILogger logger) - { - _bus = bus; - _logger = logger; - - var configurationPath = configuration.GetValue("BackendsConfiguration"); - - if (!File.Exists(configurationPath)) - throw new FileNotFoundException("Missing BackendsConfiguration YAML file", configurationPath); - - var configurationContent = File.ReadAllText(configurationPath); - - var deserializer = new DeserializerBuilder() - .WithNamingConvention(CamelCaseNamingConvention.Instance) - .Build(); - - List = deserializer.Deserialize(configurationContent); - } - - /// - public ScanBackend[] List { get; } - - /// - public async Task QueueUrlScan(ScanResult result, ScanBackend backend, string fileUrl) - { - _logger.LogInformation( - $"Queueing scan for {result.Id} on {backend.Id} at {fileUrl}"); - - await _bus.SendAsync(backend.Id, new ScanRequestMessage - { - Id = result.Id, - Uri = new Uri(fileUrl) - }); - } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Api/Services/Interfaces/IReceiverHostedService.cs b/MalwareMultiScan.Api/Services/Interfaces/IReceiverHostedService.cs deleted file mode 100644 index 67ed8f0..0000000 --- a/MalwareMultiScan.Api/Services/Interfaces/IReceiverHostedService.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.Extensions.Hosting; - -namespace MalwareMultiScan.Api.Services.Interfaces -{ - /// - /// Receiver hosted service. - /// - public interface IReceiverHostedService : IHostedService - { - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Api/Services/Interfaces/IScanBackendService.cs b/MalwareMultiScan.Api/Services/Interfaces/IScanBackendService.cs deleted file mode 100644 index 3cc6484..0000000 --- a/MalwareMultiScan.Api/Services/Interfaces/IScanBackendService.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Threading.Tasks; -using MalwareMultiScan.Api.Data.Configuration; -using MalwareMultiScan.Api.Data.Models; - -namespace MalwareMultiScan.Api.Services.Interfaces -{ - /// - /// Scan backend service. - /// - public interface IScanBackendService - { - /// - /// Get list of parsed backends. - /// - ScanBackend[] List { get; } - - /// - /// Queue URL for scan. - /// - /// Result entry. - /// Backend entry. - /// Remote URL. - Task QueueUrlScan(ScanResult result, ScanBackend backend, string fileUrl); - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Api/Services/Interfaces/IScanResultService.cs b/MalwareMultiScan.Api/Services/Interfaces/IScanResultService.cs index 9f0b6d9..3058316 100644 --- a/MalwareMultiScan.Api/Services/Interfaces/IScanResultService.cs +++ b/MalwareMultiScan.Api/Services/Interfaces/IScanResultService.cs @@ -1,7 +1,8 @@ using System; using System.IO; using System.Threading.Tasks; -using MalwareMultiScan.Api.Data.Models; +using MalwareMultiScan.Api.Data; +using MalwareMultiScan.Shared.Message; namespace MalwareMultiScan.Api.Services.Interfaces { @@ -29,12 +30,8 @@ namespace MalwareMultiScan.Api.Services.Interfaces /// /// Result id. /// Backend id. - /// Duration. - /// Completion status. - /// Indicates that scanning completed without error. - /// Detected names of threats. - Task UpdateScanResultForBackend(string resultId, string backendId, long duration, - bool completed = false, bool succeeded = false, string[] threats = null); + /// Scan result. + Task UpdateScanResultForBackend(string resultId, string backendId, ScanResultMessage result = null); /// /// Queue URL for scanning. diff --git a/MalwareMultiScan.Api/Services/ScanResultJob.cs b/MalwareMultiScan.Api/Services/ScanResultJob.cs new file mode 100644 index 0000000..f4677b9 --- /dev/null +++ b/MalwareMultiScan.Api/Services/ScanResultJob.cs @@ -0,0 +1,94 @@ +using System; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Hangfire; +using Hangfire.States; +using MalwareMultiScan.Api.Services.Interfaces; +using MalwareMultiScan.Shared.Message; +using MalwareMultiScan.Shared.Services.Interfaces; +using Microsoft.Extensions.Logging; + +namespace MalwareMultiScan.Api.Services +{ + /// + /// Job for storing scan results. + /// + public class ScanResultJob : IScanResultJob + { + private readonly IBackgroundJobClient _backgroundJobClient; + private readonly IHttpClientFactory _httpClientFactory; + private readonly ILogger _logger; + private readonly IScanResultService _scanResultService; + + /// + /// Initialize scan result storing job. + /// + /// Logger. + /// HTTP client factory. + /// Scan result service. + /// Background job client. + public ScanResultJob( + ILogger logger, + IHttpClientFactory httpClientFactory, + IScanResultService scanResultService, + IBackgroundJobClient backgroundJobClient) + { + _logger = logger; + _httpClientFactory = httpClientFactory; + _scanResultService = scanResultService; + _backgroundJobClient = backgroundJobClient; + } + + /// + public async Task Report(string resultId, string backendId, ScanResultMessage result) + { + _logger.LogInformation( + $"Received a result from {backendId} for {result} with status {result.Status} " + + $"and threats {string.Join(",", result.Threats)}"); + + await _scanResultService.UpdateScanResultForBackend(resultId, backendId, result); + + var scanResult = await _scanResultService.GetScanResult(resultId); + + if (scanResult?.CallbackUrl == null) + return; + + _backgroundJobClient.Create( + x => x.Notify(scanResult.CallbackUrl, resultId, backendId, result), + new EnqueuedState("default")); + } + + /// + public async Task Notify(Uri uri, string resultId, string backendId, ScanResultMessage result) + { + var cancellationTokenSource = new CancellationTokenSource( + TimeSpan.FromSeconds(5)); + + using var httpClient = _httpClientFactory.CreateClient(); + + try + { + var builder = new UriBuilder(uri) + { + Query = $"?id={resultId}" + + $"&backend={backendId}" + }; + + var response = await httpClient.PostAsync(builder.Uri, + new StringContent(JsonSerializer.Serialize(result), Encoding.UTF8, "application/json"), + cancellationTokenSource.Token); + + response.EnsureSuccessStatusCode(); + + _logger.LogInformation($"Sent POST to callback URL {uri}"); + } + catch (Exception exception) + { + _logger.LogError(exception, $"Failed to POST to callback URL {uri}"); + } + } + } +} \ No newline at end of file diff --git a/MalwareMultiScan.Api/Services/Implementations/ScanResultService.cs b/MalwareMultiScan.Api/Services/ScanResultService.cs similarity index 58% rename from MalwareMultiScan.Api/Services/Implementations/ScanResultService.cs rename to MalwareMultiScan.Api/Services/ScanResultService.cs index 51b1812..241bcce 100644 --- a/MalwareMultiScan.Api/Services/Implementations/ScanResultService.cs +++ b/MalwareMultiScan.Api/Services/ScanResultService.cs @@ -2,33 +2,46 @@ using System; using System.IO; using System.Linq; using System.Threading.Tasks; -using MalwareMultiScan.Api.Data.Models; +using Consul; +using Hangfire; +using Hangfire.States; +using MalwareMultiScan.Api.Data; using MalwareMultiScan.Api.Services.Interfaces; +using MalwareMultiScan.Shared.Enums; +using MalwareMultiScan.Shared.Message; +using MalwareMultiScan.Shared.Services.Interfaces; using MongoDB.Bson; using MongoDB.Driver; using MongoDB.Driver.GridFS; -namespace MalwareMultiScan.Api.Services.Implementations +namespace MalwareMultiScan.Api.Services { /// public class ScanResultService : IScanResultService { private const string CollectionName = "ScanResults"; + private readonly IBackgroundJobClient _backgroundJobClient; private readonly IGridFSBucket _bucket; private readonly IMongoCollection _collection; - private readonly IScanBackendService _scanBackendService; + private readonly IConsulClient _consulClient; /// /// Initialize scan result service. /// /// Mongo database. /// GridFS bucket. - /// Scan backend service. - public ScanResultService(IMongoDatabase db, IGridFSBucket bucket, IScanBackendService scanBackendService) + /// Consul client. + /// Background job client. + public ScanResultService( + IMongoDatabase db, + IGridFSBucket bucket, + IConsulClient consulClient, + IBackgroundJobClient backgroundJobClient) { _bucket = bucket; - _scanBackendService = scanBackendService; + _consulClient = consulClient; + _backgroundJobClient = backgroundJobClient; _collection = db.GetCollection(CollectionName); } @@ -38,11 +51,7 @@ namespace MalwareMultiScan.Api.Services.Implementations { var scanResult = new ScanResult { - CallbackUrl = callbackUrl, - - Results = _scanBackendService.List - .Where(b => b.Enabled) - .ToDictionary(k => k.Id, v => new ScanResultEntry()) + CallbackUrl = callbackUrl }; await _collection.InsertOneAsync(scanResult); @@ -60,25 +69,43 @@ namespace MalwareMultiScan.Api.Services.Implementations } /// - public async Task UpdateScanResultForBackend(string resultId, string backendId, long duration, - bool completed = false, bool succeeded = false, string[] threats = null) + public async Task UpdateScanResultForBackend(string resultId, string backendId, + ScanResultMessage result = null) { + result ??= new ScanResultMessage + { + Status = ScanResultStatus.Queued + }; + await _collection.UpdateOneAsync( Builders.Filter.Where(r => r.Id == resultId), - Builders.Update.Set(r => r.Results[backendId], new ScanResultEntry - { - Completed = completed, - Succeeded = succeeded, - Duration = duration, - Threats = threats ?? new string[] { } - })); + Builders.Update.Set(r => r.Results[backendId], result)); } /// public async Task QueueUrlScan(ScanResult result, string fileUrl) { - foreach (var backend in _scanBackendService.List.Where(b => b.Enabled)) - await _scanBackendService.QueueUrlScan(result, backend, fileUrl); + var message = new ScanQueueMessage + { + Id = result.Id, + Uri = new Uri(fileUrl) + }; + + var scanners = await _consulClient.Health.Service("scanner", null, true); + + var backends = scanners.Response + .Select(s => s.Service.Meta.TryGetValue("BackendId", out var backendId) ? backendId : null) + .Where(q => q != null) + .Distinct() + .ToArray(); + + foreach (var backend in backends) + { + await UpdateScanResultForBackend(result.Id, backend); + + _backgroundJobClient.Create( + j => j.Process(message), new EnqueuedState(backend)); + } } /// diff --git a/MalwareMultiScan.Api/Startup.cs b/MalwareMultiScan.Api/Startup.cs index 3e8624c..c40d624 100644 --- a/MalwareMultiScan.Api/Startup.cs +++ b/MalwareMultiScan.Api/Startup.cs @@ -1,12 +1,14 @@ using System.Diagnostics.CodeAnalysis; +using Hangfire; using MalwareMultiScan.Api.Extensions; -using MalwareMultiScan.Api.Services.Implementations; +using MalwareMultiScan.Api.Services; using MalwareMultiScan.Api.Services.Interfaces; -using MalwareMultiScan.Backends.Extensions; +using MalwareMultiScan.Shared.Extensions; +using MalwareMultiScan.Shared.Services.Interfaces; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; namespace MalwareMultiScan.Api { @@ -24,29 +26,26 @@ namespace MalwareMultiScan.Api { services.AddDockerForwardedHeadersOptions(); - services.Configure(options => - { - options.Limits.MaxRequestBodySize = _configuration.GetValue("MaxFileSize"); - }); - + services.AddConsul(_configuration); services.AddMongoDb(_configuration); - services.AddRabbitMq(_configuration); - - services.AddSingleton(); + services.AddHangfire(_configuration); + services.AddSingleton(); - + services.AddSingleton(); + services.AddControllers(); - - services.AddHostedService(); - services.AddHttpClient(); } - public void Configure(IApplicationBuilder app) + public void Configure(IApplicationBuilder app, IHostEnvironment hostEnvironment) { app.UseRouting(); - app.UseForwardedHeaders(); app.UseEndpoints(endpoints => endpoints.MapControllers()); + + app.UseForwardedHeaders(); + + if (hostEnvironment.IsDevelopment()) + app.UseHangfireDashboard(); } } } \ No newline at end of file diff --git a/MalwareMultiScan.Api/appsettings.json b/MalwareMultiScan.Api/appsettings.json index 0918519..23a48d7 100644 --- a/MalwareMultiScan.Api/appsettings.json +++ b/MalwareMultiScan.Api/appsettings.json @@ -7,15 +7,12 @@ } }, - "AllowedHosts": "*", - - "ConnectionStrings": { - "Mongo": "mongodb://localhost:27017", - "RabbitMQ": "host=localhost" - }, + "MONGO_ADDRESS": "mongodb://localhost:27017", + "MONGO_DATABASE": "MalwareMultiScan", - "DatabaseName": "MalwareMultiScan", - "ResultsSubscriptionId": "mms.results", - "MaxFileSize": 52428800, - "BackendsConfiguration": "backends.yaml" + "REDIS_ADDRESS": "localhost:6379", + + "CONSUL_ADDRESS": "http://localhost:8500", + + "FILE_SIZE_LIMIT": 52428800 } diff --git a/MalwareMultiScan.Api/backends.yaml b/MalwareMultiScan.Api/backends.yaml deleted file mode 100644 index 4b3609d..0000000 --- a/MalwareMultiScan.Api/backends.yaml +++ /dev/null @@ -1,23 +0,0 @@ -- id: dummy - enabled: true - -- id: clamav - enabled: true - -- id: windows-defender - enabled: true - -- id: comodo - enabled: false - -- id: drweb - enabled: false - -- id: kes - enabled: false - -- id: mcafee - enabled: false - -- id: sophos - enabled: false \ No newline at end of file diff --git a/MalwareMultiScan.Backends/Backends/Abstracts/AbstractScanBackend.cs b/MalwareMultiScan.Backends/Backends/Abstracts/AbstractScanBackend.cs index 00a282a..7448219 100644 --- a/MalwareMultiScan.Backends/Backends/Abstracts/AbstractScanBackend.cs +++ b/MalwareMultiScan.Backends/Backends/Abstracts/AbstractScanBackend.cs @@ -4,7 +4,7 @@ using System.IO; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MalwareMultiScan.Backends.Interfaces; +using MalwareMultiScan.Backends.Backends.Interfaces; namespace MalwareMultiScan.Backends.Backends.Abstracts { diff --git a/MalwareMultiScan.Backends/Backends/Implementations/ClamavScanBackend.cs b/MalwareMultiScan.Backends/Backends/ClamavScanBackend.cs similarity index 94% rename from MalwareMultiScan.Backends/Backends/Implementations/ClamavScanBackend.cs rename to MalwareMultiScan.Backends/Backends/ClamavScanBackend.cs index d80fc16..18e461c 100644 --- a/MalwareMultiScan.Backends/Backends/Implementations/ClamavScanBackend.cs +++ b/MalwareMultiScan.Backends/Backends/ClamavScanBackend.cs @@ -2,7 +2,7 @@ using System.Text.RegularExpressions; using MalwareMultiScan.Backends.Backends.Abstracts; using MalwareMultiScan.Backends.Services.Interfaces; -namespace MalwareMultiScan.Backends.Backends.Implementations +namespace MalwareMultiScan.Backends.Backends { /// public class ClamavScanBackend : AbstractLocalProcessScanBackend diff --git a/MalwareMultiScan.Backends/Backends/Implementations/ComodoScanBackend.cs b/MalwareMultiScan.Backends/Backends/ComodoScanBackend.cs similarity index 93% rename from MalwareMultiScan.Backends/Backends/Implementations/ComodoScanBackend.cs rename to MalwareMultiScan.Backends/Backends/ComodoScanBackend.cs index 6daee74..d93029c 100644 --- a/MalwareMultiScan.Backends/Backends/Implementations/ComodoScanBackend.cs +++ b/MalwareMultiScan.Backends/Backends/ComodoScanBackend.cs @@ -2,7 +2,7 @@ using System.Text.RegularExpressions; using MalwareMultiScan.Backends.Backends.Abstracts; using MalwareMultiScan.Backends.Services.Interfaces; -namespace MalwareMultiScan.Backends.Backends.Implementations +namespace MalwareMultiScan.Backends.Backends { /// public class ComodoScanBackend : AbstractLocalProcessScanBackend diff --git a/MalwareMultiScan.Backends/Backends/Implementations/DrWebScanBackend.cs b/MalwareMultiScan.Backends/Backends/DrWebScanBackend.cs similarity index 93% rename from MalwareMultiScan.Backends/Backends/Implementations/DrWebScanBackend.cs rename to MalwareMultiScan.Backends/Backends/DrWebScanBackend.cs index 7681449..159918e 100644 --- a/MalwareMultiScan.Backends/Backends/Implementations/DrWebScanBackend.cs +++ b/MalwareMultiScan.Backends/Backends/DrWebScanBackend.cs @@ -2,7 +2,7 @@ using System.Text.RegularExpressions; using MalwareMultiScan.Backends.Backends.Abstracts; using MalwareMultiScan.Backends.Services.Interfaces; -namespace MalwareMultiScan.Backends.Backends.Implementations +namespace MalwareMultiScan.Backends.Backends { /// public class DrWebScanBackend : AbstractLocalProcessScanBackend diff --git a/MalwareMultiScan.Backends/Backends/Implementations/DummyScanBackend.cs b/MalwareMultiScan.Backends/Backends/DummyScanBackend.cs similarity index 89% rename from MalwareMultiScan.Backends/Backends/Implementations/DummyScanBackend.cs rename to MalwareMultiScan.Backends/Backends/DummyScanBackend.cs index 6701648..13d1159 100644 --- a/MalwareMultiScan.Backends/Backends/Implementations/DummyScanBackend.cs +++ b/MalwareMultiScan.Backends/Backends/DummyScanBackend.cs @@ -2,9 +2,9 @@ using System; using System.IO; using System.Threading; using System.Threading.Tasks; -using MalwareMultiScan.Backends.Interfaces; +using MalwareMultiScan.Backends.Backends.Interfaces; -namespace MalwareMultiScan.Backends.Backends.Implementations +namespace MalwareMultiScan.Backends.Backends { /// public class DummyScanBackend : IScanBackend diff --git a/MalwareMultiScan.Backends/Interfaces/IScanBackend.cs b/MalwareMultiScan.Backends/Backends/Interfaces/IScanBackend.cs similarity index 95% rename from MalwareMultiScan.Backends/Interfaces/IScanBackend.cs rename to MalwareMultiScan.Backends/Backends/Interfaces/IScanBackend.cs index b352ea0..ec2215d 100644 --- a/MalwareMultiScan.Backends/Interfaces/IScanBackend.cs +++ b/MalwareMultiScan.Backends/Backends/Interfaces/IScanBackend.cs @@ -3,7 +3,7 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -namespace MalwareMultiScan.Backends.Interfaces +namespace MalwareMultiScan.Backends.Backends.Interfaces { /// /// Scan backend. diff --git a/MalwareMultiScan.Backends/Backends/Implementations/KesScanBackend.cs b/MalwareMultiScan.Backends/Backends/KesScanBackend.cs similarity index 93% rename from MalwareMultiScan.Backends/Backends/Implementations/KesScanBackend.cs rename to MalwareMultiScan.Backends/Backends/KesScanBackend.cs index d1141ec..1481a34 100644 --- a/MalwareMultiScan.Backends/Backends/Implementations/KesScanBackend.cs +++ b/MalwareMultiScan.Backends/Backends/KesScanBackend.cs @@ -2,7 +2,7 @@ using System.Text.RegularExpressions; using MalwareMultiScan.Backends.Backends.Abstracts; using MalwareMultiScan.Backends.Services.Interfaces; -namespace MalwareMultiScan.Backends.Backends.Implementations +namespace MalwareMultiScan.Backends.Backends { /// public class KesScanBackend : AbstractLocalProcessScanBackend diff --git a/MalwareMultiScan.Backends/Backends/Implementations/McAfeeScanBackend.cs b/MalwareMultiScan.Backends/Backends/McAfeeScanBackend.cs similarity index 94% rename from MalwareMultiScan.Backends/Backends/Implementations/McAfeeScanBackend.cs rename to MalwareMultiScan.Backends/Backends/McAfeeScanBackend.cs index f15a66f..6d97156 100644 --- a/MalwareMultiScan.Backends/Backends/Implementations/McAfeeScanBackend.cs +++ b/MalwareMultiScan.Backends/Backends/McAfeeScanBackend.cs @@ -2,7 +2,7 @@ using System.Text.RegularExpressions; using MalwareMultiScan.Backends.Backends.Abstracts; using MalwareMultiScan.Backends.Services.Interfaces; -namespace MalwareMultiScan.Backends.Backends.Implementations +namespace MalwareMultiScan.Backends.Backends { /// public class McAfeeScanBackend : AbstractLocalProcessScanBackend diff --git a/MalwareMultiScan.Backends/Backends/Implementations/SophosScanBackend.cs b/MalwareMultiScan.Backends/Backends/SophosScanBackend.cs similarity index 94% rename from MalwareMultiScan.Backends/Backends/Implementations/SophosScanBackend.cs rename to MalwareMultiScan.Backends/Backends/SophosScanBackend.cs index 0479582..2f8f1d8 100644 --- a/MalwareMultiScan.Backends/Backends/Implementations/SophosScanBackend.cs +++ b/MalwareMultiScan.Backends/Backends/SophosScanBackend.cs @@ -2,7 +2,7 @@ using System.Text.RegularExpressions; using MalwareMultiScan.Backends.Backends.Abstracts; using MalwareMultiScan.Backends.Services.Interfaces; -namespace MalwareMultiScan.Backends.Backends.Implementations +namespace MalwareMultiScan.Backends.Backends { /// public class SophosScanBackend : AbstractLocalProcessScanBackend diff --git a/MalwareMultiScan.Backends/Backends/Implementations/WindowsDefenderScanBackend.cs b/MalwareMultiScan.Backends/Backends/WindowsDefenderScanBackend.cs similarity index 94% rename from MalwareMultiScan.Backends/Backends/Implementations/WindowsDefenderScanBackend.cs rename to MalwareMultiScan.Backends/Backends/WindowsDefenderScanBackend.cs index 4db9613..1c57b29 100644 --- a/MalwareMultiScan.Backends/Backends/Implementations/WindowsDefenderScanBackend.cs +++ b/MalwareMultiScan.Backends/Backends/WindowsDefenderScanBackend.cs @@ -2,7 +2,7 @@ using System.Text.RegularExpressions; using MalwareMultiScan.Backends.Backends.Abstracts; using MalwareMultiScan.Backends.Services.Interfaces; -namespace MalwareMultiScan.Backends.Backends.Implementations +namespace MalwareMultiScan.Backends.Backends { /// public class WindowsDefenderScanBackend : AbstractLocalProcessScanBackend diff --git a/MalwareMultiScan.Backends/Dockerfiles/Clamav.Dockerfile b/MalwareMultiScan.Backends/Dockerfiles/Clamav.Dockerfile index 8374dd8..20ab146 100644 --- a/MalwareMultiScan.Backends/Dockerfiles/Clamav.Dockerfile +++ b/MalwareMultiScan.Backends/Dockerfiles/Clamav.Dockerfile @@ -5,6 +5,6 @@ ENV DEBIAN_FRONTEND noninteractive RUN apt-get update && apt-get install -y clamav clamav-daemon RUN freshclam --quiet -ENV BackendType=Clamav +ENV BACKEND_ID=clamav ENTRYPOINT /etc/init.d/clamav-daemon start && /worker/MalwareMultiScan.Scanner \ No newline at end of file diff --git a/MalwareMultiScan.Backends/Dockerfiles/Comodo.Dockerfile b/MalwareMultiScan.Backends/Dockerfiles/Comodo.Dockerfile index 8e10c16..e09411b 100644 --- a/MalwareMultiScan.Backends/Dockerfiles/Comodo.Dockerfile +++ b/MalwareMultiScan.Backends/Dockerfiles/Comodo.Dockerfile @@ -7,4 +7,4 @@ RUN wget -q https://cdn.download.comodo.com/cis/download/installs/linux/cav-linu RUN wget -q http://download.comodo.com/av/updates58/sigs/bases/bases.cav -O /opt/COMODO/scanners/bases.cav -ENV BackendType=Comodo \ No newline at end of file +ENV BACKEND_ID=comodo \ No newline at end of file diff --git a/MalwareMultiScan.Backends/Dockerfiles/DrWeb.Dockerfile b/MalwareMultiScan.Backends/Dockerfiles/DrWeb.Dockerfile index 53040a1..f44a52c 100644 --- a/MalwareMultiScan.Backends/Dockerfiles/DrWeb.Dockerfile +++ b/MalwareMultiScan.Backends/Dockerfiles/DrWeb.Dockerfile @@ -23,6 +23,6 @@ RUN /opt/drweb.com/bin/drweb-configd -d -p /var/run/drweb-configd.pid && \ drweb-ctl update && \ kill $(cat /var/run/drweb-configd.pid) -ENV BackendType=DrWeb +ENV BACKEND_ID=drweb ENTRYPOINT /opt/drweb.com/bin/drweb-configd -d -p /var/run/drweb-configd.pid && /worker/MalwareMultiScan.Scanner \ No newline at end of file diff --git a/MalwareMultiScan.Backends/Dockerfiles/KES.Dockerfile b/MalwareMultiScan.Backends/Dockerfiles/KES.Dockerfile index 51d2de5..3591c7f 100644 --- a/MalwareMultiScan.Backends/Dockerfiles/KES.Dockerfile +++ b/MalwareMultiScan.Backends/Dockerfiles/KES.Dockerfile @@ -30,6 +30,6 @@ kesl-control -B --query "FileName == \"$1\"" 2> /dev/null \n\ exit $? \ ' > /usr/bin/kesl-scan && chmod +x /usr/bin/kesl-scan -ENV BackendType=Kes +ENV BACKEND_ID=kes ENTRYPOINT /etc/init.d/kesl-supervisor start && /worker/MalwareMultiScan.Scanner \ No newline at end of file diff --git a/MalwareMultiScan.Backends/Dockerfiles/McAfee.Dockerfile b/MalwareMultiScan.Backends/Dockerfiles/McAfee.Dockerfile index 8dadd87..523c52e 100644 --- a/MalwareMultiScan.Backends/Dockerfiles/McAfee.Dockerfile +++ b/MalwareMultiScan.Backends/Dockerfiles/McAfee.Dockerfile @@ -16,4 +16,4 @@ RUN wget -q -Nc -r -nd -l1 -A "avvepo????dat.zip" http://download.nai.com/produc WORKDIR /worker -ENV BackendType=McAfee \ No newline at end of file +ENV BACKEND_ID=mcafee \ No newline at end of file diff --git a/MalwareMultiScan.Backends/Dockerfiles/Sophos.Dockerfile b/MalwareMultiScan.Backends/Dockerfiles/Sophos.Dockerfile index 814309d..64403b0 100644 --- a/MalwareMultiScan.Backends/Dockerfiles/Sophos.Dockerfile +++ b/MalwareMultiScan.Backends/Dockerfiles/Sophos.Dockerfile @@ -9,5 +9,5 @@ RUN wget -q $SOPHOS_URL -O /tmp/SophosInstall.sh && \ chmod +x /tmp/SophosInstall.sh && \ /tmp/SophosInstall.sh --automatic --acceptlicence || exit 0 -ENV BackendType=Sophos +ENV BACKEND_ID=sophos diff --git a/MalwareMultiScan.Backends/Dockerfiles/WindowsDefender.Dockerfile b/MalwareMultiScan.Backends/Dockerfiles/WindowsDefender.Dockerfile index b97083f..9600589 100644 --- a/MalwareMultiScan.Backends/Dockerfiles/WindowsDefender.Dockerfile +++ b/MalwareMultiScan.Backends/Dockerfiles/WindowsDefender.Dockerfile @@ -18,4 +18,4 @@ RUN apt-get update && apt-get install -y libc6-i386 COPY --from=backend /opt/loadlibrary/engine /opt/engine COPY --from=backend /opt/loadlibrary/mpclient /opt/mpclient -ENV BackendType=Defender \ No newline at end of file +ENV BACKEND_ID=windows-defender \ No newline at end of file diff --git a/MalwareMultiScan.Backends/Enums/BackendType.cs b/MalwareMultiScan.Backends/Enums/BackendType.cs deleted file mode 100644 index 5dda416..0000000 --- a/MalwareMultiScan.Backends/Enums/BackendType.cs +++ /dev/null @@ -1,48 +0,0 @@ -namespace MalwareMultiScan.Backends.Enums -{ - /// - /// Backend type. - /// - public enum BackendType - { - /// - /// Dummy - /// - Dummy, - - /// - /// Windows Defender. - /// - Defender, - - /// - /// ClamAV. - /// - Clamav, - - /// - /// DrWeb. - /// - DrWeb, - - /// - /// KES. - /// - Kes, - - /// - /// Comodo. - /// - Comodo, - - /// - /// Sophos. - /// - Sophos, - - /// - /// McAfee. - /// - McAfee - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Backends/Extensions/ServiceCollectionExtensions.cs b/MalwareMultiScan.Backends/Extensions/ServiceCollectionExtensions.cs index 0c2bf00..a5c7478 100644 --- a/MalwareMultiScan.Backends/Extensions/ServiceCollectionExtensions.cs +++ b/MalwareMultiScan.Backends/Extensions/ServiceCollectionExtensions.cs @@ -1,9 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; -using EasyNetQ; -using MalwareMultiScan.Backends.Backends.Implementations; -using MalwareMultiScan.Backends.Enums; -using MalwareMultiScan.Backends.Interfaces; +using MalwareMultiScan.Backends.Backends; +using MalwareMultiScan.Backends.Backends.Interfaces; using MalwareMultiScan.Backends.Services.Implementations; using MalwareMultiScan.Backends.Services.Interfaces; using Microsoft.Extensions.Configuration; @@ -17,55 +15,42 @@ namespace MalwareMultiScan.Backends.Extensions [ExcludeFromCodeCoverage] public static class ServiceCollectionExtensions { - /// - /// Add RabbitMQ service. - /// - /// Service collection. - /// Configuration. - public static void AddRabbitMq(this IServiceCollection services, IConfiguration configuration) - { - services.AddSingleton(x => - RabbitHutch.CreateBus(configuration.GetConnectionString("RabbitMQ"))); - } - /// /// Add scanning backend. /// /// Service collection. /// Configuration. /// Unknown backend. - public static void AddScanningBackend(this IServiceCollection services, IConfiguration configuration) + public static void AddScanBackend(this IServiceCollection services, IConfiguration configuration) { services.AddSingleton(); - switch (configuration.GetValue("BackendType")) + switch (configuration.GetValue("BACKEND_ID")) { - case BackendType.Dummy: - services.AddSingleton(); - break; - case BackendType.Defender: - services.AddSingleton(); - break; - case BackendType.Clamav: + case "clamav": services.AddSingleton(); break; - case BackendType.DrWeb: + case "drweb": services.AddSingleton(); break; - case BackendType.Kes: + case "kes": services.AddSingleton(); break; - case BackendType.Comodo: + case "comodo": services.AddSingleton(); break; - case BackendType.Sophos: + case "sophos": services.AddSingleton(); break; - case BackendType.McAfee: + case "mcafee": services.AddSingleton(); break; + case "windows-defender": + services.AddSingleton(); + break; default: - throw new ArgumentOutOfRangeException(); + services.AddSingleton(); + break; } } } diff --git a/MalwareMultiScan.Backends/MalwareMultiScan.Backends.csproj b/MalwareMultiScan.Backends/MalwareMultiScan.Backends.csproj index 7611bdd..dfa620b 100644 --- a/MalwareMultiScan.Backends/MalwareMultiScan.Backends.csproj +++ b/MalwareMultiScan.Backends/MalwareMultiScan.Backends.csproj @@ -4,17 +4,13 @@ netcoreapp3.1 Volodymyr Smirnov MalwareMultiScan Backends - 1.0.0 - 1.0.0 + 1.5.0 + 1.5.0 true - - - - - + diff --git a/MalwareMultiScan.Backends/Messages/ScanResultMessage.cs b/MalwareMultiScan.Backends/Messages/ScanResultMessage.cs deleted file mode 100644 index 01686ff..0000000 --- a/MalwareMultiScan.Backends/Messages/ScanResultMessage.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace MalwareMultiScan.Backends.Messages -{ - /// - /// Scan result message. - /// - public class ScanResultMessage - { - /// - /// Result id. - /// - public string Id { get; set; } - - /// - /// Backend. - /// - public string Backend { get; set; } - - /// - /// Status. - /// - public bool Succeeded { get; set; } - - /// - /// List of detected threats. - /// - public string[] Threats { get; set; } - - /// - /// Duration. - /// - public long Duration { get; set; } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Backends/Services/Implementations/ProcessRunner.cs b/MalwareMultiScan.Backends/Services/ProcessRunner.cs similarity index 100% rename from MalwareMultiScan.Backends/Services/Implementations/ProcessRunner.cs rename to MalwareMultiScan.Backends/Services/ProcessRunner.cs diff --git a/MalwareMultiScan.Scanner/Dockerfile b/MalwareMultiScan.Scanner/Dockerfile index 56ccb3a..f5c0ce9 100644 --- a/MalwareMultiScan.Scanner/Dockerfile +++ b/MalwareMultiScan.Scanner/Dockerfile @@ -4,6 +4,7 @@ WORKDIR /src COPY MalwareMultiScan.Scanner /src/MalwareMultiScan.Scanner COPY MalwareMultiScan.Backends /src/MalwareMultiScan.Backends +COPY MalwareMultiScan.Shared /src/MalwareMultiScan.Shared RUN dotnet publish -c Release -r linux-x64 -o ./publish MalwareMultiScan.Scanner/MalwareMultiScan.Scanner.csproj diff --git a/MalwareMultiScan.Scanner/MalwareMultiScan.Scanner.csproj b/MalwareMultiScan.Scanner/MalwareMultiScan.Scanner.csproj index b904dcc..19e6ff6 100644 --- a/MalwareMultiScan.Scanner/MalwareMultiScan.Scanner.csproj +++ b/MalwareMultiScan.Scanner/MalwareMultiScan.Scanner.csproj @@ -1,20 +1,13 @@ - netcoreapp3.1 Volodymyr Smirnov MalwareMultiScan Scanner - 1.0.1 - 1.0.1 + 1.5.0 + 1.5.0 true - - - - - - diff --git a/MalwareMultiScan.Scanner/Program.cs b/MalwareMultiScan.Scanner/Program.cs index 8fc6e45..e612b93 100644 --- a/MalwareMultiScan.Scanner/Program.cs +++ b/MalwareMultiScan.Scanner/Program.cs @@ -1,13 +1,15 @@ using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -using Hangfire; -using Hangfire.MemoryStorage; using MalwareMultiScan.Backends.Extensions; -using MalwareMultiScan.Scanner.Services.Implementations; -using MalwareMultiScan.Scanner.Services.Interfaces; +using MalwareMultiScan.Backends.Services.Implementations; +using MalwareMultiScan.Backends.Services.Interfaces; +using MalwareMultiScan.Scanner.Services; +using MalwareMultiScan.Shared.Extensions; +using MalwareMultiScan.Shared.Services.Interfaces; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; namespace MalwareMultiScan.Scanner { @@ -17,29 +19,32 @@ namespace MalwareMultiScan.Scanner public static async Task Main(string[] args) { await Host.CreateDefaultBuilder(args) - .ConfigureAppConfiguration(configure => + .ConfigureLogging((context, builder) => { - configure.AddJsonFile("appsettings.json"); - configure.AddEnvironmentVariables(); + builder.AddConsole(); + builder.AddConfiguration(context.Configuration); + }) + .ConfigureAppConfiguration(builder => + { + builder.AddJsonFile("appsettings.json"); + builder.AddEnvironmentVariables(); }) .ConfigureServices((context, services) => { - services.AddLogging(); + services.AddConsul(context.Configuration); + services.AddScanBackend(context.Configuration); - services.AddRabbitMq(context.Configuration); - services.AddScanningBackend(context.Configuration); + services.AddHangfire(context.Configuration, + context.Configuration.GetValue("BACKEND_ID")); + services.AddSingleton(); services.AddSingleton(); - services.AddHostedService(); - services.AddHangfire( - configuration => configuration.UseMemoryStorage()); - - services.AddHangfireServer(options => - { - options.WorkerCount = context.Configuration.GetValue("WorkerCount"); - }); - }).RunConsoleAsync(); + services.AddHostedService(); + }) + .UseConsoleLifetime() + .Build() + .RunAsync(); } } } \ No newline at end of file diff --git a/MalwareMultiScan.Scanner/Properties/launchSettings.json b/MalwareMultiScan.Scanner/Properties/launchSettings.json new file mode 100644 index 0000000..02c6827 --- /dev/null +++ b/MalwareMultiScan.Scanner/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "MalwareMultiScan.Scanner": { + "commandName": "Project" + } + } +} diff --git a/MalwareMultiScan.Scanner/Services/ConsulHostedService.cs b/MalwareMultiScan.Scanner/Services/ConsulHostedService.cs new file mode 100644 index 0000000..4674c3b --- /dev/null +++ b/MalwareMultiScan.Scanner/Services/ConsulHostedService.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Consul; +using MalwareMultiScan.Backends.Backends.Interfaces; +using Microsoft.Extensions.Hosting; + +namespace MalwareMultiScan.Scanner.Services +{ + /// + /// Consul registration hosted service. + /// + public class ConsulHostedService : BackgroundService + { + private static readonly string ServiceId = Dns.GetHostName(); + private static readonly string CheckId = $"ttl-{ServiceId}"; + + private readonly IConsulClient _consulClient; + private readonly AgentServiceRegistration _registration; + + /// + /// Initialize consul hosted service. + /// + /// Consul client. + /// Scan backend. + public ConsulHostedService( + IConsulClient consulClient, + IScanBackend scanBackend) + { + _consulClient = consulClient; + + _registration = new AgentServiceRegistration + { + ID = ServiceId, + Name = "scanner", + + Meta = new Dictionary + { + {"BackendId", scanBackend.Id} + } + }; + } + + /// + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (!stoppingToken.IsCancellationRequested) + { + await _consulClient.Agent.PassTTL( + CheckId, string.Empty, stoppingToken); + + await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken); + } + } + + /// + public override async Task StartAsync(CancellationToken cancellationToken) + { + await _consulClient.Agent.ServiceDeregister( + _registration.ID, cancellationToken); + + await _consulClient.Agent.ServiceRegister( + _registration, cancellationToken); + + await _consulClient.Agent.CheckRegister(new AgentCheckRegistration + { + ID = CheckId, + Name = "TTL", + ServiceID = ServiceId, + TTL = TimeSpan.FromSeconds(15), + DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(60) + }, cancellationToken); + + await base.StartAsync(cancellationToken); + } + + /// + public override async Task StopAsync(CancellationToken cancellationToken) + { + await _consulClient.Agent.ServiceDeregister( + _registration.ID, cancellationToken); + + await _consulClient.Agent.CheckDeregister( + CheckId, cancellationToken); + + await base.StopAsync(cancellationToken); + } + } +} \ No newline at end of file diff --git a/MalwareMultiScan.Scanner/Services/Implementations/ScanHostedService.cs b/MalwareMultiScan.Scanner/Services/Implementations/ScanHostedService.cs deleted file mode 100644 index 180249a..0000000 --- a/MalwareMultiScan.Scanner/Services/Implementations/ScanHostedService.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using EasyNetQ; -using Hangfire; -using MalwareMultiScan.Backends.Interfaces; -using MalwareMultiScan.Backends.Messages; -using MalwareMultiScan.Scanner.Services.Interfaces; -using Microsoft.Extensions.Logging; - -namespace MalwareMultiScan.Scanner.Services.Implementations -{ - /// - public class ScanHostedService : IScanHostedService - { - private readonly IScanBackend _backend; - private readonly IBus _bus; - private readonly ILogger _logger; - - /// - /// Initialise scan hosted service. - /// - /// Logger. - /// Scan backend. - /// EasyNetQ bus. - public ScanHostedService( - ILogger logger, - IScanBackend backend, IBus bus) - { - _logger = logger; - _bus = bus; - _backend = backend; - } - - /// - public Task StartAsync(CancellationToken cancellationToken) - { - _bus.Receive(_backend.Id, message => - BackgroundJob.Enqueue(j => j.Process(message))); - - _logger.LogInformation( - $"Started scan hosting service for the backend {_backend.Id}"); - - return Task.CompletedTask; - } - - /// - public Task StopAsync(CancellationToken cancellationToken) - { - _bus.Dispose(); - - _logger.LogInformation( - $"Stopped scan hosting service for the backend {_backend.Id}"); - - return Task.CompletedTask; - } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Scanner/Services/Interfaces/IScanBackgroundJob.cs b/MalwareMultiScan.Scanner/Services/Interfaces/IScanBackgroundJob.cs deleted file mode 100644 index e688dc9..0000000 --- a/MalwareMultiScan.Scanner/Services/Interfaces/IScanBackgroundJob.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Threading.Tasks; -using MalwareMultiScan.Backends.Messages; - -namespace MalwareMultiScan.Scanner.Services.Interfaces -{ - /// - /// Background scanning job. - /// - public interface IScanBackgroundJob - { - /// - /// Process scan request in the background worker. - /// - /// Request message. - Task Process(ScanRequestMessage requestMessage); - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Scanner/Services/Interfaces/IScanHostedService.cs b/MalwareMultiScan.Scanner/Services/Interfaces/IScanHostedService.cs deleted file mode 100644 index 8286292..0000000 --- a/MalwareMultiScan.Scanner/Services/Interfaces/IScanHostedService.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.Extensions.Hosting; - -namespace MalwareMultiScan.Scanner.Services.Interfaces -{ - /// - /// Scan hosted service. - /// - public interface IScanHostedService : IHostedService - { - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Scanner/Services/Implementations/ScanBackgroundJob.cs b/MalwareMultiScan.Scanner/Services/ScanBackgroundJob.cs similarity index 55% rename from MalwareMultiScan.Scanner/Services/Implementations/ScanBackgroundJob.cs rename to MalwareMultiScan.Scanner/Services/ScanBackgroundJob.cs index 0804298..0fbf5da 100644 --- a/MalwareMultiScan.Scanner/Services/Implementations/ScanBackgroundJob.cs +++ b/MalwareMultiScan.Scanner/Services/ScanBackgroundJob.cs @@ -2,52 +2,58 @@ using System; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; -using EasyNetQ; -using MalwareMultiScan.Backends.Interfaces; -using MalwareMultiScan.Backends.Messages; -using MalwareMultiScan.Scanner.Services.Interfaces; +using Hangfire; +using Hangfire.States; +using MalwareMultiScan.Backends.Backends.Interfaces; +using MalwareMultiScan.Shared.Enums; +using MalwareMultiScan.Shared.Message; +using MalwareMultiScan.Shared.Services.Interfaces; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; -namespace MalwareMultiScan.Scanner.Services.Implementations +namespace MalwareMultiScan.Scanner.Services { /// public class ScanBackgroundJob : IScanBackgroundJob { private readonly IScanBackend _backend; - private readonly IBus _bus; private readonly IConfiguration _configuration; private readonly ILogger _logger; + private readonly IBackgroundJobClient _jobClient; /// /// Initialize scan background job. /// /// Configuration. - /// Bus. /// Logger. /// Scan backend. - public ScanBackgroundJob(IConfiguration configuration, IBus bus, - ILogger logger, IScanBackend backend) + /// Background job client. + public ScanBackgroundJob( + IScanBackend backend, + IConfiguration configuration, + ILogger logger, + IBackgroundJobClient jobClient) { - _configuration = configuration; - _bus = bus; - _logger = logger; _backend = backend; + _configuration = configuration; + _logger = logger; + _jobClient = jobClient; } /// - public async Task Process(ScanRequestMessage message) + public async Task Process(ScanQueueMessage message) { _logger.LogInformation( $"Starting scan of {message.Uri} via backend {_backend.Id} from {message.Id}"); var cancellationTokenSource = new CancellationTokenSource( - TimeSpan.FromSeconds(_configuration.GetValue("MaxScanningTime"))); + TimeSpan.FromSeconds(_configuration.GetValue("MAX_SCANNING_TIME"))); + + var cancellationToken = cancellationTokenSource.Token; var result = new ScanResultMessage { - Id = message.Id, - Backend = _backend.Id + Status = ScanResultStatus.Queued }; var stopwatch = new Stopwatch(); @@ -56,10 +62,9 @@ namespace MalwareMultiScan.Scanner.Services.Implementations try { - result.Threats = await _backend.ScanAsync( - message.Uri, cancellationTokenSource.Token); + result.Threats = await _backend.ScanAsync(message.Uri, cancellationToken); - result.Succeeded = true; + result.Status = ScanResultStatus.Succeeded; _logger.LogInformation( $"Backend {_backend.Id} completed a scan of {message.Id} " + @@ -67,7 +72,7 @@ namespace MalwareMultiScan.Scanner.Services.Implementations } catch (Exception exception) { - result.Succeeded = false; + result.Status = ScanResultStatus.Failed; _logger.LogError( exception, "Scanning failed with exception"); @@ -79,11 +84,19 @@ namespace MalwareMultiScan.Scanner.Services.Implementations result.Duration = stopwatch.ElapsedMilliseconds / 1000; - _logger.LogInformation( - $"Sending scan results with status {result.Succeeded}"); + try + { + _logger.LogInformation( + $"Sending scan results with status {result.Status}"); - await _bus.SendAsync( - _configuration.GetValue("ResultsSubscriptionId"), result); + _jobClient.Create( + x => x.Report(message.Id, _backend.Id, result), + new EnqueuedState("default")); + } + catch (Exception exception) + { + _logger.LogError(exception, "Failed to send scan results"); + } } } } \ No newline at end of file diff --git a/MalwareMultiScan.Scanner/appsettings.json b/MalwareMultiScan.Scanner/appsettings.json index 55e4b45..3edade7 100644 --- a/MalwareMultiScan.Scanner/appsettings.json +++ b/MalwareMultiScan.Scanner/appsettings.json @@ -1,19 +1,9 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - }, - - "BackendType": "Dummy", + "REDIS_ADDRESS": "localhost:6379", - "MaxScanningTime": 60, - "ResultsSubscriptionId": "mms.results", - "WorkerCount": 4, + "CONSUL_ADDRESS": "http://localhost:8500", - "ConnectionStrings": { - "RabbitMQ": "host=localhost;prefetchcount=1" - } + "BACKEND_ID": "dummy", + "MAX_SCANNING_TIME": 60, + "WORKER_COUNT": 4 } diff --git a/MalwareMultiScan.Shared/Enums/ScanResultStatus.cs b/MalwareMultiScan.Shared/Enums/ScanResultStatus.cs new file mode 100644 index 0000000..0f2ab06 --- /dev/null +++ b/MalwareMultiScan.Shared/Enums/ScanResultStatus.cs @@ -0,0 +1,23 @@ +namespace MalwareMultiScan.Shared.Enums +{ + /// + /// Scan result status. + /// + public enum ScanResultStatus + { + /// + /// Scan is queued. + /// + Queued, + + /// + /// Scan succeeded. + /// + Succeeded, + + /// + /// Scan failed. + /// + Failed + } +} \ No newline at end of file diff --git a/MalwareMultiScan.Shared/Extensions/ServiceCollectionExtensions.cs b/MalwareMultiScan.Shared/Extensions/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..d3c5715 --- /dev/null +++ b/MalwareMultiScan.Shared/Extensions/ServiceCollectionExtensions.cs @@ -0,0 +1,60 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Consul; +using Hangfire; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace MalwareMultiScan.Shared.Extensions +{ + /// + /// Extensions for IServiceCollection. + /// + [ExcludeFromCodeCoverage] + public static class ServiceCollectionExtensions + { + /// + /// Add consul to the service collection and register the node on start. + /// + /// Service collection. + /// Configuration. + public static void AddConsul(this IServiceCollection services, IConfiguration configuration) + { + services.AddSingleton(new ConsulClient(config => + { + config.Address = configuration.GetValue("CONSUL_ADDRESS"); + config.WaitTime = TimeSpan.FromSeconds(120); + })); + } + + /// + /// Add Hangfire with Redis storage. + /// + /// Service collection. + /// Configuration. + /// Queue names. + public static void AddHangfire(this IServiceCollection services, + IConfiguration configuration, params string[] queues) + { + if (queues.Length == 0) + queues = new[] {"default"}; + + services.AddHangfire(options => + options.UseRedisStorage(configuration.GetValue("REDIS_ADDRESS"))); + + services.AddHangfireServer(options => + { + options.Queues = queues; + + options.ServerTimeout = TimeSpan.FromSeconds(30); + options.HeartbeatInterval = TimeSpan.FromSeconds(5); + options.ServerCheckInterval = TimeSpan.FromSeconds(15); + + var workerCount = configuration.GetValue("WORKER_COUNT"); + + if (workerCount > 0) + options.WorkerCount = workerCount; + }); + } + } +} \ No newline at end of file diff --git a/MalwareMultiScan.Shared/MalwareMultiScan.Shared.csproj b/MalwareMultiScan.Shared/MalwareMultiScan.Shared.csproj new file mode 100644 index 0000000..4f90da1 --- /dev/null +++ b/MalwareMultiScan.Shared/MalwareMultiScan.Shared.csproj @@ -0,0 +1,19 @@ + + + netcoreapp3.1 + Volodymyr Smirnov + MalwareMultiScan Shared + 1.5.0 + 1.5.0 + true + + + + + + + + + + + diff --git a/MalwareMultiScan.Backends/Messages/ScanRequestMessage.cs b/MalwareMultiScan.Shared/Message/ScanQueueMessage.cs similarity index 58% rename from MalwareMultiScan.Backends/Messages/ScanRequestMessage.cs rename to MalwareMultiScan.Shared/Message/ScanQueueMessage.cs index 011e49b..b900abb 100644 --- a/MalwareMultiScan.Backends/Messages/ScanRequestMessage.cs +++ b/MalwareMultiScan.Shared/Message/ScanQueueMessage.cs @@ -1,19 +1,19 @@ using System; -namespace MalwareMultiScan.Backends.Messages +namespace MalwareMultiScan.Shared.Message { /// - /// Scan request message. + /// Scan queue message. /// - public class ScanRequestMessage + public class ScanQueueMessage { /// - /// Result id. + /// Scan result id. /// public string Id { get; set; } - + /// - /// Remote URL. + /// Scan file URL. /// public Uri Uri { get; set; } } diff --git a/MalwareMultiScan.Shared/Message/ScanResultMessage.cs b/MalwareMultiScan.Shared/Message/ScanResultMessage.cs new file mode 100644 index 0000000..e2d0727 --- /dev/null +++ b/MalwareMultiScan.Shared/Message/ScanResultMessage.cs @@ -0,0 +1,25 @@ +using MalwareMultiScan.Shared.Enums; + +namespace MalwareMultiScan.Shared.Message +{ + /// + /// Scan result message. + /// + public class ScanResultMessage + { + /// + /// Scan status. + /// + public ScanResultStatus Status { get; set; } + + /// + /// Scan duration in seconds. + /// + public long Duration { get; set; } + + /// + /// Detected threats. + /// + public string[] Threats { get; set; } + } +} \ No newline at end of file diff --git a/MalwareMultiScan.Shared/Services/Interfaces/IScanBackgroundJob.cs b/MalwareMultiScan.Shared/Services/Interfaces/IScanBackgroundJob.cs new file mode 100644 index 0000000..ae26ee7 --- /dev/null +++ b/MalwareMultiScan.Shared/Services/Interfaces/IScanBackgroundJob.cs @@ -0,0 +1,19 @@ +using System.Threading.Tasks; +using Hangfire; +using MalwareMultiScan.Shared.Message; + +namespace MalwareMultiScan.Shared.Services.Interfaces +{ + /// + /// Scan background job. + /// + [AutomaticRetry(Attempts = 0, OnAttemptsExceeded = AttemptsExceededAction.Delete)] + public interface IScanBackgroundJob + { + /// + /// Start scan. + /// + /// Scan queue message. + Task Process(ScanQueueMessage message); + } +} \ No newline at end of file diff --git a/MalwareMultiScan.Shared/Services/Interfaces/IScanResultJob.cs b/MalwareMultiScan.Shared/Services/Interfaces/IScanResultJob.cs new file mode 100644 index 0000000..63f4237 --- /dev/null +++ b/MalwareMultiScan.Shared/Services/Interfaces/IScanResultJob.cs @@ -0,0 +1,32 @@ +using System; +using System.Threading.Tasks; +using Hangfire; +using MalwareMultiScan.Shared.Message; + +namespace MalwareMultiScan.Shared.Services.Interfaces +{ + /// + /// Scan background result job. + /// + public interface IScanResultJob + { + /// + /// Report job status. + /// + /// Result id. + /// Backend id. + /// Scan result. + [AutomaticRetry(Attempts = 0, OnAttemptsExceeded = AttemptsExceededAction.Delete)] + Task Report(string resultId, string backendId, ScanResultMessage result); + + /// + /// Notify remote URL on scan result. + /// + /// Base URL. + /// Result id. + /// Backend id. + /// Scan result. + [AutomaticRetry(Attempts = 0, OnAttemptsExceeded = AttemptsExceededAction.Delete)] + Task Notify(Uri uri, string resultId, string backendId, ScanResultMessage result); + } +} \ No newline at end of file diff --git a/MalwareMultiScan.Tests/Api/AttributesTests.cs b/MalwareMultiScan.Tests/Api/AttributesTests.cs index 86b2d4a..8938c6f 100644 --- a/MalwareMultiScan.Tests/Api/AttributesTests.cs +++ b/MalwareMultiScan.Tests/Api/AttributesTests.cs @@ -36,7 +36,7 @@ namespace MalwareMultiScan.Tests.Api new MemoryConfigurationProvider(new MemoryConfigurationSource()) }) { - ["MaxFileSize"] = "100" + ["FILE_SIZE_LIMIT"] = "100" }; var context = new ValidationContext( diff --git a/MalwareMultiScan.Tests/Api/ControllersTests.cs b/MalwareMultiScan.Tests/Api/ControllersTests.cs index 7383af2..4b71718 100644 --- a/MalwareMultiScan.Tests/Api/ControllersTests.cs +++ b/MalwareMultiScan.Tests/Api/ControllersTests.cs @@ -1,7 +1,7 @@ using System.IO; using System.Threading.Tasks; using MalwareMultiScan.Api.Controllers; -using MalwareMultiScan.Api.Data.Models; +using MalwareMultiScan.Api.Data; using MalwareMultiScan.Api.Services.Interfaces; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; diff --git a/MalwareMultiScan.Tests/Api/ReceiverHostedServiceTests.cs b/MalwareMultiScan.Tests/Api/ReceiverHostedServiceTests.cs deleted file mode 100644 index a92f897..0000000 --- a/MalwareMultiScan.Tests/Api/ReceiverHostedServiceTests.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading.Tasks; -using EasyNetQ; -using MalwareMultiScan.Api.Services.Implementations; -using MalwareMultiScan.Api.Services.Interfaces; -using MalwareMultiScan.Backends.Messages; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Configuration.Memory; -using Microsoft.Extensions.Logging; -using Moq; -using NUnit.Framework; - -namespace MalwareMultiScan.Tests.Api -{ - public class ReceiverHostedServiceTests - { - private Mock _busMock; - private IReceiverHostedService _receiverHostedService; - private Mock _scanResultServiceMock; - - [SetUp] - public void SetUp() - { - _busMock = new Mock(); - - _busMock - .Setup(x => x.Receive("mms.results", It.IsAny>())) - .Callback>((s, func) => - { - var task = func.Invoke(new ScanResultMessage - { - Id = "test", - Backend = "dummy", - Duration = 100, - Succeeded = true, - Threats = new[] {"Test"} - }); - - task.Wait(); - }); - - _scanResultServiceMock = new Mock(); - - var configuration = new ConfigurationRoot(new List - { - new MemoryConfigurationProvider(new MemoryConfigurationSource()) - }) - { - ["ResultsSubscriptionId"] = "mms.results" - }; - - var httpClientFactoryMock = new Mock(); - - _receiverHostedService = new ReceiverHostedService( - _busMock.Object, - configuration, - _scanResultServiceMock.Object, - Mock.Of>(), - httpClientFactoryMock.Object); - } - - [Test] - public async Task TestBusReceiveScanResultMessage() - { - await _receiverHostedService.StartAsync(default); - - _scanResultServiceMock.Verify(x => x.UpdateScanResultForBackend( - "test", "dummy", 100, true, true, new[] {"Test"}), Times.Once); - } - - [Test] - public async Task TestBusIsDisposedOnStop() - { - await _receiverHostedService.StopAsync(default); - - _busMock.Verify(x => x.Dispose(), Times.Once); - } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Tests/Api/ScanBackendServiceTests.cs b/MalwareMultiScan.Tests/Api/ScanBackendServiceTests.cs deleted file mode 100644 index 8e8fbf2..0000000 --- a/MalwareMultiScan.Tests/Api/ScanBackendServiceTests.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using EasyNetQ; -using MalwareMultiScan.Api.Data.Configuration; -using MalwareMultiScan.Api.Data.Models; -using MalwareMultiScan.Api.Services.Implementations; -using MalwareMultiScan.Api.Services.Interfaces; -using MalwareMultiScan.Backends.Messages; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Configuration.Memory; -using Microsoft.Extensions.Logging; -using Moq; -using NUnit.Framework; - -namespace MalwareMultiScan.Tests.Api -{ - public class ScanBackendServiceTests - { - private Mock _busMock; - private IScanBackendService _scanBackendService; - - [SetUp] - public void SetUp() - { - var configuration = new ConfigurationRoot(new List - { - new MemoryConfigurationProvider(new MemoryConfigurationSource()) - }) - { - ["BackendsConfiguration"] = "backends.yaml" - }; - - _busMock = new Mock(); - - _scanBackendService = new ScanBackendService( - configuration, - _busMock.Object, - Mock.Of>()); - } - - [Test] - public void TestBackendsAreNotEmpty() - { - Assert.IsNotEmpty(_scanBackendService.List); - } - - [Test] - public async Task TestQueueUrlScan() - { - await _scanBackendService.QueueUrlScan( - new ScanResult {Id = "test"}, - new ScanBackend {Id = "dummy"}, - "http://test.com" - ); - - _busMock.Verify(x => x.SendAsync( - "dummy", It.Is(r => - r.Id == "test" && - r.Uri == new Uri("http://test.com"))), Times.Once); - } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Tests/Api/ScanResultServiceTests.cs b/MalwareMultiScan.Tests/Api/ScanResultServiceTests.cs index e85cdbb..9332c5b 100644 --- a/MalwareMultiScan.Tests/Api/ScanResultServiceTests.cs +++ b/MalwareMultiScan.Tests/Api/ScanResultServiceTests.cs @@ -1,9 +1,11 @@ using System.IO; using System.Threading.Tasks; -using MalwareMultiScan.Api.Data.Configuration; -using MalwareMultiScan.Api.Data.Models; -using MalwareMultiScan.Api.Services.Implementations; +using Consul; +using Hangfire; +using MalwareMultiScan.Api.Services; using MalwareMultiScan.Api.Services.Interfaces; +using MalwareMultiScan.Shared.Enums; +using MalwareMultiScan.Shared.Message; using Mongo2Go; using MongoDB.Bson; using MongoDB.Driver; @@ -17,7 +19,6 @@ namespace MalwareMultiScan.Tests.Api { private MongoDbRunner _mongoDbRunner; private IScanResultService _resultService; - private Mock _scanBackendService; [SetUp] public void SetUp() @@ -27,20 +28,11 @@ namespace MalwareMultiScan.Tests.Api var connection = new MongoClient(_mongoDbRunner.ConnectionString); var database = connection.GetDatabase("Test"); var gridFsBucket = new GridFSBucket(database); - - _scanBackendService = new Mock(); - - _scanBackendService - .SetupGet(x => x.List) - .Returns(() => new[] - { - new ScanBackend {Id = "dummy", Enabled = true}, - new ScanBackend {Id = "clamav", Enabled = true}, - new ScanBackend {Id = "disabled", Enabled = false} - }); - + _resultService = new ScanResultService( - database, gridFsBucket, _scanBackendService.Object); + database, gridFsBucket, + Mock.Of(), + Mock.Of()); } [TearDown] @@ -85,10 +77,14 @@ namespace MalwareMultiScan.Tests.Api var result = await _resultService.CreateScanResult(null); Assert.NotNull(result.Id); - Assert.That(result.Results, Contains.Key("dummy")); await _resultService.UpdateScanResultForBackend( - result.Id, "dummy", 100, true, true, new[] {"Test"}); + result.Id, "dummy", new ScanResultMessage + { + Duration = 100, + Status = ScanResultStatus.Succeeded, + Threats = new []{"Test"} + }); result = await _resultService.GetScanResult(result.Id); @@ -97,25 +93,10 @@ namespace MalwareMultiScan.Tests.Api var dummyResult = result.Results["dummy"]; - Assert.IsTrue(dummyResult.Completed); - Assert.IsTrue(dummyResult.Succeeded); + Assert.IsTrue(dummyResult.Status == ScanResultStatus.Succeeded); Assert.AreEqual(dummyResult.Duration, 100); Assert.IsNotEmpty(dummyResult.Threats); Assert.That(dummyResult.Threats, Is.EquivalentTo(new[] {"Test"})); } - - [Test] - public async Task TestQueueUrlScan() - { - var result = await _resultService.CreateScanResult(null); - - await _resultService.QueueUrlScan( - result, "http://url.com"); - - _scanBackendService.Verify(x => x.QueueUrlScan( - It.IsAny(), - It.IsAny(), - "http://url.com"), Times.Exactly(2)); - } } } \ No newline at end of file diff --git a/MalwareMultiScan.Tests/Backends/BackendsTests.cs b/MalwareMultiScan.Tests/Backends/BackendsTests.cs index 619da9a..e8bf939 100644 --- a/MalwareMultiScan.Tests/Backends/BackendsTests.cs +++ b/MalwareMultiScan.Tests/Backends/BackendsTests.cs @@ -1,7 +1,7 @@ using System.Threading; using System.Threading.Tasks; -using MalwareMultiScan.Backends.Backends.Implementations; -using MalwareMultiScan.Backends.Interfaces; +using MalwareMultiScan.Backends.Backends; +using MalwareMultiScan.Backends.Backends.Interfaces; using MalwareMultiScan.Backends.Services.Interfaces; using Moq; using NUnit.Framework; diff --git a/MalwareMultiScan.Tests/Scanner/ScanBackgroundJobTests.cs b/MalwareMultiScan.Tests/Scanner/ScanBackgroundJobTests.cs deleted file mode 100644 index d255d03..0000000 --- a/MalwareMultiScan.Tests/Scanner/ScanBackgroundJobTests.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using EasyNetQ; -using MalwareMultiScan.Backends.Interfaces; -using MalwareMultiScan.Backends.Messages; -using MalwareMultiScan.Scanner.Services.Implementations; -using MalwareMultiScan.Scanner.Services.Interfaces; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Configuration.Memory; -using Microsoft.Extensions.Logging; -using Moq; -using NUnit.Framework; - -namespace MalwareMultiScan.Tests.Scanner -{ - public class ScanBackgroundJobTests - { - private Mock _busMock; - private Mock _scanBackendMock; - private IScanBackgroundJob _scanBackgroundJob; - - [SetUp] - public void SetUp() - { - var configuration = new ConfigurationRoot(new List - { - new MemoryConfigurationProvider(new MemoryConfigurationSource()) - }) - { - ["ResultsSubscriptionId"] = "mms.results" - }; - - _busMock = new Mock(); - - _scanBackendMock = new Mock(); - - _scanBackendMock - .SetupGet(x => x.Id) - .Returns("dummy"); - - _scanBackendMock - .Setup(x => x.ScanAsync(It.IsAny(), It.IsAny())) - .Returns(Task.FromResult(new[] {"Test"})); - - _scanBackgroundJob = new ScanBackgroundJob(configuration, _busMock.Object, - Mock.Of>(), _scanBackendMock.Object); - } - - [Test] - public async Task TestMessageProcessing() - { - await _scanBackgroundJob.Process(new ScanRequestMessage - { - Id = "test", - Uri = new Uri("http://test.com") - }); - - _scanBackendMock.Verify( - x => x.ScanAsync(It.IsAny(), It.IsAny())); - - _busMock.Verify(x => x.SendAsync("mms.results", It.Is(m => - m.Succeeded && m.Backend == "dummy" && m.Id == "test" && m.Threats.Contains("Test")))); - } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Tests/Scanner/ScanHostedServiceTests.cs b/MalwareMultiScan.Tests/Scanner/ScanHostedServiceTests.cs deleted file mode 100644 index a7a2a19..0000000 --- a/MalwareMultiScan.Tests/Scanner/ScanHostedServiceTests.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Threading.Tasks; -using EasyNetQ; -using MalwareMultiScan.Backends.Interfaces; -using MalwareMultiScan.Backends.Messages; -using MalwareMultiScan.Scanner.Services.Implementations; -using MalwareMultiScan.Scanner.Services.Interfaces; -using Microsoft.Extensions.Logging; -using Moq; -using NUnit.Framework; - -namespace MalwareMultiScan.Tests.Scanner -{ - public class ScanHostedServiceTests - { - private Mock _busMock; - private Mock _scanBackendMock; - private IScanHostedService _scanHostedService; - - [SetUp] - public void SetUp() - { - _busMock = new Mock(); - - _scanBackendMock = new Mock(); - - _scanBackendMock - .SetupGet(x => x.Id) - .Returns("dummy"); - - _scanHostedService = new ScanHostedService( - Mock.Of>(), - _scanBackendMock.Object, - _busMock.Object); - } - - [Test] - public async Task TestBusReceiveScanResultMessage() - { - await _scanHostedService.StartAsync(default); - - _busMock.Verify( - x => x.Receive("dummy", It.IsAny>())); - } - - [Test] - public async Task TestBusIsDisposedOnStop() - { - await _scanHostedService.StopAsync(default); - - _busMock.Verify(x => x.Dispose(), Times.Once); - } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Ui/models/scan-result-entry-flattened.ts b/MalwareMultiScan.Ui/models/scan-result-entry-flattened.ts index faa2df9..56be49c 100644 --- a/MalwareMultiScan.Ui/models/scan-result-entry-flattened.ts +++ b/MalwareMultiScan.Ui/models/scan-result-entry-flattened.ts @@ -1,19 +1,31 @@ import ScanResultEntry from '~/models/scan-result-entry'; +import {ScanResultStatus} from '~/models/scan-result-status'; export default class ScanResultEntryFlattened implements ScanResultEntry { readonly id: string; - readonly completed: boolean; - readonly succeeded: boolean | null; + readonly status: ScanResultStatus; readonly duration: number; readonly threats: string[]; - constructor(id: string, completed: boolean, succeeded: boolean | null, duration: number, threats: string[]) { + constructor(id: string, status: ScanResultStatus, duration: number, threats: string[]) { this.id = id; - this.completed = completed; - this.succeeded = succeeded; + this.status = status; this.duration = duration; this.threats = threats; } + get completed() + { + return this.status != ScanResultStatus.Queued + } + get succeeded() + { + return this.status === ScanResultStatus.Succeeded; + } + + get failed() + { + return this.status === ScanResultStatus.Failed; + } } diff --git a/MalwareMultiScan.Ui/models/scan-result-entry.ts b/MalwareMultiScan.Ui/models/scan-result-entry.ts index 96856b3..0cf0767 100644 --- a/MalwareMultiScan.Ui/models/scan-result-entry.ts +++ b/MalwareMultiScan.Ui/models/scan-result-entry.ts @@ -1,6 +1,7 @@ +import { ScanResultStatus } from '~/models/scan-result-status'; + export default interface ScanResultEntry { - readonly completed: boolean, - readonly succeeded: boolean | null, + readonly status: ScanResultStatus, readonly duration: number, readonly threats: string[] } diff --git a/MalwareMultiScan.Ui/models/scan-result-status.ts b/MalwareMultiScan.Ui/models/scan-result-status.ts new file mode 100644 index 0000000..c8a6e07 --- /dev/null +++ b/MalwareMultiScan.Ui/models/scan-result-status.ts @@ -0,0 +1,6 @@ +export enum ScanResultStatus +{ + Queued, + Succeeded, + Failed +} \ No newline at end of file diff --git a/MalwareMultiScan.Ui/package.json b/MalwareMultiScan.Ui/package.json index acbcf9e..103aba1 100644 --- a/MalwareMultiScan.Ui/package.json +++ b/MalwareMultiScan.Ui/package.json @@ -1,6 +1,6 @@ { "name": "malware-multi-scan-ui", - "version": "1.0.2", + "version": "1.5.0", "private": true, "scripts": { "dev": "nuxt-ts", diff --git a/MalwareMultiScan.Ui/pages/_id/index.vue b/MalwareMultiScan.Ui/pages/_id/index.vue index 509cb76..b85fc3a 100644 --- a/MalwareMultiScan.Ui/pages/_id/index.vue +++ b/MalwareMultiScan.Ui/pages/_id/index.vue @@ -1,9 +1,9 @@