Merge pull request #7 from mindcollapse/ui

Basic UI for the MalwareMultiScan
This commit is contained in:
Volodymyr Smirnov 2020-10-27 20:28:13 +02:00 committed by GitHub
commit 2a933ebe6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 13148 additions and 118 deletions

View File

@ -3,3 +3,7 @@
Dockerfile Dockerfile
bin bin
obj obj
dist
node_modules
.nuxt
MalwareMultiScan.Ui

View File

@ -7,14 +7,6 @@
<option name="command" value="" /> <option name="command" value="" />
<option name="containerName" value="malware-multi-scan-worker-clamav" /> <option name="containerName" value="malware-multi-scan-worker-clamav" />
<option name="entrypoint" value="" /> <option name="entrypoint" value="" />
<option name="portBindings">
<list>
<DockerPortBindingImpl>
<option name="containerPort" value="9901" />
<option name="hostPort" value="9901" />
</DockerPortBindingImpl>
</list>
</option>
<option name="commandLineOptions" value="" /> <option name="commandLineOptions" value="" />
<option name="sourceFilePath" value="MalwareMultiScan.Backends/Dockerfiles/Clamav.Dockerfile" /> <option name="sourceFilePath" value="MalwareMultiScan.Backends/Dockerfiles/Clamav.Dockerfile" />
</settings> </settings>

View File

@ -7,14 +7,6 @@
<option name="command" value="" /> <option name="command" value="" />
<option name="containerName" value="malware-multi-scan-worker-comodo" /> <option name="containerName" value="malware-multi-scan-worker-comodo" />
<option name="entrypoint" value="" /> <option name="entrypoint" value="" />
<option name="portBindings">
<list>
<DockerPortBindingImpl>
<option name="containerPort" value="9901" />
<option name="hostPort" value="9901" />
</DockerPortBindingImpl>
</list>
</option>
<option name="commandLineOptions" value="" /> <option name="commandLineOptions" value="" />
<option name="sourceFilePath" value="MalwareMultiScan.Backends/Dockerfiles/Comodo.Dockerfile" /> <option name="sourceFilePath" value="MalwareMultiScan.Backends/Dockerfiles/Comodo.Dockerfile" />
</settings> </settings>

View File

@ -7,14 +7,6 @@
<option name="command" value="" /> <option name="command" value="" />
<option name="containerName" value="malware-multi-scan-worker-drweb" /> <option name="containerName" value="malware-multi-scan-worker-drweb" />
<option name="entrypoint" value="" /> <option name="entrypoint" value="" />
<option name="portBindings">
<list>
<DockerPortBindingImpl>
<option name="containerPort" value="9901" />
<option name="hostPort" value="9901" />
</DockerPortBindingImpl>
</list>
</option>
<option name="commandLineOptions" value="" /> <option name="commandLineOptions" value="" />
<option name="sourceFilePath" value="MalwareMultiScan.Backends/Dockerfiles/DrWeb.Dockerfile" /> <option name="sourceFilePath" value="MalwareMultiScan.Backends/Dockerfiles/DrWeb.Dockerfile" />
</settings> </settings>

View File

@ -7,14 +7,6 @@
<option name="command" value="" /> <option name="command" value="" />
<option name="containerName" value="malware-multi-scan-worker-kes" /> <option name="containerName" value="malware-multi-scan-worker-kes" />
<option name="entrypoint" value="" /> <option name="entrypoint" value="" />
<option name="portBindings">
<list>
<DockerPortBindingImpl>
<option name="containerPort" value="9901" />
<option name="hostPort" value="9901" />
</DockerPortBindingImpl>
</list>
</option>
<option name="commandLineOptions" value="" /> <option name="commandLineOptions" value="" />
<option name="sourceFilePath" value="MalwareMultiScan.Backends/Dockerfiles/KES.Dockerfile" /> <option name="sourceFilePath" value="MalwareMultiScan.Backends/Dockerfiles/KES.Dockerfile" />
</settings> </settings>

