diff --git a/MalwareMultiScan.Shared/Attributes/UrlValidationAttribute.cs b/MalwareMultiScan.Api/Attributes/HttpUrlValidationAttribute.cs similarity index 65% rename from MalwareMultiScan.Shared/Attributes/UrlValidationAttribute.cs rename to MalwareMultiScan.Api/Attributes/HttpUrlValidationAttribute.cs index 3bd58e0..feef5c4 100644 --- a/MalwareMultiScan.Shared/Attributes/UrlValidationAttribute.cs +++ b/MalwareMultiScan.Api/Attributes/HttpUrlValidationAttribute.cs @@ -1,16 +1,16 @@ using System; using System.ComponentModel.DataAnnotations; -namespace MalwareMultiScan.Shared.Attributes +namespace MalwareMultiScan.Api.Attributes { - public class UrlValidationAttribute : ValidationAttribute + public class HttpUrlValidationAttribute : ValidationAttribute { protected override ValidationResult IsValid(object value, ValidationContext validationContext) { var uri = (Uri) value; if (uri == null || !uri.IsAbsoluteUri || uri.Scheme != "http" && uri.Scheme != "https") - return new ValidationResult("Only http(s) absolute URLs are supported"); + return new ValidationResult("Only absolute http(s) URLs are supported"); return ValidationResult.Success; } diff --git a/MalwareMultiScan.Api/Controllers/QueueController.cs b/MalwareMultiScan.Api/Controllers/QueueController.cs index de4e9c4..3cb44ab 100644 --- a/MalwareMultiScan.Api/Controllers/QueueController.cs +++ b/MalwareMultiScan.Api/Controllers/QueueController.cs @@ -1,9 +1,8 @@ using System; -using System.IO; using System.Threading.Tasks; +using MalwareMultiScan.Api.Attributes; +using MalwareMultiScan.Api.Data.Models; using MalwareMultiScan.Api.Services; -using MalwareMultiScan.Shared.Attributes; -using MalwareMultiScan.Shared.Data.Responses; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -16,13 +15,13 @@ namespace MalwareMultiScan.Api.Controllers { private readonly ScanResultService _scanResultService; - public QueueController( ScanResultService scanResultService) + public QueueController(ScanResultService scanResultService) { _scanResultService = scanResultService; } - + [HttpPost("file")] - [ProducesResponseType(typeof(ResultResponse), StatusCodes.Status201Created)] + [ProducesResponseType(typeof(ScanResult), StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] public async Task ScanFile([FromForm] IFormFile file) { @@ -31,27 +30,29 @@ namespace MalwareMultiScan.Api.Controllers string storedFileId; await using (var uploadFileStream = file.OpenReadStream()) + { storedFileId = await _scanResultService.StoreFile(file.Name, uploadFileStream); - - await _scanResultService.QueueUrlScan(result, Url.Action("Index", "Download", new {id = storedFileId}, + } + + await _scanResultService.QueueUrlScan(result, Url.Action("Index", "Download", new {id = storedFileId}, Request.Scheme, Request.Host.Value)); - return Created(Url.Action("Index", "ScanResults", new {id = result.Id}, + return Created(Url.Action("Index", "ScanResults", new {id = result.Id}, Request.Scheme, Request.Host.Value), result); } - + [HttpPost("url")] - [ProducesResponseType(typeof(ResultResponse), StatusCodes.Status201Created)] + [ProducesResponseType(typeof(ScanResult), StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] - public async Task ScanUrl([FromForm] [UrlValidation] Uri url) + public async Task ScanUrl([FromForm] [HttpUrlValidation] Uri url) { var result = await _scanResultService.CreateScanResult(); - - var resultUrl = Url.Action("Index", "ScanResults", new {id = result.Id}, + + var resultUrl = Url.Action("Index", "ScanResults", new {id = result.Id}, Request.Scheme, Request.Host.Value); await _scanResultService.QueueUrlScan(result, url.ToString()); - + return Created(resultUrl, result); } } diff --git a/MalwareMultiScan.Api/Controllers/ScanResultsController.cs b/MalwareMultiScan.Api/Controllers/ScanResultsController.cs index c2aca1f..5137920 100644 --- a/MalwareMultiScan.Api/Controllers/ScanResultsController.cs +++ b/MalwareMultiScan.Api/Controllers/ScanResultsController.cs @@ -1,7 +1,6 @@ using System.Threading.Tasks; using MalwareMultiScan.Api.Data.Models; using MalwareMultiScan.Api.Services; -using MalwareMultiScan.Shared.Data.Responses; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -18,7 +17,7 @@ namespace MalwareMultiScan.Api.Controllers { _scanResultService = scanResultService; } - + [HttpGet("{id}")] [ProducesResponseType(typeof(ScanResult), StatusCodes.Status200OK)] public async Task Index(string id) diff --git a/MalwareMultiScan.Api/Data/Models/ScanResult.cs b/MalwareMultiScan.Api/Data/Models/ScanResult.cs index c61369c..76315c8 100644 --- a/MalwareMultiScan.Api/Data/Models/ScanResult.cs +++ b/MalwareMultiScan.Api/Data/Models/ScanResult.cs @@ -10,7 +10,7 @@ namespace MalwareMultiScan.Api.Data.Models [BsonRepresentation(BsonType.ObjectId)] public string Id { get; set; } - public Dictionary Results { get; set; } = + public Dictionary Results { get; set; } = new Dictionary(); } } \ No newline at end of file diff --git a/MalwareMultiScan.Api/Data/Response/ScanBackendResponse.cs b/MalwareMultiScan.Api/Data/Response/ScanBackendResponse.cs deleted file mode 100644 index a7afe8f..0000000 --- a/MalwareMultiScan.Api/Data/Response/ScanBackendResponse.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace MalwareMultiScan.Api.Data.Response -{ - public class ScanBackendResponse - { - public string Id { get; set; } - public string Name { get; set; } - public bool Online { get; set; } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Api/MalwareMultiScan.Api.csproj b/MalwareMultiScan.Api/MalwareMultiScan.Api.csproj index bb6c35a..472b337 100644 --- a/MalwareMultiScan.Api/MalwareMultiScan.Api.csproj +++ b/MalwareMultiScan.Api/MalwareMultiScan.Api.csproj @@ -23,6 +23,7 @@ + diff --git a/MalwareMultiScan.Api/Services/ReceiverHostedService.cs b/MalwareMultiScan.Api/Services/ReceiverHostedService.cs index 61a2402..6996d5d 100644 --- a/MalwareMultiScan.Api/Services/ReceiverHostedService.cs +++ b/MalwareMultiScan.Api/Services/ReceiverHostedService.cs @@ -1,9 +1,10 @@ using System.Threading; using System.Threading.Tasks; using EasyNetQ; -using MalwareMultiScan.Shared.Data.Messages; +using MalwareMultiScan.Backends.Messages; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; namespace MalwareMultiScan.Api.Services { @@ -12,21 +13,31 @@ namespace MalwareMultiScan.Api.Services private readonly IBus _bus; private readonly IConfiguration _configuration; private readonly ScanResultService _scanResultService; + private readonly ILogger _logger; - public ReceiverHostedService(IBus bus, IConfiguration configuration, ScanResultService scanResultService) + public ReceiverHostedService(IBus bus, IConfiguration configuration, ScanResultService scanResultService, + ILogger logger) { _bus = bus; _configuration = configuration; _scanResultService = scanResultService; + _logger = logger; } public Task StartAsync(CancellationToken cancellationToken) { _bus.Receive(_configuration.GetValue("ResultsSubscriptionId"), async message => { + _logger.LogInformation( + $"Received a result from {message.Backend} for {message.Id} " + + $"with threats {string.Join(",", message.Threats)}"); + await _scanResultService.UpdateScanResultForBackend( message.Id, message.Backend, true, true, message.Threats); }); + + _logger.LogInformation( + "Started hosted service for receiving scan results"); return Task.CompletedTask; } @@ -35,6 +46,9 @@ namespace MalwareMultiScan.Api.Services { _bus.Dispose(); + _logger.LogInformation( + "Stopped hosted service for receiving scan results"); + return Task.CompletedTask; } } diff --git a/MalwareMultiScan.Api/Services/ScanBackendService.cs b/MalwareMultiScan.Api/Services/ScanBackendService.cs index 81bc744..d8926b9 100644 --- a/MalwareMultiScan.Api/Services/ScanBackendService.cs +++ b/MalwareMultiScan.Api/Services/ScanBackendService.cs @@ -4,8 +4,9 @@ using System.Threading.Tasks; using EasyNetQ; using MalwareMultiScan.Api.Data.Configuration; using MalwareMultiScan.Api.Data.Models; -using MalwareMultiScan.Shared.Data.Messages; +using MalwareMultiScan.Backends.Messages; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; @@ -14,11 +15,13 @@ namespace MalwareMultiScan.Api.Services public class ScanBackendService { private readonly IBus _bus; + private readonly ILogger _logger; - public ScanBackendService(IConfiguration configuration, IBus bus) + public ScanBackendService(IConfiguration configuration, IBus bus, ILogger logger) { _bus = bus; - + _logger = logger; + var configurationPath = configuration.GetValue("BackendsConfiguration"); if (!File.Exists(configurationPath)) @@ -34,9 +37,12 @@ namespace MalwareMultiScan.Api.Services } 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, diff --git a/MalwareMultiScan.Api/Services/ScanResultService.cs b/MalwareMultiScan.Api/Services/ScanResultService.cs index 6e85f8c..93b1926 100644 --- a/MalwareMultiScan.Api/Services/ScanResultService.cs +++ b/MalwareMultiScan.Api/Services/ScanResultService.cs @@ -1,13 +1,7 @@ -using System; using System.IO; using System.Linq; -using System.Net.Http; -using System.Threading; using System.Threading.Tasks; -using MalwareMultiScan.Api.Data.Configuration; using MalwareMultiScan.Api.Data.Models; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; using MongoDB.Bson; using MongoDB.Driver; using MongoDB.Driver.GridFS; @@ -17,16 +11,16 @@ namespace MalwareMultiScan.Api.Services public class ScanResultService { private const string CollectionName = "ScanResults"; - - private readonly IMongoCollection _collection; private readonly GridFSBucket _bucket; - + + private readonly IMongoCollection _collection; + private readonly ScanBackendService _scanBackendService; public ScanResultService(IMongoDatabase db, ScanBackendService scanBackendService) { _scanBackendService = scanBackendService; - + _collection = db.GetCollection(CollectionName); _bucket = new GridFSBucket(db); } @@ -40,7 +34,7 @@ namespace MalwareMultiScan.Api.Services }; await _collection.InsertOneAsync(scanResult); - + return scanResult; } @@ -51,12 +45,12 @@ namespace MalwareMultiScan.Api.Services return await result.FirstOrDefaultAsync(); } - - public async Task UpdateScanResultForBackend(string resultId, string backendId, + + public async Task UpdateScanResultForBackend(string resultId, string backendId, bool completed = false, bool succeeded = false, string[] threats = null) { var filterScanResult = Builders.Filter.Where(r => r.Id == resultId); - + var updateScanResult = Builders.Update.Set(r => r.Results[backendId], new ScanResultEntry { Completed = completed, @@ -85,7 +79,7 @@ namespace MalwareMultiScan.Api.Services { if (!ObjectId.TryParse(id, out var objectId)) return null; - + return await _bucket.OpenDownloadStreamAsync(objectId, new GridFSDownloadOptions { Seekable = true diff --git a/MalwareMultiScan.Api/Startup.cs b/MalwareMultiScan.Api/Startup.cs index 3685a83..6fbc9ec 100644 --- a/MalwareMultiScan.Api/Startup.cs +++ b/MalwareMultiScan.Api/Startup.cs @@ -18,7 +18,7 @@ namespace MalwareMultiScan.Api { _configuration = configuration; } - + public void ConfigureServices(IServiceCollection services) { services.AddSingleton(x => @@ -26,7 +26,7 @@ namespace MalwareMultiScan.Api services.AddSingleton(); services.AddSingleton(); - + services.AddSingleton( serviceProvider => { @@ -35,7 +35,7 @@ namespace MalwareMultiScan.Api return db.GetDatabase( _configuration.GetValue("DatabaseName")); }); - + services.AddControllers(); services.AddHttpClient(); @@ -48,14 +48,14 @@ namespace MalwareMultiScan.Api Version = "1.0.0" }); }); - + services.AddHostedService(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseRouting(); - + var forwardingOptions = new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto @@ -67,13 +67,13 @@ namespace MalwareMultiScan.Api app.UseForwardedHeaders(forwardingOptions); app.UseEndpoints(endpoints => endpoints.MapControllers()); - + app.UseSwagger(); app.UseSwaggerUI(options => { options.DocumentTitle = "MalwareMultiScan"; - + options.SwaggerEndpoint( $"/swagger/{options.DocumentTitle}/swagger.json", options.DocumentTitle); }); diff --git a/MalwareMultiScan.Backends/Backends/Abstracts/AbstractLocalProcessScanBackend.cs b/MalwareMultiScan.Backends/Backends/Abstracts/AbstractLocalProcessScanBackend.cs index 5e4c344..b00dc5b 100644 --- a/MalwareMultiScan.Backends/Backends/Abstracts/AbstractLocalProcessScanBackend.cs +++ b/MalwareMultiScan.Backends/Backends/Abstracts/AbstractLocalProcessScanBackend.cs @@ -18,15 +18,12 @@ namespace MalwareMultiScan.Backends.Backends.Abstracts _logger = logger; } - protected virtual Regex MatchRegex { get; } - protected virtual string BackendPath { get; } + protected abstract Regex MatchRegex { get; } + protected abstract string BackendPath { get; } protected virtual bool ParseStdErr { get; } protected virtual bool ThrowOnNonZeroExitCode { get; } = true; - protected virtual string GetBackendArguments(string path) - { - throw new NotImplementedException(); - } + protected abstract string GetBackendArguments(string path); public override async Task ScanAsync(string path, CancellationToken cancellationToken) { diff --git a/MalwareMultiScan.Backends/Backends/Abstracts/AbstractScanBackend.cs b/MalwareMultiScan.Backends/Backends/Abstracts/AbstractScanBackend.cs index 49633b2..03aa90a 100644 --- a/MalwareMultiScan.Backends/Backends/Abstracts/AbstractScanBackend.cs +++ b/MalwareMultiScan.Backends/Backends/Abstracts/AbstractScanBackend.cs @@ -3,23 +3,21 @@ using System.IO; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MalwareMultiScan.Shared.Interfaces; +using MalwareMultiScan.Backends.Interfaces; using Microsoft.AspNetCore.Http; namespace MalwareMultiScan.Backends.Backends.Abstracts { public abstract class AbstractScanBackend : IScanBackend { - public virtual string Id => throw new NotImplementedException(); + public abstract string Id { get; } - public virtual Task ScanAsync(string path, CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } + public abstract Task ScanAsync(string path, CancellationToken cancellationToken); public async Task ScanAsync(Uri uri, CancellationToken cancellationToken) { using var httpClient = new HttpClient(); + await using var uriStream = await httpClient.GetStreamAsync(uri); return await ScanAsync(uriStream, cancellationToken); diff --git a/MalwareMultiScan.Backends/Backends/Implementations/ComodoScanBackend.cs b/MalwareMultiScan.Backends/Backends/Implementations/ComodoScanBackend.cs index ac8b3a2..e222d3e 100644 --- a/MalwareMultiScan.Backends/Backends/Implementations/ComodoScanBackend.cs +++ b/MalwareMultiScan.Backends/Backends/Implementations/ComodoScanBackend.cs @@ -1,5 +1,3 @@ -using System; -using System.IO; using System.Text.RegularExpressions; using MalwareMultiScan.Backends.Backends.Abstracts; using Microsoft.Extensions.Logging; diff --git a/MalwareMultiScan.Backends/Backends/Implementations/DummyScanBackend.cs b/MalwareMultiScan.Backends/Backends/Implementations/DummyScanBackend.cs index 42338db..fb70597 100644 --- a/MalwareMultiScan.Backends/Backends/Implementations/DummyScanBackend.cs +++ b/MalwareMultiScan.Backends/Backends/Implementations/DummyScanBackend.cs @@ -2,7 +2,7 @@ using System; using System.IO; using System.Threading; using System.Threading.Tasks; -using MalwareMultiScan.Shared.Interfaces; +using MalwareMultiScan.Backends.Interfaces; using Microsoft.AspNetCore.Http; namespace MalwareMultiScan.Backends.Backends.Implementations diff --git a/MalwareMultiScan.Shared/Data/Enums/BackendType.cs b/MalwareMultiScan.Backends/Enums/BackendType.cs similarity index 78% rename from MalwareMultiScan.Shared/Data/Enums/BackendType.cs rename to MalwareMultiScan.Backends/Enums/BackendType.cs index 7bdad0c..cc935b3 100644 --- a/MalwareMultiScan.Shared/Data/Enums/BackendType.cs +++ b/MalwareMultiScan.Backends/Enums/BackendType.cs @@ -1,4 +1,4 @@ -namespace MalwareMultiScan.Shared.Data.Enums +namespace MalwareMultiScan.Backends.Enums { public enum BackendType { diff --git a/MalwareMultiScan.Scanner/Extensions/ServiceCollectionExtensions.cs b/MalwareMultiScan.Backends/Extensions/ServiceCollectionExtensions.cs similarity index 83% rename from MalwareMultiScan.Scanner/Extensions/ServiceCollectionExtensions.cs rename to MalwareMultiScan.Backends/Extensions/ServiceCollectionExtensions.cs index da1d90b..a7005c8 100644 --- a/MalwareMultiScan.Scanner/Extensions/ServiceCollectionExtensions.cs +++ b/MalwareMultiScan.Backends/Extensions/ServiceCollectionExtensions.cs @@ -1,20 +1,20 @@ using System; using MalwareMultiScan.Backends.Backends.Implementations; -using MalwareMultiScan.Shared.Data.Enums; -using MalwareMultiScan.Shared.Interfaces; +using MalwareMultiScan.Backends.Enums; +using MalwareMultiScan.Backends.Interfaces; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace MalwareMultiScan.Scanner.Extensions +namespace MalwareMultiScan.Backends.Extensions { public static class ServiceCollectionExtensions { public static void AddScanningBackend(this IServiceCollection services, BackendType type) { using var provider = services.BuildServiceProvider(); - - var logger = provider.GetService(); - + + var logger = provider.GetService>(); + services.AddSingleton(type switch { BackendType.Dummy => new DummyScanBackend(), diff --git a/MalwareMultiScan.Shared/Interfaces/IScanBackend.cs b/MalwareMultiScan.Backends/Interfaces/IScanBackend.cs similarity index 92% rename from MalwareMultiScan.Shared/Interfaces/IScanBackend.cs rename to MalwareMultiScan.Backends/Interfaces/IScanBackend.cs index dcf0411..b234ce5 100644 --- a/MalwareMultiScan.Shared/Interfaces/IScanBackend.cs +++ b/MalwareMultiScan.Backends/Interfaces/IScanBackend.cs @@ -4,7 +4,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -namespace MalwareMultiScan.Shared.Interfaces +namespace MalwareMultiScan.Backends.Interfaces { public interface IScanBackend { diff --git a/MalwareMultiScan.Backends/MalwareMultiScan.Backends.csproj b/MalwareMultiScan.Backends/MalwareMultiScan.Backends.csproj index fb7bbed..0ce0c90 100644 --- a/MalwareMultiScan.Backends/MalwareMultiScan.Backends.csproj +++ b/MalwareMultiScan.Backends/MalwareMultiScan.Backends.csproj @@ -8,4 +8,8 @@ + + + + diff --git a/MalwareMultiScan.Shared/Data/Messages/ScanRequestMessage.cs b/MalwareMultiScan.Backends/Messages/ScanRequestMessage.cs similarity index 71% rename from MalwareMultiScan.Shared/Data/Messages/ScanRequestMessage.cs rename to MalwareMultiScan.Backends/Messages/ScanRequestMessage.cs index c5b857e..2ba674c 100644 --- a/MalwareMultiScan.Shared/Data/Messages/ScanRequestMessage.cs +++ b/MalwareMultiScan.Backends/Messages/ScanRequestMessage.cs @@ -1,11 +1,11 @@ using System; -namespace MalwareMultiScan.Shared.Data.Messages +namespace MalwareMultiScan.Backends.Messages { public class ScanRequestMessage { public string Id { get; set; } - + public Uri Uri { get; set; } } } \ No newline at end of file diff --git a/MalwareMultiScan.Shared/Data/Messages/ScanResultMessage.cs b/MalwareMultiScan.Backends/Messages/ScanResultMessage.cs similarity index 78% rename from MalwareMultiScan.Shared/Data/Messages/ScanResultMessage.cs rename to MalwareMultiScan.Backends/Messages/ScanResultMessage.cs index f288000..f6627d7 100644 --- a/MalwareMultiScan.Shared/Data/Messages/ScanResultMessage.cs +++ b/MalwareMultiScan.Backends/Messages/ScanResultMessage.cs @@ -1,4 +1,4 @@ -namespace MalwareMultiScan.Shared.Data.Messages +namespace MalwareMultiScan.Backends.Messages { public class ScanResultMessage { diff --git a/MalwareMultiScan.Worker/Dockerfile b/MalwareMultiScan.Scanner/Dockerfile similarity index 100% rename from MalwareMultiScan.Worker/Dockerfile rename to MalwareMultiScan.Scanner/Dockerfile diff --git a/MalwareMultiScan.Scanner/MalwareMultiScan.Scanner.csproj b/MalwareMultiScan.Scanner/MalwareMultiScan.Scanner.csproj index ef10cce..5930542 100644 --- a/MalwareMultiScan.Scanner/MalwareMultiScan.Scanner.csproj +++ b/MalwareMultiScan.Scanner/MalwareMultiScan.Scanner.csproj @@ -13,4 +13,8 @@ + + + <_ContentIncludedByDefault Remove="Properties\launchSettings.json" /> + diff --git a/MalwareMultiScan.Scanner/Program.cs b/MalwareMultiScan.Scanner/Program.cs index 034996d..eba4512 100644 --- a/MalwareMultiScan.Scanner/Program.cs +++ b/MalwareMultiScan.Scanner/Program.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; -using MalwareMultiScan.Scanner.Extensions; -using MalwareMultiScan.Shared.Data.Enums; +using MalwareMultiScan.Backends.Enums; +using MalwareMultiScan.Backends.Extensions; +using MalwareMultiScan.Scanner.Services; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -14,7 +15,7 @@ namespace MalwareMultiScan.Scanner await Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration(configure => { - configure.AddJsonFile("settings.json"); + configure.AddJsonFile("appsettings.json"); configure.AddEnvironmentVariables(); }) .ConfigureServices((context, services) => diff --git a/MalwareMultiScan.Scanner/Properties/launchSettings.json b/MalwareMultiScan.Scanner/Properties/launchSettings.json deleted file mode 100644 index f97ddfe..0000000 --- a/MalwareMultiScan.Scanner/Properties/launchSettings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "profiles": { - "MalwareMultiScan.Scanner": { - "commandName": "Project", - "environmentVariables": { - "DOTNET_ENVIRONMENT": "Development" - } - } - } -} diff --git a/MalwareMultiScan.Scanner/ScanHostedService.cs b/MalwareMultiScan.Scanner/Services/ScanHostedService.cs similarity index 58% rename from MalwareMultiScan.Scanner/ScanHostedService.cs rename to MalwareMultiScan.Scanner/Services/ScanHostedService.cs index 214ab5b..5c52d0d 100644 --- a/MalwareMultiScan.Scanner/ScanHostedService.cs +++ b/MalwareMultiScan.Scanner/Services/ScanHostedService.cs @@ -2,27 +2,27 @@ using System; using System.Threading; using System.Threading.Tasks; using EasyNetQ; -using MalwareMultiScan.Shared.Data.Messages; -using MalwareMultiScan.Shared.Interfaces; +using MalwareMultiScan.Backends.Interfaces; +using MalwareMultiScan.Backends.Messages; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -namespace MalwareMultiScan.Scanner +namespace MalwareMultiScan.Scanner.Services { - public class ScanHostedService : IHostedService + internal class ScanHostedService : IHostedService { + private readonly IScanBackend _backend; private readonly IConfiguration _configuration; private readonly ILogger _logger; - private readonly IScanBackend _scanBackend; private IBus _bus; - public ScanHostedService(ILogger logger, IConfiguration configuration, IScanBackend scanBackend) + public ScanHostedService(ILogger logger, IConfiguration configuration, IScanBackend backend) { _logger = logger; _configuration = configuration; - _scanBackend = scanBackend; + _backend = backend; } public Task StartAsync(CancellationToken cancellationToken) @@ -30,38 +30,48 @@ namespace MalwareMultiScan.Scanner _bus = RabbitHutch.CreateBus( _configuration.GetConnectionString("RabbitMQ")); - _bus.Receive( - _scanBackend.Id, Scan); + _bus.Receive(_backend.Id, Scan); + + _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; } private async Task Scan(ScanRequestMessage message) { _logger.LogInformation( - $"Starting scan of {message.Uri} via backend {_scanBackend.Id}"); + $"Starting scan of {message.Uri} via backend {_backend.Id} from {message.Id}"); var cancellationTokenSource = new CancellationTokenSource( TimeSpan.FromSeconds(_configuration.GetValue("MaxScanningTime"))); try { - await _bus.SendAsync(_configuration.GetValue("ResultsSubscriptionId"), new ScanResultMessage + var result = new ScanResultMessage { Id = message.Id, - Backend = _scanBackend.Id, + Backend = _backend.Id, - Threats = await _scanBackend.ScanAsync( + Threats = await _backend.ScanAsync( message.Uri, cancellationTokenSource.Token) - }); + }; + + _logger.LogInformation( + $"Backend {_backend.Id} completed a scan of {message.Id} " + + $"with result '{string.Join(", ", result.Threats)}'"); + + await _bus.SendAsync(_configuration.GetValue("ResultsSubscriptionId"), result); } catch (Exception exception) { diff --git a/MalwareMultiScan.Scanner/settings.json b/MalwareMultiScan.Scanner/appsettings.json similarity index 94% rename from MalwareMultiScan.Scanner/settings.json rename to MalwareMultiScan.Scanner/appsettings.json index 18e14f3..3c086e9 100644 --- a/MalwareMultiScan.Scanner/settings.json +++ b/MalwareMultiScan.Scanner/appsettings.json @@ -8,10 +8,8 @@ }, "BackendType": "Dummy", - - "MaxWorkers": 5, - "MaxScanningTime": 60, + "MaxScanningTime": 60, "ResultsSubscriptionId": "mms.results", "ConnectionStrings": { diff --git a/MalwareMultiScan.Shared/Data/Requests/BasicRequest.cs b/MalwareMultiScan.Shared/Data/Requests/BasicRequest.cs deleted file mode 100644 index 0870b99..0000000 --- a/MalwareMultiScan.Shared/Data/Requests/BasicRequest.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using MalwareMultiScan.Shared.Attributes; - -namespace MalwareMultiScan.Shared.Data.Requests -{ - public abstract class BasicRequest - { - [Required] - [UrlValidation] - public Uri CallbackUrl { get; set; } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Shared/Data/Requests/FileRequest.cs b/MalwareMultiScan.Shared/Data/Requests/FileRequest.cs deleted file mode 100644 index 612bd18..0000000 --- a/MalwareMultiScan.Shared/Data/Requests/FileRequest.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Microsoft.AspNetCore.Http; - -namespace MalwareMultiScan.Shared.Data.Requests -{ - public class FileRequest : BasicRequest - { - [Required] - public IFormFile InputFile { get; set; } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Shared/Data/Requests/UrlRequest.cs b/MalwareMultiScan.Shared/Data/Requests/UrlRequest.cs deleted file mode 100644 index e9a0478..0000000 --- a/MalwareMultiScan.Shared/Data/Requests/UrlRequest.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using MalwareMultiScan.Shared.Attributes; - -namespace MalwareMultiScan.Shared.Data.Requests -{ - public class UrlRequest : BasicRequest - { - [Required] - [UrlValidation] - public Uri InputUrl { get; set; } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Shared/Data/Responses/ResultResponse.cs b/MalwareMultiScan.Shared/Data/Responses/ResultResponse.cs deleted file mode 100644 index 0f7542a..0000000 --- a/MalwareMultiScan.Shared/Data/Responses/ResultResponse.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.Linq; - -namespace MalwareMultiScan.Shared.Data.Responses -{ - public class ResultResponse - { - [Required] - public string Backend { get; set; } - - [Required] - public bool Success { get; set; } - - public string[] Threats { get; set; } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Shared/MalwareMultiScan.Shared.csproj b/MalwareMultiScan.Shared/MalwareMultiScan.Shared.csproj deleted file mode 100644 index 5d2c164..0000000 --- a/MalwareMultiScan.Shared/MalwareMultiScan.Shared.csproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - netcoreapp3.1 - - - - - - - - diff --git a/MalwareMultiScan.Worker/Controllers/ScanController.cs b/MalwareMultiScan.Worker/Controllers/ScanController.cs deleted file mode 100644 index 230e440..0000000 --- a/MalwareMultiScan.Worker/Controllers/ScanController.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System.IO; -using System.Threading.Tasks; -using Hangfire; -using MalwareMultiScan.Shared.Data.Requests; -using MalwareMultiScan.Worker.Jobs; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; - -namespace MalwareMultiScan.Worker.Controllers -{ - [ApiController] - [Produces("application/json")] - public class ScanController : ControllerBase - { - - [HttpGet] - [ProducesResponseType(StatusCodes.Status200OK)] - [Route("/ping")] - public IActionResult Ping() - { - return Ok("pong"); - } - - [HttpPost] - [ProducesResponseType(StatusCodes.Status202Accepted)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] - [Route("/scan/file")] - public async Task ScanFile([FromForm] FileRequest request) - { - var temporaryFile = Path.GetTempFileName(); - - await using (var temporaryFileSteam = System.IO.File.OpenWrite(temporaryFile)) - { - await request.InputFile.CopyToAsync(temporaryFileSteam); - } - - BackgroundJob.Enqueue( - x => x.ScanFile(temporaryFile, request.CallbackUrl)); - - return Accepted(request.CallbackUrl); - } - - [HttpPost] - [ProducesResponseType(StatusCodes.Status202Accepted)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] - [Route("/scan/url")] - public IActionResult ScanUrl([FromForm] UrlRequest request) - { - BackgroundJob.Enqueue( - x => x.ScanUrl(request.InputUrl, request.CallbackUrl)); - - return Accepted(request.CallbackUrl); - } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Worker/Jobs/ScanJob.cs b/MalwareMultiScan.Worker/Jobs/ScanJob.cs deleted file mode 100644 index 7dcf178..0000000 --- a/MalwareMultiScan.Worker/Jobs/ScanJob.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System; -using System.IO; -using System.Net.Http; -using System.Text; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; -using Hangfire; -using MalwareMultiScan.Backends.Backends.Implementations; -using MalwareMultiScan.Shared.Data.Enums; -using MalwareMultiScan.Shared.Data.Responses; -using MalwareMultiScan.Shared.Interfaces; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; - -namespace MalwareMultiScan.Worker.Jobs -{ - public class ScanJob - { - private readonly IScanBackend _backend; - private readonly IHttpClientFactory _httpClientFactory; - private readonly ILogger _logger; - private readonly int _scanTimeout; - - public ScanJob(IConfiguration configuration, ILogger logger, - IHttpClientFactory httpClientFactory) - { - _logger = logger; - _httpClientFactory = httpClientFactory; - _scanTimeout = configuration.GetValue("ScanTimeout"); - - _backend = configuration.GetValue("BackendType") switch - { - BackendType.Defender => new WindowsDefenderScanBackend(logger), - BackendType.Clamav => new ClamavScanBackend(logger), - BackendType.DrWeb => new DrWebScanBackend(logger), - BackendType.Kes => new KesScanBackend(logger), - BackendType.Comodo => new ComodoScanBackend(logger), - BackendType.Sophos => new SophosScanBackend(logger), - BackendType.McAfee => new McAfeeScanBackend(logger), - _ => throw new NotImplementedException() - }; - } - - private async Task PostResult(ResultResponse response, Uri callbackUrl, CancellationToken cancellationToken) - { - var serializedResponse = JsonSerializer.Serialize( - response, typeof(ResultResponse), new JsonSerializerOptions - { - WriteIndented = true - }); - - _logger.LogInformation( - $"Sending following payload to {callbackUrl}: {serializedResponse}"); - - using var httpClient = _httpClientFactory.CreateClient(); - - var callbackResponse = await httpClient.PostAsync(callbackUrl, - new StringContent(serializedResponse, Encoding.UTF8, "application/json"), cancellationToken); - - _logger.LogInformation($"Callback URL {callbackUrl} returned a status {callbackResponse.StatusCode}"); - } - - private async Task Scan(Func> scanMethod, Uri callbackUrl) - { - var cancellationTokenSource = new CancellationTokenSource(); - - cancellationTokenSource.CancelAfter(_scanTimeout * 1000); - - var cancellationToken = cancellationTokenSource.Token; - - var response = new ResultResponse - { - Backend = _backend.Id - }; - - try - { - response.Success = true; - response.Threats = await scanMethod(cancellationToken); - } - catch (Exception exception) - { - response.Success = false; - - _logger.LogError(exception, "Scanning failed with exception"); - } - - await PostResult(response, callbackUrl, cancellationToken); - } - - [AutomaticRetry(Attempts = 0, OnAttemptsExceeded = AttemptsExceededAction.Delete)] - public async Task ScanUrl(Uri url, Uri callbackUrl) - { - await Scan(async t => await _backend.ScanAsync(url, t), callbackUrl); - } - - [AutomaticRetry(Attempts = 0, OnAttemptsExceeded = AttemptsExceededAction.Delete)] - public async Task ScanFile(string file, Uri callbackUrl) - { - try - { - await Scan(async t => await _backend.ScanAsync(file, t), callbackUrl); - } - finally - { - File.Delete(file); - } - } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Worker/MalwareMultiScan.Worker.csproj b/MalwareMultiScan.Worker/MalwareMultiScan.Worker.csproj deleted file mode 100644 index ded602c..0000000 --- a/MalwareMultiScan.Worker/MalwareMultiScan.Worker.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - netcoreapp3.1 - - - - - - - - - - - - - - <_ContentIncludedByDefault Remove="Properties\launchSettings.json" /> - - - diff --git a/MalwareMultiScan.Worker/Program.cs b/MalwareMultiScan.Worker/Program.cs deleted file mode 100644 index afcc239..0000000 --- a/MalwareMultiScan.Worker/Program.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Hosting; - -namespace MalwareMultiScan.Worker -{ - public static class Program - { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } - - private static IHostBuilder CreateHostBuilder(string[] args) - { - return Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(builder => { builder.UseStartup(); }); - } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Worker/Startup.cs b/MalwareMultiScan.Worker/Startup.cs deleted file mode 100644 index a4e1b19..0000000 --- a/MalwareMultiScan.Worker/Startup.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Hangfire; -using Hangfire.MemoryStorage; -using MalwareMultiScan.Worker.Jobs; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; - -namespace MalwareMultiScan.Worker -{ - public class Startup - { - public void ConfigureServices(IServiceCollection services) - { - services.AddLogging(); - services.AddControllers(); - services.AddHttpClient(); - - services.AddSingleton(); - - services.AddHangfire( - configuration => configuration.UseMemoryStorage()); - - services.AddHangfireServer(); - } - - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - app.UseRouting(); - - app.UseEndpoints( - endpoints => endpoints.MapControllers()); - - app.UseHangfireServer(); - } - } -} \ No newline at end of file diff --git a/MalwareMultiScan.Worker/appsettings.Development.json b/MalwareMultiScan.Worker/appsettings.Development.json deleted file mode 100644 index 8983e0f..0000000 --- a/MalwareMultiScan.Worker/appsettings.Development.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - } -} diff --git a/MalwareMultiScan.Worker/appsettings.json b/MalwareMultiScan.Worker/appsettings.json deleted file mode 100644 index 1c64e84..0000000 --- a/MalwareMultiScan.Worker/appsettings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Warning", - "MalwareMultiScan": "Debug" - } - }, - - "AllowedHosts": "*", - - "BackendType": "", - "ScanTimeout": 300 -} diff --git a/MalwareMultiScan.sln b/MalwareMultiScan.sln index 5bf071d..20591bc 100644 --- a/MalwareMultiScan.sln +++ b/MalwareMultiScan.sln @@ -1,9 +1,5 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MalwareMultiScan.Worker", "MalwareMultiScan.Worker\MalwareMultiScan.Worker.csproj", "{5D515E0A-B2C6-4C1D-88F6-C296F73409FA}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MalwareMultiScan.Shared", "MalwareMultiScan.Shared\MalwareMultiScan.Shared.csproj", "{9E0A0B50-741F-4A49-97A2-0B337374347F}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MalwareMultiScan.Backends", "MalwareMultiScan.Backends\MalwareMultiScan.Backends.csproj", "{382B49AC-0FFA-44FC-875D-9D4692DDC05D}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MalwareMultiScan.Api", "MalwareMultiScan.Api\MalwareMultiScan.Api.csproj", "{7B63B897-D390-4617-821F-F96799CBA2F4}" @@ -16,14 +12,6 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {5D515E0A-B2C6-4C1D-88F6-C296F73409FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5D515E0A-B2C6-4C1D-88F6-C296F73409FA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5D515E0A-B2C6-4C1D-88F6-C296F73409FA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5D515E0A-B2C6-4C1D-88F6-C296F73409FA}.Release|Any CPU.Build.0 = Release|Any CPU - {9E0A0B50-741F-4A49-97A2-0B337374347F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9E0A0B50-741F-4A49-97A2-0B337374347F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9E0A0B50-741F-4A49-97A2-0B337374347F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9E0A0B50-741F-4A49-97A2-0B337374347F}.Release|Any CPU.Build.0 = Release|Any CPU {382B49AC-0FFA-44FC-875D-9D4692DDC05D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {382B49AC-0FFA-44FC-875D-9D4692DDC05D}.Debug|Any CPU.Build.0 = Debug|Any CPU {382B49AC-0FFA-44FC-875D-9D4692DDC05D}.Release|Any CPU.ActiveCfg = Release|Any CPU