View File

@ -7,14 +7,6 @@
<option name="command" value="" /> <option name="command" value="" />
<option name="containerName" value="malware-multi-scan-worker-mcafee" /> <option name="containerName" value="malware-multi-scan-worker-mcafee" />
<option name="entrypoint" value="" /> <option name="entrypoint" value="" />
<option name="portBindings">
<list>
<DockerPortBindingImpl>
<option name="containerPort" value="9901" />
<option name="hostPort" value="9901" />
</DockerPortBindingImpl>
</list>
</option>
<option name="commandLineOptions" value="" /> <option name="commandLineOptions" value="" />
<option name="sourceFilePath" value="MalwareMultiScan.Backends/Dockerfiles/McAfee.Dockerfile" /> <option name="sourceFilePath" value="MalwareMultiScan.Backends/Dockerfiles/McAfee.Dockerfile" />
</settings> </settings>

View File

@ -7,14 +7,6 @@
<option name="command" value="" /> <option name="command" value="" />
<option name="containerName" value="malware-multi-scan-worker-sophos" /> <option name="containerName" value="malware-multi-scan-worker-sophos" />
<option name="entrypoint" value="" /> <option name="entrypoint" value="" />
<option name="portBindings">
<list>
<DockerPortBindingImpl>
<option name="containerPort" value="9901" />
<option name="hostPort" value="9901" />
</DockerPortBindingImpl>
</list>
</option>
<option name="commandLineOptions" value="" /> <option name="commandLineOptions" value="" />
<option name="sourceFilePath" value="MalwareMultiScan.Backends/Dockerfiles/Sophos.Dockerfile" /> <option name="sourceFilePath" value="MalwareMultiScan.Backends/Dockerfiles/Sophos.Dockerfile" />
</settings> </settings>

View File

@ -7,14 +7,6 @@
<option name="command" value="" /> <option name="command" value="" />
<option name="containerName" value="malware-multi-scan-worker-windows-defender" /> <option name="containerName" value="malware-multi-scan-worker-windows-defender" />
<option name="entrypoint" value="" /> <option name="entrypoint" value="" />
<option name="portBindings">
<list>
<DockerPortBindingImpl>
<option name="containerPort" value="9901" />
<option name="hostPort" value="9901" />
</DockerPortBindingImpl>
</list>
</option>
<option name="commandLineOptions" value="" /> <option name="commandLineOptions" value="" />
<option name="sourceFilePath" value="MalwareMultiScan.Backends/Dockerfiles/WindowsDefender.Dockerfile" /> <option name="sourceFilePath" value="MalwareMultiScan.Backends/Dockerfiles/WindowsDefender.Dockerfile" />
</settings> </settings>

View File

@ -0,0 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Dummy API" type="CompoundRunConfigurationType">
<toRun name="MalwareMultiScan.Scanner" type="DotNetProject" />
<toRun name="MalwareMultiScan.Api" type="DotNetProject" />
<method v="2" />
</configuration>
</component>

View File

@ -2,7 +2,7 @@
<configuration default="false" name="MalwareMultiScan.Scanner/Dockerfile" type="docker-deploy" factoryName="dockerfile" server-name="Docker"> <configuration default="false" name="MalwareMultiScan.Scanner/Dockerfile" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile"> <deployment type="dockerfile">
<settings> <settings>
<option name="imageTag" value="mindcollapse/malware-multi-scan-worker" /> <option name="imageTag" value="mindcollapse/malware-multi-scan-scanner" />
<option name="buildCliOptions" value="" /> <option name="buildCliOptions" value="" />
<option name="buildOnly" value="true" /> <option name="buildOnly" value="true" />
<option name="command" value="" /> <option name="command" value="" />

View File

@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Mvc;
namespace MalwareMultiScan.Api.Controllers namespace MalwareMultiScan.Api.Controllers
{ {
[ApiController] [ApiController]
[Route("download")] [Route("api/download")]
[Produces("application/octet-stream")] [Produces("application/octet-stream")]
public class DownloadController : Controller public class DownloadController : Controller
{ {

View File

@ -9,7 +9,7 @@ using Microsoft.AspNetCore.Mvc;
namespace MalwareMultiScan.Api.Controllers namespace MalwareMultiScan.Api.Controllers
{ {
[ApiController] [ApiController]
[Route("queue")] [Route("api/queue")]
[Produces("application/json")] [Produces("application/json")]
public class QueueController : Controller public class QueueController : Controller
{ {

View File

@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Mvc;
namespace MalwareMultiScan.Api.Controllers namespace MalwareMultiScan.Api.Controllers
{ {
[ApiController] [ApiController]
[Route("results")] [Route("api/results")]
[Produces("application/json")] [Produces("application/json")]
public class ScanResultsController : Controller public class ScanResultsController : Controller
{ {

View File

@ -0,0 +1,19 @@
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS builder
WORKDIR /src
COPY MalwareMultiScan.Api /src/MalwareMultiScan.Api
COPY MalwareMultiScan.Backends /src/MalwareMultiScan.Backends
RUN dotnet publish -c Release -r linux-x64 -o ./publish MalwareMultiScan.Api/MalwareMultiScan.Api.csproj
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1
WORKDIR /worker
COPY --from=builder /src/publish /worker
ENV ASPNETCORE_URLS=http://+:5000
ENV ASPNETCORE_ENVIRONMENT=Production
ENTRYPOINT ["/worker/MalwareMultiScan.Api"]

View File

@ -39,13 +39,15 @@ namespace MalwareMultiScan.Api.Services
{ {
_bus.Receive<ScanResultMessage>(_configuration.GetValue<string>("ResultsSubscriptionId"), async message => _bus.Receive<ScanResultMessage>(_configuration.GetValue<string>("ResultsSubscriptionId"), async message =>
{ {
message.Threats ??= new string[] { };
_logger.LogInformation( _logger.LogInformation(
$"Received a result from {message.Backend} for {message.Id} " + $"Received a result from {message.Backend} for {message.Id} " +
$"with threats {string.Join(",", message.Threats)}"); $"with threats {string.Join(",", message.Threats)}");
await _scanResultService.UpdateScanResultForBackend( await _scanResultService.UpdateScanResultForBackend(
message.Id, message.Backend, true, message.Id, message.Backend, true,
message.Succeeded, message.Threats ?? new string[] { }); message.Succeeded, message.Threats);
}); });
_logger.LogInformation( _logger.LogInformation(

View File

@ -1,6 +1,7 @@
using MalwareMultiScan.Api.Extensions; using MalwareMultiScan.Api.Extensions;
using MalwareMultiScan.Api.Services; using MalwareMultiScan.Api.Services;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -19,6 +20,11 @@ namespace MalwareMultiScan.Api
{ {
services.AddDockerForwardedHeadersOptions(); services.AddDockerForwardedHeadersOptions();
services.Configure<KestrelServerOptions>(options =>
{
options.Limits.MaxRequestBodySize = _configuration.GetValue<int>("MaxFileSize");
});
services.AddMongoDb(_configuration); services.AddMongoDb(_configuration);
services.AddRabbitMq(_configuration); services.AddRabbitMq(_configuration);

View File

@ -6,6 +6,7 @@
"Microsoft.Hosting.Lifetime": "Information" "Microsoft.Hosting.Lifetime": "Information"
} }
}, },
"AllowedHosts": "*", "AllowedHosts": "*",
"ConnectionStrings": { "ConnectionStrings": {
@ -15,6 +16,6 @@
"DatabaseName": "MalwareMultiScan", "DatabaseName": "MalwareMultiScan",
"ResultsSubscriptionId": "mms.results", "ResultsSubscriptionId": "mms.results",
"MaxFileSize": 1048576, "MaxFileSize": 52428800,
"BackendsConfiguration": "backends.yaml" "BackendsConfiguration": "backends.yaml"
} }

View File

@ -1,2 +1,23 @@
- id: dummy - id: dummy
enabled: true 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

View File

@ -12,7 +12,7 @@ namespace MalwareMultiScan.Backends.Backends.Implementations
public override string Id { get; } = "clamav"; public override string Id { get; } = "clamav";
protected override string BackendPath { get; } = "/usr/bin/clamscan"; protected override string BackendPath { get; } = "/usr/bin/clamdscan";
protected override Regex MatchRegex { get; } = protected override Regex MatchRegex { get; } =
new Regex(@"(\S+): (?<threat>[\S]+) FOUND", RegexOptions.Compiled | RegexOptions.Multiline); new Regex(@"(\S+): (?<threat>[\S]+) FOUND", RegexOptions.Compiled | RegexOptions.Multiline);
@ -21,7 +21,7 @@ namespace MalwareMultiScan.Backends.Backends.Implementations
protected override string GetBackendArguments(string path) protected override string GetBackendArguments(string path)
{ {
return $"--no-summary {path}"; return $"-m --fdpass --no-summary {path}";
} }
} }
} }

View File

@ -25,9 +25,12 @@ namespace MalwareMultiScan.Backends.Backends.Implementations
return Scan(); return Scan();
} }
private static Task<string[]> Scan() private static async Task<string[]> Scan()
{ {
return Task.FromResult(new[] {"Malware.Dummy.Result"}); await Task.Delay(
TimeSpan.FromSeconds(5));
return new[] {"Malware.Dummy.Result"};
} }
} }
} }

View File

@ -10,7 +10,7 @@ namespace MalwareMultiScan.Backends.Backends.Implementations
{ {
} }
public override string Id { get; } = "mcafeee"; public override string Id { get; } = "mcafee";
protected override string BackendPath { get; } = "/usr/local/uvscan/uvscan"; protected override string BackendPath { get; } = "/usr/local/uvscan/uvscan";

View File

@ -2,9 +2,9 @@ FROM mindcollapse/malware-multi-scan-scanner:latest
ENV DEBIAN_FRONTEND noninteractive ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update && apt-get install -y clamav RUN apt-get update && apt-get install -y clamav clamav-daemon
RUN freshclam --quiet RUN freshclam --quiet
ENV MULTI_SCAN_BACKEND_BIN=/usr/bin/clamscan
ENV BackendType=Clamav ENV BackendType=Clamav
ENTRYPOINT /etc/init.d/clamav-daemon start && /worker/MalwareMultiScan.Scanner

View File

@ -25,4 +25,4 @@ RUN /opt/drweb.com/bin/drweb-configd -d -p /var/run/drweb-configd.pid && \
ENV BackendType=DrWeb ENV BackendType=DrWeb
ENTRYPOINT /opt/drweb.com/bin/drweb-configd -d -p /var/run/drweb-configd.pid && /worker/MalwareMultiScan.Worker ENTRYPOINT /opt/drweb.com/bin/drweb-configd -d -p /var/run/drweb-configd.pid && /worker/MalwareMultiScan.Scanner

View File

@ -32,4 +32,4 @@ exit $? \
ENV BackendType=Kes ENV BackendType=Kes
ENTRYPOINT /etc/init.d/kesl-supervisor start && /worker/MalwareMultiScan.Worker ENTRYPOINT /etc/init.d/kesl-supervisor start && /worker/MalwareMultiScan.Scanner

View File

@ -2,12 +2,12 @@ FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS builder
WORKDIR /src WORKDIR /src
COPY MalwareMultiScan.Worker /src/MalwareMultiScan.Scanner COPY MalwareMultiScan.Scanner /src/MalwareMultiScan.Scanner
COPY MalwareMultiScan.Backends /src/MalwareMultiScan.Backends COPY MalwareMultiScan.Backends /src/MalwareMultiScan.Backends
RUN dotnet publish -c Release -r linux-x64 -o ./publish MalwareMultiScan.Scanner/MalwareMultiScan.Scanner.csproj RUN dotnet publish -c Release -r linux-x64 -o ./publish MalwareMultiScan.Scanner/MalwareMultiScan.Scanner.csproj
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 FROM mcr.microsoft.com/dotnet/core/runtime:3.1
WORKDIR /worker WORKDIR /worker

View File

@ -80,11 +80,12 @@ namespace MalwareMultiScan.Scanner.Services
_logger.LogError( _logger.LogError(
exception, "Scanning failed with exception"); exception, "Scanning failed with exception");
} }
finally
{ _logger.LogInformation(
$"Sending scan results with status {result.Succeeded}");
await _bus.SendAsync( await _bus.SendAsync(
_configuration.GetValue<string>("ResultsSubscriptionId"), result); _configuration.GetValue<string>("ResultsSubscriptionId"), result);
} }
} }
} }
}

View File

@ -0,0 +1,90 @@
# Created by .ignore support plugin (hsz.mobi)
### Node template
# Logs
/logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# Nuxt generate
dist
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless
# IDE / Editor
.idea
# Service worker
sw.*
# macOS
.DS_Store
# Vim swap files
*.swp

90
MalwareMultiScan.Ui/.gitignore vendored Normal file
View File

@ -0,0 +1,90 @@
# Created by .ignore support plugin (hsz.mobi)
### Node template
# Logs
/logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# Nuxt generate
dist
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless
# IDE / Editor
.idea
# Service worker
sw.*
# macOS
.DS_Store
# Vim swap files
*.swp

View File

@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="npm run dev" type="js.build_tools.npm">
<package-json value="$PROJECT_DIR$/package.json" />
<command value="run" />
<scripts>
<script value="dev" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
</component>

View File

@ -0,0 +1,11 @@
FROM node:lts
WORKDIR /app
COPY package.json /app/package.json
RUN npm install
COPY . /app
RUN npm run build
ENTRYPOINT ["npm", "run", "start"]

View File

@ -0,0 +1,26 @@
<template>
<div class="application">
<b-navbar toggleable="md" type="dark" variant="primary">
<b-navbar-brand to="/">
<b-icon-bug-fill /> MalwareMultiScan
</b-navbar-brand>
<b-navbar-toggle target="nav-collapse" />
<b-collapse id="nav-collapse" is-nav>
<b-navbar-nav class="ml-auto">
<b-nav-form>
<b-button href="https://github.com/mindcollapse/MalwareMultiScan" variant="dark" size="sm" target="_blank">
<b-icon-code /> GitHub
</b-button>
</b-nav-form>
</b-navbar-nav>
</b-collapse>
</b-navbar>
<b-container :fluid="true" class="mt-3">
<nuxt />
</b-container>
</div>
</template>

View File

@ -0,0 +1,15 @@
import ScanResultEntry from '~/models/scan-result-entry';
export default class ScanResultEntryFlattened implements ScanResultEntry {
readonly id: string;
readonly completed: boolean;
readonly succeeded: boolean | null;
readonly threats: string[];
constructor(id: string, completed: boolean, succeeded: boolean | null, threats: string[]) {
this.id = id;
this.completed = completed;
this.succeeded = succeeded;
this.threats = threats;
}
}

View File

@ -0,0 +1,5 @@
export default interface ScanResultEntry {
readonly completed: boolean,
readonly succeeded: boolean | null,
readonly threats: string[]
}

View File

@ -0,0 +1,6 @@
import ScanResultEntry from "~/models/scan-result-entry";
export default interface ScanResult {
readonly id: string,
readonly results: { [id: string]: ScanResultEntry; }
}

View File

@ -0,0 +1,37 @@
export default {
ssr: false,
head: {
title: 'MalwareMultiScan UI',
meta: [
{charset: 'utf-8'},
{name: 'viewport', content: 'width=device-width, initial-scale=1, shrink-to-fit=no'},
]
},
buildModules: [
'@nuxt/typescript-build',
],
modules: [
'bootstrap-vue/nuxt',
'@nuxtjs/axios',
'@nuxtjs/proxy'
],
axios: {
proxy: true,
prefix: '/api/'
},
proxy: {
'/api': {
target: process.env.API_URL || 'http://localhost:5000',
changeOrigin: true
}
},
bootstrapVue: {
icons: true
}
}

12451
MalwareMultiScan.Ui/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,26 @@
{
"name": "malware-multi-scan-ui",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "nuxt-ts",
"build": "nuxt-ts build",
"start": "nuxt-ts start",
"generate": "nuxt-ts generate"
},
"dependencies": {
"@nuxt/typescript-runtime": "^2.0.0",
"@nuxtjs/axios": "^5.12.2",
"@nuxtjs/proxy": "^2.0.1",
"bootstrap": "^4.5.2",
"bootstrap-vue": "^2.17.3",
"core-js": "^3.6.5",
"nuxt": "^2.14.6"
},
"devDependencies": {
"@nuxt/types": "^2.14.6",
"@nuxt/typescript-build": "^2.0.3",
"node-sass": "^4.14.1",
"sass-loader": "^10.0.4"
}
}

View File

@ -0,0 +1,73 @@
<template>
<b-table :fields="fields" :items="flattenedData" class="m-0" striped>
<template #cell(completed)="data">
<div class="h5 m-0">
<b-icon-check-circle-fill v-if="data.item.succeeded === true" variant="success"/>
<b-icon-exclamation-circle-fill v-if="data.item.succeeded === false" variant="danger"/>
<b-icon-bug-fill v-if="!data.item.completed" animation="spin-pulse"
class="rounded-circle bg-primary text-white" scale="0.7"/>
</div>
</template>
<template #cell(threats)="data">
<div class="text-success" v-if="data.item.succeeded && !data.item.threats.length">No threats have been detected</div>
<div class="text-danger" v-if="data.item.succeeded === false">Scan failed to complete due to the error or timeout</div>
<div v-if="!data.item.completed">Scan is in progress</div>
<ul v-if="data.item.completed && data.item.threats.length" class="list-inline m-0">
<li class="text-danger" v-for="threat in data.item.threats" v-bind:key="threat">{{ threat }}</li>
</ul>
</template>
</b-table>
</template>
<script lang="ts">
import Vue from 'vue';
import ScanResult from '~/models/scan-result';
import ScanResultEntryFlattened from '~/models/scan-result-entry-flattened';
export default Vue.extend({
async asyncData({params, $axios}) {
return {data: await $axios.$get<ScanResult>(`results/${params.id}`)}
},
created() {
this.timer = setInterval(this.getData, 1000 * 5);
},
beforeDestroy() {
clearInterval(this.timer);
},
methods: {
async getData() {
this.data = await this.$axios.$get<ScanResult>(`results/${this.$route.params.id}`);
}
},
computed: {
flattenedData(): ScanResultEntryFlattened[] {
return Object
.entries((this.data as ScanResult).results)
.map(([k, v]) => new ScanResultEntryFlattened(k, v.completed, v.succeeded, v.threats))
}
},
data() {
return {
data: {} as ScanResult,
fields: [
{key: 'id', label: 'Backend'},
{key: 'completed', label: 'Completed'},
{key: 'threats', label: 'Threats'},
],
timer: 0 as any // otherwise it conflicts with SSR / Type Checking
}
}
});
</script>

View File

@ -0,0 +1,89 @@
<template>
<b-card no-body>
<b-tabs card no-fade pills>
<b-tab active title="File Scan">
<b-input-group>
<b-form-file v-model="file" placeholder="Select or drop files here"/>
<b-input-group-append>
<b-button :disabled="!file || uploading" variant="primary" @click="scanFile">Scan</b-button>
</b-input-group-append>
</b-input-group>
<b-progress v-if="uploading" :value="progress" animated class="mt-3"/>
</b-tab>
<b-tab title="URL Scan">
<b-input-group>
<b-input v-model="url" placeholder="https://secure.eicar.org/eicar.com.txt" type="url"/>
<b-input-group-append>
<b-button :disabled="!isUrl(url)" variant="primary" @click="scanUrl">Scan</b-button>
</b-input-group-append>
</b-input-group>
</b-tab>
</b-tabs>
</b-card>
</template>
<script lang="ts">
import Vue from 'vue';
import ScanResult from '~/models/scan-result';
export default Vue.extend({
data() {
return {
uploading: false,
progress: 0,
file: null as File | null,
url: '' as string,
}
},
methods: {
isUrl(url: string): boolean {
return /(http|https):\/\/(\w+:?\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@\-\/]))?/.test(url);
},
async scanFile() {
if (!this.file || this.uploading)
return
const data = new FormData();
data.append('file', this.file, this.file.name);
try {
this.uploading = true;
const result = await this.$axios.$post<ScanResult>('queue/file', data, {
onUploadProgress: (progressEvent) => {
if (progressEvent.total == 0)
return;
this.progress = progressEvent.loaded / progressEvent.total * 100;
}
});
await this.$router.push({name: 'id', params: {id: result.id}});
} finally {
this.progress = 0;
this.uploading = false;
}
},
async scanUrl() {
if (!this.isUrl(this.url))
return;
const data = new FormData();
data.append('url', this.url);
const result = await this.$axios.$post<ScanResult>('queue/url', data);
await this.$router.push({name: 'id', params: {id: result.id}});
}
}
});
</script>

View File

@ -0,0 +1,37 @@
{
"compilerOptions": {
"target": "ES2018",
"module": "ESNext",
"moduleResolution": "Node",
"lib": [
"ESNext",
"ESNext.AsyncIterable",
"DOM"
],
"esModuleInterop": true,
"allowJs": true,
"sourceMap": true,
"strict": true,
"noEmit": true,
"experimentalDecorators": true,
"baseUrl": ".",
"paths": {
"~/*": [
"./*"
],
"@/*": [
"./*"
]
},
"types": [
"@types/node",
"@nuxt/types",
"@nuxtjs/axios"
]
},
"exclude": [
"node_modules",
".nuxt",
"dist"
]
}

View File

@ -6,6 +6,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MalwareMultiScan.Api", "Mal
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MalwareMultiScan.Scanner", "MalwareMultiScan.Scanner\MalwareMultiScan.Scanner.csproj", "{8A16A3C4-2AE3-4F63-8280-635FF7878080}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MalwareMultiScan.Scanner", "MalwareMultiScan.Scanner\MalwareMultiScan.Scanner.csproj", "{8A16A3C4-2AE3-4F63-8280-635FF7878080}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Misc", "Misc", "{A248B5B7-7CBB-4242-98BD-51A9E915E485}"
ProjectSection(SolutionItems) = preProject
.dockerignore = .dockerignore
.gitignore = .gitignore
docker-compose.yaml = docker-compose.yaml
EndProjectSection
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU

View File

@ -1,47 +1,94 @@
version: "3" version: "3.8"
services: services:
clamav-backend: rabbitmq:
image: ${DOCKER_IMAGE_PREFIX-mindcollapse}/malware-multi-scan-worker-clamav image: rabbitmq:3
restart: on-failure
expose:
- "5672"
volumes:
- rabbitmq_etc/:/etc/rabbitmq/
- rabbitmq_data:/var/lib/rabbitmq/
- rabbitmq_logs/:/var/log/rabbitmq/
mongodb:
image: mongo:4
restart: on-failure
expose:
- "27019"
volumes:
- mongodb:/data
ui:
image: mindcollapse/malware-multi-scan-ui
restart: on-failure
ports:
- "8888:8888"
expose:
- "8888"
depends_on:
- api
environment:
- "API_URL=http://api:5000"
- "NUXT_HOST=0.0.0.0"
- "NUXT_PORT=8888"
build:
context: MalwareMultiScan.Ui
dockerfile: Dockerfile
api:
image: mindcollapse/malware-multi-scan-api
restart: on-failure
expose:
- "5000"
depends_on:
- rabbitmq
- mongodb
environment:
- "ConnectionStrings__RabbitMQ=host=rabbitmq;timeout=120"
- "ConnectionStrings__Mongo=mongodb://mongodb:27017?connectTimeoutMS=120000"
- "BackendsConfiguration=/etc/backends.yaml"
volumes:
- "./MalwareMultiScan.Api/backends.yaml:/etc/backends.yaml:ro"
build:
context: .
dockerfile: MalwareMultiScan.Api/Dockerfile
dummy-scanner:
image: mindcollapse/malware-multi-scan-scanner
restart: on-failure
depends_on:
- rabbitmq
environment:
- "ConnectionStrings__RabbitMQ=host=rabbitmq;prefetchcount=1;timeout=120"
build:
context: .
dockerfile: MalwareMultiScan.Scanner/Dockerfile
clamav-scanner:
image: mindcollapse/malware-multi-scan-scanner-clamav
restart: on-failure
depends_on:
- dummy-scanner
environment:
- "ConnectionStrings__RabbitMQ=host=rabbitmq;prefetchcount=1;timeout=120"
build: build:
context: MalwareMultiScan.Backends/Dockerfiles context: MalwareMultiScan.Backends/Dockerfiles
dockerfile: Clamav.Dockerfile dockerfile: Clamav.Dockerfile
windows-defender-backend: windows-defender-scanner:
image: ${DOCKER_IMAGE_PREFIX-mindcollapse}/malware-multi-scan-worker-windows-defender image: mindcollapse/malware-multi-scan-scanner-windows-defender
restart: on-failure
depends_on:
- dummy-scanner
environment:
- "ConnectionStrings__RabbitMQ=host=rabbitmq;prefetchcount=1;timeout=120"
build: build:
context: MalwareMultiScan.Backends/Dockerfiles context: MalwareMultiScan.Backends/Dockerfiles
dockerfile: WindowsDefender.Dockerfile dockerfile: WindowsDefender.Dockerfile
# SCAN BACKENDS BELOW DISABLE DUE TO THE VENDOR LICENSING POLICY volumes:
# RE-ENABLE ONLY AFTER CHECKING EULA POLICY & BUYING THE LICENSE mongodb:
# rabbitmq_etc:
# comodo-backend: rabbitmq_data:
# image: ${DOCKER_IMAGE_PREFIX-mindcollapse}/malware-multi-scan-worker-comodo rabbitmq_logs:
# build:
# context: MalwareMultiScan.Backends/Dockerfiles
# dockerfile: Comodo.Dockerfile
#
# drweb-backend:
# image: ${DOCKER_IMAGE_PREFIX-mindcollapse}/malware-multi-scan-worker-drweb
# build:
# context: MalwareMultiScan.Backends/Dockerfiles
# dockerfile: DrWeb.Dockerfile
#
# kes-backend:
# image: ${DOCKER_IMAGE_PREFIX-mindcollapse}/malware-multi-scan-worker-kes
# build:
# context: MalwareMultiScan.Backends/Dockerfiles
# dockerfile: KES.Dockerfile
#
# mcafee-backend:
# image: ${DOCKER_IMAGE_PREFIX-mindcollapse}/malware-multi-scan-worker-mcafee
# build:
# context: MalwareMultiScan.Backends/Dockerfiles
# dockerfile: McAfee.Dockerfile
#
# sophos-backend:
# image: ${DOCKER_IMAGE_PREFIX-mindcollapse}/malware-multi-scan-worker-sophos
# build:
# context: MalwareMultiScan.Backends/Dockerfiles
# dockerfile: Sophos.Dockerfile