diff --git a/backend/config/default.cjs b/backend/config/default.cjs index 1b5cd00b5..6468af545 100644 --- a/backend/config/default.cjs +++ b/backend/config/default.cjs @@ -190,10 +190,6 @@ module.exports = { text: '', startTimestamp: '', }, - - avScanning: { - enabled: false, - }, }, connectors: { @@ -208,6 +204,10 @@ module.exports = { audit: { kind: 'silly', }, + + fileScanners: { + kinds: [], + }, }, s3: { diff --git a/backend/config/docker_compose.cjs b/backend/config/docker_compose.cjs index 6edd987bf..d113374d1 100644 --- a/backend/config/docker_compose.cjs +++ b/backend/config/docker_compose.cjs @@ -67,9 +67,9 @@ module.exports = { }, }, - ui: { - avScanning: { - enabled: false, + connectors: { + fileScanners: { + kinds: ['clamAV'], }, }, } diff --git a/backend/src/connectors/fileScanning/Base.ts b/backend/src/connectors/fileScanning/Base.ts new file mode 100644 index 000000000..2ea4bbd77 --- /dev/null +++ b/backend/src/connectors/fileScanning/Base.ts @@ -0,0 +1,12 @@ +import { FileInterface, ScanStateKeys } from '../../models/File.js' +export interface FileScanResult { + toolName: string + state: ScanStateKeys + isInfected?: boolean + viruses?: string[] +} + +export abstract class BaseFileScanningConnector { + abstract info(): string[] + abstract scan(file: FileInterface): Promise +} diff --git a/backend/src/connectors/fileScanning/clamAv.ts b/backend/src/connectors/fileScanning/clamAv.ts new file mode 100644 index 000000000..0173384d3 --- /dev/null +++ b/backend/src/connectors/fileScanning/clamAv.ts @@ -0,0 +1,67 @@ +import NodeClam from 'clamscan' +import { Readable } from 'stream' + +import { getObjectStream } from '../../clients/s3.js' +import { FileInterfaceDoc, ScanState } from '../../models/File.js' +import log from '../../services/log.js' +import config from '../../utils/config.js' +import { ConfigurationError } from '../../utils/error.js' +import { BaseFileScanningConnector, FileScanResult } from './Base.js' + +let av: NodeClam +export const clamAvToolName = 'Clam AV' + +export class ClamAvFileScanningConnector extends BaseFileScanningConnector { + constructor() { + super() + } + + info() { + return [clamAvToolName] + } + + async init() { + try { + av = await new NodeClam().init({ clamdscan: config.avScanning.clamdscan }) + } catch (error) { + throw ConfigurationError('Could not scan file as Clam AV is not running.', { + clamAvConfig: config.avScanning, + }) + } + } + + async scan(file: FileInterfaceDoc): Promise { + if (!av) { + throw ConfigurationError( + 'Clam AV does not look like it is running. Check that it has been correctly initialised by calling the init function.', + { + clamAvConfig: config.avScanning, + }, + ) + } + const s3Stream = (await getObjectStream(file.bucket, file.path)).Body as Readable + try { + const { isInfected, viruses } = await av.scanStream(s3Stream) + log.info( + { modelId: file.modelId, fileId: file._id, name: file.name, result: { isInfected, viruses } }, + 'Scan complete.', + ) + return [ + { + toolName: clamAvToolName, + state: ScanState.Complete, + isInfected, + viruses, + }, + ] + } catch (error) { + log.error({ error, modelId: file.modelId, fileId: file._id, name: file.name }, 'Scan errored.') + return [ + { + toolName: clamAvToolName, + state: ScanState.Error, + }, + ] + } + } +} diff --git a/backend/src/connectors/fileScanning/index.ts b/backend/src/connectors/fileScanning/index.ts new file mode 100644 index 000000000..d005c4c38 --- /dev/null +++ b/backend/src/connectors/fileScanning/index.ts @@ -0,0 +1,39 @@ +import config from '../../utils/config.js' +import { ConfigurationError } from '../../utils/error.js' +import { BaseFileScanningConnector } from './Base.js' +import { ClamAvFileScanningConnector } from './clamAv.js' +import { FileScanningWrapper } from './wrapper.js' + +export const FileScanKind = { + ClamAv: 'clamAV', +} as const +export type FileScanKindKeys = (typeof FileScanKind)[keyof typeof FileScanKind] + +const fileScanConnectors: BaseFileScanningConnector[] = [] +let scannerWrapper: undefined | BaseFileScanningConnector = undefined +export function runFileScanners(cache = true) { + if (scannerWrapper && cache) { + return scannerWrapper + } + config.connectors.fileScanners.kinds.forEach(async (fileScanner) => { + switch (fileScanner) { + case FileScanKind.ClamAv: + try { + const scanner = new ClamAvFileScanningConnector() + await scanner.init() + fileScanConnectors.push(scanner) + } catch (error) { + throw ConfigurationError('Could not configure or initialise Clam AV') + } + break + default: + throw ConfigurationError(`'${fileScanner}' is not a valid file scanning kind.`, { + validKinds: Object.values(FileScanKind), + }) + } + }) + scannerWrapper = new FileScanningWrapper(fileScanConnectors) + return scannerWrapper +} + +export default runFileScanners() diff --git a/backend/src/connectors/fileScanning/wrapper.ts b/backend/src/connectors/fileScanning/wrapper.ts new file mode 100644 index 000000000..d85bd1642 --- /dev/null +++ b/backend/src/connectors/fileScanning/wrapper.ts @@ -0,0 +1,34 @@ +import { FileInterface } from '../../models/File.js' +import log from '../../services/log.js' +import { BaseFileScanningConnector, FileScanResult } from './Base.js' + +export class FileScanningWrapper extends BaseFileScanningConnector { + scanners: BaseFileScanningConnector[] = [] + + constructor(scanners: BaseFileScanningConnector[]) { + super() + this.scanners = scanners + } + + info() { + const scannerNames: string[] = [] + for (const scanner of this.scanners) { + scannerNames.push(...scanner.info()) + } + return scannerNames + } + + async scan(file: FileInterface) { + const results: FileScanResult[] = [] + for (const scanner of this.scanners) { + log.info( + { modelId: file.modelId, fileId: file._id, name: file.name, toolName: scanner.info().pop() }, + 'Scan started.', + ) + const scannerResults = await scanner.scan(file) + results.push(...scannerResults) + } + + return results + } +} diff --git a/backend/src/models/File.ts b/backend/src/models/File.ts index 5100accc9..b4c9dc276 100644 --- a/backend/src/models/File.ts +++ b/backend/src/models/File.ts @@ -1,6 +1,8 @@ import { Document, model, ObjectId, Schema } from 'mongoose' import MongooseDelete from 'mongoose-delete' +import { FileScanResult } from '../connectors/fileScanning/Base.js' + // This interface stores information about the properties on the base object. // It should be used for plain object representations, e.g. for sending to the // client. @@ -17,11 +19,7 @@ export interface FileInterface { complete: boolean - avScan: { - state: ScanStateKeys - isInfected?: boolean - viruses?: Array - } + avScan: Array createdAt: Date updatedAt: Date @@ -51,11 +49,14 @@ const FileSchema = new Schema( bucket: { type: String, required: true }, path: { type: String, required: true }, - avScan: { - state: { type: String, enum: Object.values(ScanState), default: 'notScanned' }, - isInfected: { type: Boolean }, - viruses: [{ type: String }], - }, + avScan: [ + { + toolName: { type: String }, + state: { type: String, enum: Object.values(ScanState) }, + isInfected: { type: Boolean }, + viruses: [{ type: String }], + }, + ], complete: { type: Boolean, default: false }, }, diff --git a/backend/src/routes.ts b/backend/src/routes.ts index ded625ca7..19c831b7a 100644 --- a/backend/src/routes.ts +++ b/backend/src/routes.ts @@ -10,6 +10,7 @@ import { getDockerRegistryAuth } from './routes/v1/registryAuth.js' import { getCurrentUser } from './routes/v2/entities/getCurrentUser.js' import { getEntities } from './routes/v2/entities/getEntities.js' import { getEntityLookup } from './routes/v2/entities/getEntityLookup.js' +import { getFilescanningInfo } from './routes/v2/filescanning/getFilescanningInfo.js' import { deleteAccessRequest } from './routes/v2/model/accessRequest/deleteAccessRequest.js' import { getAccessRequest } from './routes/v2/model/accessRequest/getAccessRequest.js' import { getAccessRequestCurrentUserPermissions } from './routes/v2/model/accessRequest/getAccessRequestCurrentUserPermissions.js' @@ -217,6 +218,8 @@ server.delete('/api/v2/user/token/:accessKey', ...deleteUserToken) server.get('/api/v2/specification', ...getSpecification) +server.get('/api/v2/filescanning/info', ...getFilescanningInfo) + // Python docs const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) diff --git a/backend/src/routes/v2/filescanning/getFilescanningInfo.ts b/backend/src/routes/v2/filescanning/getFilescanningInfo.ts new file mode 100644 index 000000000..87aadec45 --- /dev/null +++ b/backend/src/routes/v2/filescanning/getFilescanningInfo.ts @@ -0,0 +1,15 @@ +import bodyParser from 'body-parser' +import { Request, Response } from 'express' + +import scanners from '../../../connectors/fileScanning/index.js' + +interface GetFileScanningInfoResponse { + scanners: string[] +} + +export const getFilescanningInfo = [ + bodyParser.json(), + async (req: Request, res: Response) => { + return res.json({ scanners: scanners.info() }) + }, +] diff --git a/backend/src/services/file.ts b/backend/src/services/file.ts index 24e8405d4..cfdd9c83f 100644 --- a/backend/src/services/file.ts +++ b/backend/src/services/file.ts @@ -1,20 +1,19 @@ -import NodeClam from 'clamscan' +import { Schema } from 'mongoose' import { Readable } from 'stream' import { getObjectStream, putObjectStream } from '../clients/s3.js' import { FileAction } from '../connectors/authorisation/actions.js' import authorisation from '../connectors/authorisation/index.js' +import { FileScanResult } from '../connectors/fileScanning/Base.js' +import scanners from '../connectors/fileScanning/index.js' import FileModel, { ScanState } from '../models/File.js' import { UserInterface } from '../models/User.js' import config from '../utils/config.js' import { BadReq, Forbidden, NotFound } from '../utils/error.js' import { longId } from '../utils/id.js' -import log from './log.js' import { getModelById } from './model.js' import { removeFileFromReleases } from './release.js' -let av: NodeClam - export async function uploadFile(user: UserInterface, modelId: string, name: string, mime: string, stream: Readable) { const model = await getModelById(user, modelId) if (model.settings.mirror.sourceModelId) { @@ -38,43 +37,37 @@ export async function uploadFile(user: UserInterface, modelId: string, name: str await file.save() - if (config.ui.avScanning.enabled) { - if (!av) { - try { - av = await new NodeClam().init({ clamdscan: config.avScanning.clamdscan }) - } catch (error) { - log.error(error, 'Unable to connect to ClamAV.') - return file - } - } - const avStream = av.passthrough() - const s3Stream = (await getObjectStream(file.bucket, file.path)).Body as Readable - s3Stream.pipe(avStream) - log.info({ modelId, fileId: file._id, name }, 'Scan started.') - - avStream.on('scan-complete', async (result) => { - log.info({ result, modelId, fileId: file._id, name }, 'Scan complete.') - await file.update({ - avScan: { state: ScanState.Complete, isInfected: result.isInfected, viruses: result.viruses }, - }) - }) - avStream.on('error', async (error) => { - log.error({ error, modelId, fileId: file._id, name }, 'Scan errored.') - await file.update({ - avScan: { state: ScanState.Error }, - }) - }) - avStream.on('timeout', async (error) => { - log.error({ error, modelId, fileId: file._id, name }, 'Scan timed out.') - await file.update({ - avScan: { state: ScanState.Error }, - }) - }) + if (scanners.info()) { + const resultsInprogress = scanners.info().map((scannerName) => ({ + toolName: scannerName, + state: ScanState.InProgress, + })) + await updateFileWithResults(file._id, resultsInprogress) + scanners.scan(file).then((resultsArray) => updateFileWithResults(file._id, resultsArray)) } return file } +async function updateFileWithResults(_id: Schema.Types.ObjectId, results: FileScanResult[]) { + for (const result of results) { + const updateExistingResult = await FileModel.updateOne( + { _id, 'avScan.toolName': result.toolName }, + { + $set: { 'avScan.$': result }, + }, + ) + if (updateExistingResult.modifiedCount === 0) { + await FileModel.updateOne( + { _id }, + { + $set: { avScan: { toolName: result.toolName, state: result.state } }, + }, + ) + } + } +} + export async function downloadFile(user: UserInterface, fileId: string, range?: { start: number; end: number }) { const file = await getFileById(user, fileId) const model = await getModelById(user, file.modelId) diff --git a/backend/src/services/mirroredModel.ts b/backend/src/services/mirroredModel.ts index 613526ab3..c590faff6 100644 --- a/backend/src/services/mirroredModel.ts +++ b/backend/src/services/mirroredModel.ts @@ -10,6 +10,7 @@ import { sign } from '../clients/kms.js' import { getObjectStream, putObjectStream } from '../clients/s3.js' import { ModelAction } from '../connectors/authorisation/actions.js' import authorisation from '../connectors/authorisation/index.js' +import scanners from '../connectors/fileScanning/index.js' import { FileInterfaceDoc, ScanState } from '../models/File.js' import { ModelDoc } from '../models/Model.js' import { ModelCardRevisionInterface } from '../models/ModelCardRevision.js' @@ -354,7 +355,7 @@ async function checkReleaseFiles(user: UserInterface, modelId: string, semvers: } } - if (config.ui.avScanning.enabled) { + if (scanners.info()) { const files: FileInterfaceDoc[] = await getFilesByIds(user, modelId, fileIds) const scanErrors: { missingScan: Array<{ name: string; id: string }> @@ -364,9 +365,9 @@ async function checkReleaseFiles(user: UserInterface, modelId: string, semvers: for (const file of files) { if (!file.avScan) { scanErrors.missingScan.push({ name: file.name, id: file.id }) - } else if (file.avScan.state !== ScanState.Complete) { + } else if (file.avScan.some((scanResult) => scanResult.state !== ScanState.Complete)) { scanErrors.incompleteScan.push({ name: file.name, id: file.id }) - } else if (file.avScan.isInfected) { + } else if (file.avScan.some((scanResult) => scanResult.isInfected)) { scanErrors.failedScan.push({ name: file.name, id: file.id }) } } diff --git a/backend/src/types/types.ts b/backend/src/types/types.ts index 744726906..053e4bfc8 100644 --- a/backend/src/types/types.ts +++ b/backend/src/types/types.ts @@ -70,8 +70,4 @@ export interface UiConfig { text: string startTimestamp: string } - - avScanning: { - enabled: boolean - } } diff --git a/backend/src/utils/__mocks__/config.ts b/backend/src/utils/__mocks__/config.ts index c26361d60..1d8daf572 100644 --- a/backend/src/utils/__mocks__/config.ts +++ b/backend/src/utils/__mocks__/config.ts @@ -20,6 +20,9 @@ const config = { authorisation: { kind: 'basic', }, + fileScanners: { + kinds: [], + }, }, log: { level: 'debug', @@ -32,8 +35,10 @@ const config = { instrumentation: { enabled: false, }, - avScanning: { - enabled: false, + ui: { + inference: { + enabled: true, + }, }, } diff --git a/backend/src/utils/config.ts b/backend/src/utils/config.ts index d9f365e01..1501c69a2 100644 --- a/backend/src/utils/config.ts +++ b/backend/src/utils/config.ts @@ -5,6 +5,7 @@ import grant from 'grant' import { AuditKindKeys } from '../connectors/audit/index.js' import { AuthenticationKindKeys } from '../connectors/authentication/index.js' import { AuthorisationKindKeys } from '../connectors/authorisation/index.js' +import { FileScanKindKeys } from '../connectors/fileScanning/index.js' import { DefaultSchema } from '../services/schema.js' import { deepFreeze } from './object.js' @@ -36,6 +37,10 @@ export interface Config { audit: { kind: AuditKindKeys } + + fileScanners: { + kinds: FileScanKindKeys[] + } } smtp: { @@ -135,10 +140,6 @@ export interface Config { text: string startTimestamp: string } - - avScanning: { - enabled: boolean - } } session: { diff --git a/backend/test/connectors/authorisation/authorisation.spec.ts b/backend/test/connectors/authorisation/authorisation.spec.ts index 184607ec8..3c492ebf3 100644 --- a/backend/test/connectors/authorisation/authorisation.spec.ts +++ b/backend/test/connectors/authorisation/authorisation.spec.ts @@ -1,9 +1,10 @@ import { describe, expect, test, vi } from 'vitest' import { getAuthorisationConnector } from '../../../src/connectors/authorisation/index.js' -import config from '../../../src/utils/__mocks__/config.js' +import { FileScanResult } from '../../../src/connectors/fileScanning/Base.js' +// import config from '../../../src/utils/__mocks__/config.js' -vi.mock('../../../src/utils/config.js') +// vi.mock('../../../src/utils/config.js') vi.mock('../../../src/connectors/authentication/index.js', () => ({ default: { @@ -11,6 +12,50 @@ vi.mock('../../../src/connectors/authentication/index.js', () => ({ }, })) +const fileScanResult: FileScanResult = { + state: 'complete', + isInfected: false, + toolName: 'Test', +} + +const fileScanningMock = vi.hoisted(() => ({ + info: vi.fn(() => []), + scan: vi.fn(() => new Promise(() => [fileScanResult])), +})) +vi.mock('../../src/connectors/fileScanning/index.js', async () => ({ default: fileScanningMock })) + +const configMock = vi.hoisted(() => ({ + app: { + protocol: 'http', + }, + instrumentation: { + enabled: false, + }, + log: { + level: 'info', + }, + connectors: { + authorisation: { + kind: 'basic', + }, + audit: { + kind: 'silly', + }, + fileScanners: { + kinds: [], + }, + }, + registry: { + connection: { + internal: 'https://localhost:5000', + }, + }, +})) +vi.mock('../../../src/utils/config.js', () => ({ + __esModule: true, + default: configMock, +})) + describe('connectors > authorisation', () => { test('basic', () => { const connector = getAuthorisationConnector(false) @@ -19,7 +64,7 @@ describe('connectors > authorisation', () => { test('invalid', () => { const invalidConnector = 'invalid' - config.connectors.authorisation.kind = invalidConnector + configMock.connectors.authorisation.kind = invalidConnector expect(() => getAuthorisationConnector(false)).toThrowError( `'${invalidConnector}' is not a valid authorisation kind.`, diff --git a/backend/test/routes/entities/getEntities.spec.ts b/backend/test/routes/entities/getEntities.spec.ts index 80d13e75e..040bf567d 100644 --- a/backend/test/routes/entities/getEntities.spec.ts +++ b/backend/test/routes/entities/getEntities.spec.ts @@ -3,7 +3,6 @@ import { describe, expect, test, vi } from 'vitest' import { testGet } from '../../testUtils/routes.js' vi.mock('../../../src/utils/user.js') -vi.mock('../../../src/utils/config.js') const authenticationMocks = vi.hoisted(() => ({ authenticationMiddleware: vi.fn(() => []), diff --git a/backend/test/routes/entities/getEntityLookup.spec.ts b/backend/test/routes/entities/getEntityLookup.spec.ts index 34a5639ae..b99bd7d08 100644 --- a/backend/test/routes/entities/getEntityLookup.spec.ts +++ b/backend/test/routes/entities/getEntityLookup.spec.ts @@ -2,9 +2,7 @@ import { describe, expect, test, vi } from 'vitest' import { testGet } from '../../testUtils/routes.js' -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/utils/user.js') -vi.mock('../../../src/utils/config.js') const authenticationMocks = vi.hoisted(() => ({ authenticationMiddleware: vi.fn(() => []), diff --git a/backend/test/routes/middleware/expressErrorHandler.spec.ts b/backend/test/routes/middleware/expressErrorHandler.spec.ts index ff4ebfcf3..dc21529d1 100644 --- a/backend/test/routes/middleware/expressErrorHandler.spec.ts +++ b/backend/test/routes/middleware/expressErrorHandler.spec.ts @@ -3,7 +3,6 @@ import { describe, expect, test, vi } from 'vitest' import audit from '../../../src/connectors/audit/__mocks__/index.js' import { bailoErrorGuard, expressErrorHandler } from '../../../src/routes/middleware/expressErrorHandler.js' -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/connectors/audit/index.js') describe('middleware > expressErrorHandler', () => { diff --git a/backend/test/routes/model/accessRequest/__snapshots__/patchAccessRequestComment.spec.ts.snap b/backend/test/routes/model/accessRequest/__snapshots__/patchAccessRequestComment.spec.ts.snap deleted file mode 100644 index 2c8faf897..000000000 --- a/backend/test/routes/model/accessRequest/__snapshots__/patchAccessRequestComment.spec.ts.snap +++ /dev/null @@ -1,15 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`routes > release > patchReleaseComment > 200 > ok 1`] = ` -{ - "accessRequest": { - "message": "test", - }, -} -`; - -exports[`routes > release > patchReleaseComment > audit > expected call 1`] = ` -{ - "message": "test", -} -`; diff --git a/backend/test/routes/model/accessRequest/deleteAccessRequest.spec.ts b/backend/test/routes/model/accessRequest/deleteAccessRequest.spec.ts index 2fb81f41f..4e27ebb6e 100644 --- a/backend/test/routes/model/accessRequest/deleteAccessRequest.spec.ts +++ b/backend/test/routes/model/accessRequest/deleteAccessRequest.spec.ts @@ -5,7 +5,6 @@ import { deleteAccessRequestSchema } from '../../../../src/routes/v2/model/acces import { createFixture, testDelete } from '../../../testUtils/routes.js' vi.mock('../../../../src/utils/user.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/connectors/audit/index.js') vi.mock('../../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/model/accessRequest/getAccessRequest.spec.ts b/backend/test/routes/model/accessRequest/getAccessRequest.spec.ts index 6950fbd8c..3c000674a 100644 --- a/backend/test/routes/model/accessRequest/getAccessRequest.spec.ts +++ b/backend/test/routes/model/accessRequest/getAccessRequest.spec.ts @@ -7,7 +7,6 @@ import { createFixture, testGet } from '../../../testUtils/routes.js' import { testAccessRequest } from '../../../testUtils/testModels.js' vi.mock('../../../../src/utils/user.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/connectors/audit/index.js') vi.mock('../../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/model/accessRequest/getAccessRequestCurrentUserPermissions.spec.ts b/backend/test/routes/model/accessRequest/getAccessRequestCurrentUserPermissions.spec.ts index f8e8cd352..83fe636c3 100644 --- a/backend/test/routes/model/accessRequest/getAccessRequestCurrentUserPermissions.spec.ts +++ b/backend/test/routes/model/accessRequest/getAccessRequestCurrentUserPermissions.spec.ts @@ -4,7 +4,6 @@ import { getAccessRequestCurrentUserPermissionsSchema } from '../../../../src/ro import { createFixture, testGet } from '../../../testUtils/routes.js' vi.mock('../../../../src/utils/user.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/connectors/audit/index.js') vi.mock('../../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/model/accessRequest/getModelAccessRequests.spec.ts b/backend/test/routes/model/accessRequest/getModelAccessRequests.spec.ts index 0253edd14..1a8d93f7a 100644 --- a/backend/test/routes/model/accessRequest/getModelAccessRequests.spec.ts +++ b/backend/test/routes/model/accessRequest/getModelAccessRequests.spec.ts @@ -4,7 +4,6 @@ import audit from '../../../../src/connectors/audit/__mocks__/index.js' import { getModelAccessRequestsSchema } from '../../../../src/routes/v2/model/accessRequest/getModelAccessRequests.js' import { createFixture, testGet } from '../../../testUtils/routes.js' -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/utils/user.js') vi.mock('../../../../src/connectors/audit/index.js') vi.mock('../../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/model/accessRequest/patchAccessRequest.spec.ts b/backend/test/routes/model/accessRequest/patchAccessRequest.spec.ts index 28b1f5aa8..4c8aa1a81 100644 --- a/backend/test/routes/model/accessRequest/patchAccessRequest.spec.ts +++ b/backend/test/routes/model/accessRequest/patchAccessRequest.spec.ts @@ -5,7 +5,6 @@ import { patchAccessRequestSchema } from '../../../../src/routes/v2/model/access import { createFixture, testPatch } from '../../../testUtils/routes.js' vi.mock('../../../../src/utils/user.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/connectors/audit/index.js') vi.mock('../../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/model/accessRequest/postAccessRequest.spec.ts b/backend/test/routes/model/accessRequest/postAccessRequest.spec.ts index 234228ccb..c0bf1c810 100644 --- a/backend/test/routes/model/accessRequest/postAccessRequest.spec.ts +++ b/backend/test/routes/model/accessRequest/postAccessRequest.spec.ts @@ -1,10 +1,10 @@ import { describe, expect, test, vi } from 'vitest' import audit from '../../../../src/connectors/audit/__mocks__/index.js' +import { FileScanResult } from '../../../../src/connectors/fileScanning/Base.js' import { postAccessRequestSchema } from '../../../../src/routes/v2/model/accessRequest/postAccessRequest.js' import { createFixture, testPost } from '../../../testUtils/routes.js' -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/utils/user.js') vi.mock('../../../../src/connectors/audit/index.js') vi.mock('../../../../src/connectors/authorisation/index.js') @@ -13,6 +13,19 @@ vi.mock('../../../../src/services/accessRequest.js', async () => ({ createAccessRequest: vi.fn(() => ({ _id: 'test' })), })) +const fileScanResult: FileScanResult = { + state: 'complete', + isInfected: false, + toolName: 'Test', +} + +const fileScanningMock = vi.hoisted(() => ({ + info: vi.fn(() => []), + scan: vi.fn(() => new Promise(() => [fileScanResult])), + init: vi.fn(() => {}), +})) +vi.mock('../../src/connectors/fileScanning/index.js', async () => ({ default: fileScanningMock })) + describe('routes > accessRequest > postAccessRequest', () => { test('200 > ok', async () => { const fixture = createFixture(postAccessRequestSchema) diff --git a/backend/test/routes/model/accessRequest/postAccessRequestComment.spec.ts b/backend/test/routes/model/accessRequest/postAccessRequestComment.spec.ts index eb65e3e39..50b16e4c4 100644 --- a/backend/test/routes/model/accessRequest/postAccessRequestComment.spec.ts +++ b/backend/test/routes/model/accessRequest/postAccessRequestComment.spec.ts @@ -4,7 +4,6 @@ import audit from '../../../../src/connectors/audit/__mocks__/index.js' import { postAccessRequestCommentSchema } from '../../../../src/routes/v2/model/accessRequest/postAccessRequestComment.js' import { createFixture, testPost } from '../../../testUtils/routes.js' -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/utils/user.js') vi.mock('../../../../src/connectors/audit/index.js') vi.mock('../../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/model/file/deleteFile.spec.ts b/backend/test/routes/model/file/deleteFile.spec.ts index 7f1224d07..bb1680b64 100644 --- a/backend/test/routes/model/file/deleteFile.spec.ts +++ b/backend/test/routes/model/file/deleteFile.spec.ts @@ -4,9 +4,7 @@ import audit from '../../../../src/connectors/audit/__mocks__/index.js' import { deleteFileSchema } from '../../../../src/routes/v2/model/file/deleteFile.js' import { createFixture, testDelete } from '../../../testUtils/routes.js' -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/utils/user.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/connectors/audit/index.js') vi.mock('../../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/model/file/getFiles.spec.ts b/backend/test/routes/model/file/getFiles.spec.ts index ecdbef4c4..ba64887e7 100644 --- a/backend/test/routes/model/file/getFiles.spec.ts +++ b/backend/test/routes/model/file/getFiles.spec.ts @@ -4,9 +4,7 @@ import audit from '../../../../src/connectors/audit/__mocks__/index.js' import { getFilesSchema } from '../../../../src/routes/v2/model/file/getFiles.js' import { createFixture, testGet } from '../../../testUtils/routes.js' -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/utils/user.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/connectors/audit/index.js') vi.mock('../../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/model/file/postSimpleUpload.spec.ts b/backend/test/routes/model/file/postSimpleUpload.spec.ts index 35e7823e4..0c8b18e1e 100644 --- a/backend/test/routes/model/file/postSimpleUpload.spec.ts +++ b/backend/test/routes/model/file/postSimpleUpload.spec.ts @@ -5,9 +5,7 @@ import audit from '../../../../src/connectors/audit/__mocks__/index.js' import { postSimpleUploadSchema } from '../../../../src/routes/v2/model/file/postSimpleUpload.js' import { createFixture, testPost } from '../../../testUtils/routes.js' -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/utils/user.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/connectors/audit/index.js') vi.mock('../../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/model/getModel.spec.ts b/backend/test/routes/model/getModel.spec.ts index 881a8ef53..26007ce70 100644 --- a/backend/test/routes/model/getModel.spec.ts +++ b/backend/test/routes/model/getModel.spec.ts @@ -4,9 +4,7 @@ import audit from '../../../src/connectors/audit/__mocks__/index.js' import { getModelSchema } from '../../../src/routes/v2/model/getModel.js' import { createFixture, testGet } from '../../testUtils/routes.js' -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/utils/user.js') -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/connectors/audit/index.js') describe('routes > model > getModel', () => { diff --git a/backend/test/routes/model/getModelCurrentUserPermissions.spec.ts b/backend/test/routes/model/getModelCurrentUserPermissions.spec.ts index d69263cea..ec34e402e 100644 --- a/backend/test/routes/model/getModelCurrentUserPermissions.spec.ts +++ b/backend/test/routes/model/getModelCurrentUserPermissions.spec.ts @@ -3,9 +3,7 @@ import { describe, expect, test, vi } from 'vitest' import { getModelCurrentUserPermissionsSchema } from '../../../src/routes/v2/model/getModelCurrentUserPermissions.js' import { createFixture, testGet } from '../../testUtils/routes.js' -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/utils/user.js') -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/connectors/audit/index.js') describe('routes > model > getModelCurrentUserPermissions', () => { diff --git a/backend/test/routes/model/getModelsSearch.spec.ts b/backend/test/routes/model/getModelsSearch.spec.ts index db52bcaa0..b770903a8 100644 --- a/backend/test/routes/model/getModelsSearch.spec.ts +++ b/backend/test/routes/model/getModelsSearch.spec.ts @@ -5,9 +5,7 @@ import audit from '../../../src/connectors/audit/__mocks__/index.js' import { getModelsSearchSchema } from '../../../src/routes/v2/model/getModelsSearch.js' import { createFixture, testGet } from '../../testUtils/routes.js' -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/utils/user.js') -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/connectors/audit/index.js') vi.mock('../../../src/services/model.js', () => ({ diff --git a/backend/test/routes/model/images/getImages.spec.ts b/backend/test/routes/model/images/getImages.spec.ts index d94362378..5124b944a 100644 --- a/backend/test/routes/model/images/getImages.spec.ts +++ b/backend/test/routes/model/images/getImages.spec.ts @@ -4,9 +4,7 @@ import audit from '../../../../src/connectors/audit/__mocks__/index.js' import { getImagesSchema } from '../../../../src/routes/v2/model/images/getImages.js' import { createFixture, testGet } from '../../../testUtils/routes.js' -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/utils/user.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/connectors/audit/index.js') vi.mock('../../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/model/inferencing/createInferencingService.spec.ts b/backend/test/routes/model/inferencing/createInferencingService.spec.ts index b5bd2b7ad..6b69bc814 100644 --- a/backend/test/routes/model/inferencing/createInferencingService.spec.ts +++ b/backend/test/routes/model/inferencing/createInferencingService.spec.ts @@ -17,7 +17,6 @@ describe('routes > inferencing > postInference', () => { test('200 > ok', async () => { const fixture = createFixture(postInferenceSchema) const res = await testPost(`/api/v2/model/${fixture.params.modelId}/inference`, fixture) - expect(res.statusCode).toBe(200) expect(res.body).matchSnapshot() }) diff --git a/backend/test/routes/model/inferencing/getInferencingService.spec.ts b/backend/test/routes/model/inferencing/getInferencingService.spec.ts index d0e265754..dcc68af18 100644 --- a/backend/test/routes/model/inferencing/getInferencingService.spec.ts +++ b/backend/test/routes/model/inferencing/getInferencingService.spec.ts @@ -4,8 +4,8 @@ import audit from '../../../../src/connectors/audit/__mocks__/index.js' import { getInferenceSchema } from '../../../../src/routes/v2/model/inferencing/getInferenceService.js' import { createFixture, testGet } from '../../../testUtils/routes.js' -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/utils/user.js') +vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/connectors/audit/index.js') vi.mock('../../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/model/modelcard/getModelCard.spec.ts b/backend/test/routes/model/modelcard/getModelCard.spec.ts index 69c619056..99121db5d 100644 --- a/backend/test/routes/model/modelcard/getModelCard.spec.ts +++ b/backend/test/routes/model/modelcard/getModelCard.spec.ts @@ -4,9 +4,7 @@ import audit from '../../../../src/connectors/audit/__mocks__/index.js' import { getModelCardSchema } from '../../../../src/routes/v2/model/modelcard/getModelCard.js' import { createFixture, testGet } from '../../../testUtils/routes.js' -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/utils/user.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/connectors/audit/index.js') const modelMock = vi.hoisted(() => { diff --git a/backend/test/routes/model/modelcard/getModelCardHtml.spec.ts b/backend/test/routes/model/modelcard/getModelCardHtml.spec.ts index 9988635a0..628d1c07d 100644 --- a/backend/test/routes/model/modelcard/getModelCardHtml.spec.ts +++ b/backend/test/routes/model/modelcard/getModelCardHtml.spec.ts @@ -6,9 +6,7 @@ import { getModelCardHtmlSchema } from '../../../../src/routes/v2/model/modelcar import { renderToHtml } from '../../../../src/services/modelCardExport.js' import { createFixture, testGet } from '../../../testUtils/routes.js' -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/utils/user.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/connectors/audit/index.js') const mockModelCardExportService = vi.hoisted(() => { diff --git a/backend/test/routes/model/modelcard/getModelCardRevisions.spec.ts b/backend/test/routes/model/modelcard/getModelCardRevisions.spec.ts index f1108487b..689ff1d2d 100644 --- a/backend/test/routes/model/modelcard/getModelCardRevisions.spec.ts +++ b/backend/test/routes/model/modelcard/getModelCardRevisions.spec.ts @@ -5,9 +5,7 @@ import { getModelCardRevisionsSchema } from '../../../../src/routes/v2/model/mod import { createFixture, testGet } from '../../../testUtils/routes.js' import { testModelCardRevision } from '../../../testUtils/testModels.js' -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/utils/user.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/connectors/audit/index.js') const mockModelService = vi.hoisted(() => { diff --git a/backend/test/routes/model/modelcard/postFromSchema.spec.ts b/backend/test/routes/model/modelcard/postFromSchema.spec.ts index fb39c30b4..b1b4c531e 100644 --- a/backend/test/routes/model/modelcard/postFromSchema.spec.ts +++ b/backend/test/routes/model/modelcard/postFromSchema.spec.ts @@ -5,9 +5,7 @@ import { postFromSchemaSchema } from '../../../../src/routes/v2/model/modelcard/ import { createFixture, testPost } from '../../../testUtils/routes.js' vi.mock('../../../../src/connectors/authorisation/index.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/utils/user.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/connectors/audit/index.js') vi.mock('../../../../src/services/model.js', async () => { diff --git a/backend/test/routes/model/modelcard/postFromTemplate.spec.ts b/backend/test/routes/model/modelcard/postFromTemplate.spec.ts index b4e51d91a..3e98d83b3 100644 --- a/backend/test/routes/model/modelcard/postFromTemplate.spec.ts +++ b/backend/test/routes/model/modelcard/postFromTemplate.spec.ts @@ -5,9 +5,7 @@ import { postFromTemplateSchema } from '../../../../src/routes/v2/model/modelcar import { createFixture, testPost } from '../../../testUtils/routes.js' vi.mock('../../../../src/connectors/authorisation/index.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/utils/user.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/connectors/audit/index.js') vi.mock('../../../../src/services/model.js', async () => { diff --git a/backend/test/routes/model/modelcard/putModelCard.spec.ts b/backend/test/routes/model/modelcard/putModelCard.spec.ts index c5cc6c569..b8e2cfd84 100644 --- a/backend/test/routes/model/modelcard/putModelCard.spec.ts +++ b/backend/test/routes/model/modelcard/putModelCard.spec.ts @@ -5,9 +5,7 @@ import { putModelCardSchema } from '../../../../src/routes/v2/model/modelcard/pu import { createFixture, testPut } from '../../../testUtils/routes.js' vi.mock('../../../../src/connectors/authorisation/index.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/utils/user.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/connectors/audit/index.js') vi.mock('../../../../src/services/model.js', async () => { diff --git a/backend/test/routes/model/patchModel.spec.ts b/backend/test/routes/model/patchModel.spec.ts index 533e58638..75b5f8960 100644 --- a/backend/test/routes/model/patchModel.spec.ts +++ b/backend/test/routes/model/patchModel.spec.ts @@ -4,9 +4,7 @@ import audit from '../../../src/connectors/audit/__mocks__/index.js' import { patchModelSchema } from '../../../src/routes/v2/model/patchModel.js' import { createFixture, testPatch } from '../../testUtils/routes.js' -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/utils/user.js') -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/connectors/audit/index.js') describe('routes > model > patchModel', () => { diff --git a/backend/test/routes/model/postModel.spec.ts b/backend/test/routes/model/postModel.spec.ts index c0bb63836..71904c221 100644 --- a/backend/test/routes/model/postModel.spec.ts +++ b/backend/test/routes/model/postModel.spec.ts @@ -4,9 +4,7 @@ import audit from '../../../src/connectors/audit/__mocks__/index.js' import { postModelSchema } from '../../../src/routes/v2/model/postModel.js' import { createFixture, testPost } from '../../testUtils/routes.js' -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/utils/user.js') -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/connectors/audit/index.js') describe('routes > model > postModel', () => { diff --git a/backend/test/routes/model/postRequestExport.spec.ts b/backend/test/routes/model/postRequestExport.spec.ts index e25bb6d1e..5ff5a7acd 100644 --- a/backend/test/routes/model/postRequestExport.spec.ts +++ b/backend/test/routes/model/postRequestExport.spec.ts @@ -4,9 +4,7 @@ import audit from '../../../src/connectors/audit/__mocks__/index.js' import { postRequestExportToS3Schema } from '../../../src/routes/v2/model/postRequestExport.js' import { createFixture, testPost } from '../../testUtils/routes.js' -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/utils/user.js') -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/connectors/audit/index.js') describe('routes > model > postModel', () => { diff --git a/backend/test/routes/model/postRequestImport.spec.ts b/backend/test/routes/model/postRequestImport.spec.ts index 02be87632..83c5e71bb 100644 --- a/backend/test/routes/model/postRequestImport.spec.ts +++ b/backend/test/routes/model/postRequestImport.spec.ts @@ -4,9 +4,7 @@ import audit from '../../../src/connectors/audit/__mocks__/index.js' import { postRequestImportFromS3Schema } from '../../../src/routes/v2/model/postRequestImport.js' import { createFixture, testPost } from '../../testUtils/routes.js' -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/utils/user.js') -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/connectors/audit/index.js') describe('routes > model > postRequestImport', () => { diff --git a/backend/test/routes/model/release/deleteRelease.spec.ts b/backend/test/routes/model/release/deleteRelease.spec.ts index 85da8fc43..eb888c5b4 100644 --- a/backend/test/routes/model/release/deleteRelease.spec.ts +++ b/backend/test/routes/model/release/deleteRelease.spec.ts @@ -4,9 +4,7 @@ import audit from '../../../../src/connectors/audit/__mocks__/index.js' import { deleteReleaseSchema } from '../../../../src/routes/v2/release/deleteRelease.js' import { createFixture, testDelete } from '../../../testUtils/routes.js' -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/utils/user.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/connectors/audit/index.js') vi.mock('../../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/model/release/getRelease.spec.ts b/backend/test/routes/model/release/getRelease.spec.ts index 44951614c..93249d36e 100644 --- a/backend/test/routes/model/release/getRelease.spec.ts +++ b/backend/test/routes/model/release/getRelease.spec.ts @@ -4,9 +4,7 @@ import audit from '../../../../src/connectors/audit/__mocks__/index.js' import { getReleaseSchema } from '../../../../src/routes/v2/release/getRelease.js' import { createFixture, testGet } from '../../../testUtils/routes.js' -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/utils/user.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/connectors/audit/index.js') vi.mock('../../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/model/release/getReleases.spec.ts b/backend/test/routes/model/release/getReleases.spec.ts index 1a83ca99c..b859a8582 100644 --- a/backend/test/routes/model/release/getReleases.spec.ts +++ b/backend/test/routes/model/release/getReleases.spec.ts @@ -4,9 +4,7 @@ import audit from '../../../../src/connectors/audit/__mocks__/index.js' import { getReleasesSchema } from '../../../../src/routes/v2/release/getReleases.js' import { createFixture, testGet } from '../../../testUtils/routes.js' -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/utils/user.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/connectors/audit/index.js') vi.mock('../../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/model/release/postRelease.spec.ts b/backend/test/routes/model/release/postRelease.spec.ts index 28dae9343..10b8c76f5 100644 --- a/backend/test/routes/model/release/postRelease.spec.ts +++ b/backend/test/routes/model/release/postRelease.spec.ts @@ -4,9 +4,7 @@ import audit from '../../../../src/connectors/audit/__mocks__/index.js' import { postReleaseSchema } from '../../../../src/routes/v2/release/postRelease.js' import { createFixture, testPost } from '../../../testUtils/routes.js' -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/utils/user.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/connectors/audit/index.js') vi.mock('../../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/model/release/postReleaseComment.spec.ts b/backend/test/routes/model/release/postReleaseComment.spec.ts index 036b9a599..cd99745f9 100644 --- a/backend/test/routes/model/release/postReleaseComment.spec.ts +++ b/backend/test/routes/model/release/postReleaseComment.spec.ts @@ -4,8 +4,6 @@ import audit from '../../../../src/connectors/audit/__mocks__/index.js' import { postReleaseCommentSchema } from '../../../../src/routes/v2/release/postReleaseComment.js' import { createFixture, testPost } from '../../../testUtils/routes.js' -vi.mock('../../../../src/utils/config.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/utils/user.js') vi.mock('../../../../src/connectors/audit/index.js') vi.mock('../../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/model/release/putRelease.spec.ts b/backend/test/routes/model/release/putRelease.spec.ts index 613021270..951baef2c 100644 --- a/backend/test/routes/model/release/putRelease.spec.ts +++ b/backend/test/routes/model/release/putRelease.spec.ts @@ -4,9 +4,7 @@ import audit from '../../../../src/connectors/audit/__mocks__/index.js' import { putReleaseSchema } from '../../../../src/routes/v2/release/putRelease.js' import { createFixture, testPut } from '../../../testUtils/routes.js' -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/utils/user.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/connectors/audit/index.js') vi.mock('../../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/model/webhook/deleteWebhook.spec.ts b/backend/test/routes/model/webhook/deleteWebhook.spec.ts index 0d054c252..c2f703f4a 100644 --- a/backend/test/routes/model/webhook/deleteWebhook.spec.ts +++ b/backend/test/routes/model/webhook/deleteWebhook.spec.ts @@ -2,9 +2,7 @@ import { describe, expect, test, vi } from 'vitest' import { testDelete } from '../../../testUtils/routes.js' -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/utils/user.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/connectors/audit/index.js') vi.mock('../../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/model/webhook/getWebhooks.spec.ts b/backend/test/routes/model/webhook/getWebhooks.spec.ts index 9df044817..6a6e8d64f 100644 --- a/backend/test/routes/model/webhook/getWebhooks.spec.ts +++ b/backend/test/routes/model/webhook/getWebhooks.spec.ts @@ -2,9 +2,7 @@ import { describe, expect, test, vi } from 'vitest' import { testGet } from '../../../testUtils/routes.js' -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/utils/user.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/connectors/audit/index.js') vi.mock('../../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/model/webhook/postWebhook.spec.ts b/backend/test/routes/model/webhook/postWebhook.spec.ts index e1ef2abd0..131c5b67e 100644 --- a/backend/test/routes/model/webhook/postWebhook.spec.ts +++ b/backend/test/routes/model/webhook/postWebhook.spec.ts @@ -3,9 +3,7 @@ import { describe, expect, test, vi } from 'vitest' import { postWebhookSchema } from '../../../../src/routes/v2/model/webhook/postWebhook.js' import { createFixture, testPost } from '../../../testUtils/routes.js' -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/utils/user.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/connectors/audit/index.js') vi.mock('../../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/model/webhook/putWebhook.spec.ts b/backend/test/routes/model/webhook/putWebhook.spec.ts index 2b28bc185..829916740 100644 --- a/backend/test/routes/model/webhook/putWebhook.spec.ts +++ b/backend/test/routes/model/webhook/putWebhook.spec.ts @@ -3,9 +3,7 @@ import { describe, expect, test, vi } from 'vitest' import { putWebhookSchema } from '../../../../src/routes/v2/model/webhook/putWebhook.js' import { createFixture, testPut } from '../../../testUtils/routes.js' -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/utils/user.js') -vi.mock('../../../../src/utils/config.js') vi.mock('../../../../src/connectors/audit/index.js') vi.mock('../../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/review/getReviews.spec.ts b/backend/test/routes/review/getReviews.spec.ts index 3b7b08ac5..45d057d7d 100644 --- a/backend/test/routes/review/getReviews.spec.ts +++ b/backend/test/routes/review/getReviews.spec.ts @@ -4,8 +4,6 @@ import audit from '../../../src/connectors/audit/__mocks__/index.js' import { testGet } from '../../testUtils/routes.js' import { testReleaseReview } from '../../testUtils/testModels.js' -vi.mock('../../../src/utils/config.js') -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/utils/user.js') vi.mock('../../../src/connectors/audit/index.js') vi.mock('../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/review/postAccessRequestReviewResponse.spec.ts b/backend/test/routes/review/postAccessRequestReviewResponse.spec.ts index 303da9b63..0b0fe7a4a 100644 --- a/backend/test/routes/review/postAccessRequestReviewResponse.spec.ts +++ b/backend/test/routes/review/postAccessRequestReviewResponse.spec.ts @@ -6,8 +6,6 @@ import { postAccessRequestReviewResponseSchema } from '../../../src/routes/v2/re import { createFixture, testPost } from '../../testUtils/routes.js' import { testReviewResponse } from '../../testUtils/testModels.js' -vi.mock('../../../src/utils/config.js') -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/utils/user.js') vi.mock('../../../src/connectors/audit/index.js') vi.mock('../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/review/postReleaseReviewResponse.spec.ts b/backend/test/routes/review/postReleaseReviewResponse.spec.ts index 894863162..cad90d558 100644 --- a/backend/test/routes/review/postReleaseReviewResponse.spec.ts +++ b/backend/test/routes/review/postReleaseReviewResponse.spec.ts @@ -6,8 +6,6 @@ import { postReleaseReviewResponseSchema } from '../../../src/routes/v2/review/p import { createFixture, testPost } from '../../testUtils/routes.js' import { testReviewResponse } from '../../testUtils/testModels.js' -vi.mock('../../../src/utils/config.js') -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/utils/user.js') vi.mock('../../../src/connectors/audit/index.js') vi.mock('../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/schema/deleteSchema.spec.ts b/backend/test/routes/schema/deleteSchema.spec.ts index 2866c3dcc..2bea8deec 100644 --- a/backend/test/routes/schema/deleteSchema.spec.ts +++ b/backend/test/routes/schema/deleteSchema.spec.ts @@ -5,8 +5,6 @@ import { deleteSchemaSchema } from '../../../src/routes/v2/schema/deleteSchema.j import { createFixture, testDelete } from '../../testUtils/routes.js' vi.mock('../../../src/utils/user.js') -vi.mock('../../../src/utils/config.js') -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/connectors/audit/index.js') vi.mock('../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/schema/getSchema.spec.ts b/backend/test/routes/schema/getSchema.spec.ts index 89e473d0e..41c140822 100644 --- a/backend/test/routes/schema/getSchema.spec.ts +++ b/backend/test/routes/schema/getSchema.spec.ts @@ -5,9 +5,7 @@ import { NotFound } from '../../../src/utils/error.js' import { testGet } from '../../testUtils/routes.js' import { testModelSchema } from '../../testUtils/testModels.js' -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/utils/user.js') -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/connectors/audit/index.js') vi.mock('../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/schema/getSchemas.spec.ts b/backend/test/routes/schema/getSchemas.spec.ts index 0dca0e02c..930e00b1b 100644 --- a/backend/test/routes/schema/getSchemas.spec.ts +++ b/backend/test/routes/schema/getSchemas.spec.ts @@ -4,9 +4,7 @@ import audit from '../../../src/connectors/audit/__mocks__/index.js' import { testGet } from '../../testUtils/routes.js' import { testDeploymentSchema, testModelSchema } from '../../testUtils/testModels.js' -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/utils/user.js') -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/connectors/audit/index.js') vi.mock('../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/schema/patchSchema.spec.ts b/backend/test/routes/schema/patchSchema.spec.ts index f3c0813df..299dd3f4e 100644 --- a/backend/test/routes/schema/patchSchema.spec.ts +++ b/backend/test/routes/schema/patchSchema.spec.ts @@ -5,8 +5,6 @@ import { patchSchemaSchema } from '../../../src/routes/v2/schema/patchSchema.js' import { createFixture, testPatch } from '../../testUtils/routes.js' vi.mock('../../../src/utils/user.js') -vi.mock('../../../src/utils/config.js') -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/connectors/audit/index.js') vi.mock('../../../src/connectors/authorisation/index.js') diff --git a/backend/test/routes/schema/postSchema.spec.ts b/backend/test/routes/schema/postSchema.spec.ts index 73118078b..928af27de 100644 --- a/backend/test/routes/schema/postSchema.spec.ts +++ b/backend/test/routes/schema/postSchema.spec.ts @@ -5,8 +5,6 @@ import { postSchemaSchema } from '../../../src/routes/v2/schema/postSchema.js' import { createFixture, testPost } from '../../testUtils/routes.js' vi.mock('../../../src/utils/user.js') -vi.mock('../../../src/utils/config.js') -vi.mock('../../../src/utils/config.js') vi.mock('../../../src/connectors/audit/index.js') vi.mock('../../../src/connectors/authorisation/index.js') diff --git a/backend/test/services/__snapshots__/file.spec.ts.snap b/backend/test/services/__snapshots__/file.spec.ts.snap index a32615be2..21fc76217 100644 --- a/backend/test/services/__snapshots__/file.spec.ts.snap +++ b/backend/test/services/__snapshots__/file.spec.ts.snap @@ -61,19 +61,4 @@ exports[`services > file > removeFile > success 1`] = ` } `; -exports[`services > file > uploadFile > virus scan initialised 1`] = ` -[ - [ - "scan-complete", - [Function], - ], - [ - "error", - [Function], - ], - [ - "timeout", - [Function], - ], -] -`; +exports[`services > file > uploadFile > virus scan initialised 1`] = `[]`; diff --git a/backend/test/services/file.spec.ts b/backend/test/services/file.spec.ts index 2e3a307f6..99df49326 100644 --- a/backend/test/services/file.spec.ts +++ b/backend/test/services/file.spec.ts @@ -3,6 +3,7 @@ import { describe, expect, test, vi } from 'vitest' import { FileAction } from '../../src/connectors/authorisation/actions.js' import authorisation from '../../src/connectors/authorisation/index.js' +import { FileScanResult } from '../../src/connectors/fileScanning/Base.js' import { UserInterface } from '../../src/models/User.js' import { downloadFile, @@ -14,6 +15,7 @@ import { } from '../../src/services/file.js' vi.mock('../../src/connectors/authorisation/index.js') +vi.mock('../../src/connectors/fileScanning/index.js') const logMock = vi.hoisted(() => ({ info: vi.fn(), @@ -32,17 +34,17 @@ const configMock = vi.hoisted( port: 8080, }, }, - ui: { - avScanning: { - enabled: false, - }, - }, s3: { buckets: { uploads: 'uploads', registry: 'registry', }, }, + connectors: { + fileScanners: { + kinds: ['clamAV'], + }, + }, }) as any, ) vi.mock('../../src/utils/config.js', () => ({ @@ -50,6 +52,18 @@ vi.mock('../../src/utils/config.js', () => ({ default: configMock, })) +const fileScanResult: FileScanResult = { + state: 'complete', + isInfected: false, + toolName: 'Test', +} + +const fileScanningMock = vi.hoisted(() => ({ + info: vi.fn(() => []), + scan: vi.fn(() => new Promise(() => [fileScanResult])), +})) +vi.mock('../../src/connectors/fileScanning/index.js', async () => ({ default: fileScanningMock })) + const s3Mocks = vi.hoisted(() => ({ putObjectStream: vi.fn(() => ({ fileSize: 100 })), getObjectStream: vi.fn(() => ({ Body: { pipe: vi.fn() } })), @@ -99,7 +113,6 @@ describe('services > file', () => { const mime = 'text/plain' const stream = new Readable() as any fileModelMocks.save.mockResolvedValueOnce({ example: true }) - const result = await uploadFile(user, modelId, name, mime, stream) expect(s3Mocks.putObjectStream).toBeCalled() @@ -109,7 +122,11 @@ describe('services > file', () => { test('uploadFile > virus scan initialised', async () => { vi.spyOn(configMock, 'avScanning', 'get').mockReturnValue({ clamdscan: 'test' }) - vi.spyOn(configMock, 'ui', 'get').mockReturnValue({ avScanning: { enabled: true } }) + vi.spyOn(configMock, 'connectors', 'get').mockReturnValue({ + fileScanners: { + kinds: ['clamAV'], + }, + }) const user = { dn: 'testUser' } as UserInterface const modelId = 'testModelId' const name = 'testFile' diff --git a/backend/test/services/mirroredModel.spec.ts b/backend/test/services/mirroredModel.spec.ts index 4edc9a360..74fc2b325 100644 --- a/backend/test/services/mirroredModel.spec.ts +++ b/backend/test/services/mirroredModel.spec.ts @@ -2,9 +2,22 @@ import { PassThrough } from 'stream' import { describe, expect, test, vi } from 'vitest' import authorisation from '../../src/connectors/authorisation/index.js' +import { FileScanResult } from '../../src/connectors/fileScanning/Base.js' import { UserInterface } from '../../src/models/User.js' import { exportModel, importModel } from '../../src/services/mirroredModel.js' +const fileScanResult: FileScanResult = { + state: 'complete', + isInfected: false, + toolName: 'Test', +} + +const fileScanningMock = vi.hoisted(() => ({ + info: vi.fn(() => []), + scan: vi.fn(() => new Promise(() => [fileScanResult])), +})) +vi.mock('../../src/connectors/fileScanning/index.js', async () => ({ default: fileScanningMock })) + const fflateMock = vi.hoisted(() => ({ unzipSync: vi.fn(), })) @@ -34,9 +47,6 @@ const configMock = vi.hoisted( modelMirror: { enabled: true, }, - avScanning: { - enabled: true, - }, }, s3: { buckets: { uploads: 'test' } }, modelMirror: { @@ -80,7 +90,7 @@ const releaseMocks = vi.hoisted(() => ({ vi.mock('../../src/services/release.js', () => releaseMocks) const fileMocks = vi.hoisted(() => ({ - getFilesByIds: vi.fn(() => [{ _id: '123', avScan: { state: 'complete', isInfected: false }, toJSON: vi.fn() }]), + getFilesByIds: vi.fn(() => [{ _id: '123', avScan: [{ state: 'complete', isInfected: false }], toJSON: vi.fn() }]), getTotalFileSize: vi.fn(() => 42), downloadFile: vi.fn(() => ({ Body: 'test' })), })) @@ -218,8 +228,8 @@ describe('services > mirroredModel', () => { test('exportModel > export contains infected file', async () => { fileMocks.getFilesByIds.mockReturnValueOnce([ - { _id: '123', avScan: { state: 'complete', isInfected: true }, toJSON: vi.fn() }, - { _id: '321', avScan: { state: 'complete', isInfected: false }, toJSON: vi.fn() }, + { _id: '123', avScan: [{ state: 'complete', isInfected: true }], toJSON: vi.fn() }, + { _id: '321', avScan: [{ state: 'complete', isInfected: false }], toJSON: vi.fn() }, ]) const response = exportModel({} as UserInterface, 'modelId', true, ['1.2.3']) await expect(response).rejects.toThrowError('The releases contain file(s) that do not have a clean AV scan.') @@ -228,8 +238,8 @@ describe('services > mirroredModel', () => { test('exportModel > export contains incomplete file scan', async () => { fileMocks.getFilesByIds.mockReturnValueOnce([ - { _id: '123', avScan: { state: 'inProgress' } as any, toJSON: vi.fn() }, - { _id: '321', avScan: { state: 'complete', isInfected: false }, toJSON: vi.fn() }, + { _id: '123', avScan: [{ state: 'inProgress' }] as any, toJSON: vi.fn() }, + { _id: '321', avScan: [{ state: 'complete', isInfected: false }], toJSON: vi.fn() }, ]) const response = exportModel({} as UserInterface, 'modelId', true, ['1.2.3']) await expect(response).rejects.toThrowError('The releases contain file(s) that do not have a clean AV scan.') @@ -239,8 +249,8 @@ describe('services > mirroredModel', () => { test('exportModel > export missing file scan', async () => { fileMocks.getFilesByIds.mockReturnValueOnce([ { _id: '123', toJSON: vi.fn() } as any, - { _id: '321', avScan: { state: 'complete', isInfected: false }, toJSON: vi.fn() }, - { _id: '321', avScan: { state: 'complete', isInfected: false }, toJSON: vi.fn() }, + { _id: '321', avScan: [{ state: 'complete', isInfected: false }], toJSON: vi.fn() }, + { _id: '321', avScan: [{ state: 'complete', isInfected: false }], toJSON: vi.fn() }, ]) const response = exportModel({} as UserInterface, 'testmod', true, ['1.2.3']) await expect(response).rejects.toThrowError('The releases contain file(s) that do not have a clean AV scan.') diff --git a/docker-compose-prod.yml b/docker-compose-prod.yml index 505f4d363..303086bf2 100644 --- a/docker-compose-prod.yml +++ b/docker-compose-prod.yml @@ -25,6 +25,14 @@ services: volumes: - minioVolume:/bitnami/minio + clamd: + image: clamav/clamav:1.3 + healthcheck: + test: ['CMD-SHELL', "echo 'PING' | nc -w 5 localhost 3310"] + interval: 30s + timeout: 10s + retries: 5 + mailcrab: image: marlonb/mailcrab:v1.2.0 ports: @@ -84,8 +92,12 @@ services: - ./backend/certs:/usr/local/share/ca-certificates - ./backend/config:/app/config depends_on: - - mongo - - minio + clamd: + condition: service_healthy + minio: + condition: service_started + mongo: + condition: service_started security_opt: - seccomp:unconfined - apparmor:unconfined diff --git a/docker-compose.yml b/docker-compose.yml index 68a1a788d..cc88d334f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,6 +29,11 @@ services: clamd: image: clamav/clamav:1.3 + healthcheck: + test: ['CMD-SHELL', "echo 'PING' | nc -w 5 localhost 3310"] + interval: 30s + timeout: 10s + retries: 5 mailcrab: image: marlonb/mailcrab:v1.2.0 @@ -92,9 +97,12 @@ services: - 3001:3001 - 9229:9229 depends_on: - - mongo - - minio - - clamd + clamd: + condition: service_healthy + minio: + condition: service_started + mongo: + condition: service_started security_opt: - seccomp:unconfined - apparmor:unconfined diff --git a/frontend/actions/fileScanning.ts b/frontend/actions/fileScanning.ts new file mode 100644 index 000000000..c3732c9e3 --- /dev/null +++ b/frontend/actions/fileScanning.ts @@ -0,0 +1,20 @@ +import useSWR from 'swr' +import { ErrorInfo, fetcher } from 'utils/fetcher' + +const emptyScannerList = [] + +export function useGetFileScannerInfo() { + const { data, isLoading, error, mutate } = useSWR< + { + scanners: string[] + }, + ErrorInfo + >('/api/v2/filescanning/info', fetcher) + + return { + scannersMutate: mutate, + scanners: data ? data.scanners : emptyScannerList, + isScannersLoading: isLoading, + isScannersError: error, + } +} diff --git a/frontend/src/entry/model/releases/FileDownload.tsx b/frontend/src/entry/model/releases/FileDownload.tsx index 50dda8aa3..583e4e715 100644 --- a/frontend/src/entry/model/releases/FileDownload.tsx +++ b/frontend/src/entry/model/releases/FileDownload.tsx @@ -1,8 +1,8 @@ -import { Done, Warning } from '@mui/icons-material' -import { Chip, Grid, Link, Popover, Stack, Tooltip, Typography } from '@mui/material' -import { useGetUiConfig } from 'actions/uiConfig' +import { Done, Error, Warning } from '@mui/icons-material' +import { Chip, Divider, Grid, Link, Popover, Stack, Tooltip, Typography } from '@mui/material' +import { useGetFileScannerInfo } from 'actions/fileScanning' import prettyBytes from 'pretty-bytes' -import { useMemo, useState } from 'react' +import { Fragment, ReactElement, useCallback, useMemo, useState } from 'react' import Loading from 'src/common/Loading' import MessageAlert from 'src/MessageAlert' import { FileInterface, isFileInterface, ScanState } from 'types/types' @@ -13,61 +13,113 @@ type FileDownloadProps = { file: FileInterface | File } +interface ChipDetails { + label: string + colour: 'error' | 'warning' | 'success' + icon: ReactElement +} + export default function FileDownload({ modelId, file }: FileDownloadProps) { const [anchorEl, setAnchorEl] = useState(null) - const { uiConfig, isUiConfigLoading, isUiConfigError } = useGetUiConfig() + const { scanners, isScannersLoading, isScannersError } = useGetFileScannerInfo() const open = Boolean(anchorEl) + const threatsFound = (file: FileInterface) => { + if (file.avScan === undefined) { + return 0 + } + return file.avScan.reduce((acc, scan) => { + return scan.viruses ? scan.viruses.length + acc : acc + }, 0) + } + + const chipDetails = useCallback((file: FileInterface): ChipDetails => { + if (file.avScan === undefined) { + return { label: 'Virus scan results could not be found.', colour: 'warning', icon: } + } + if (file.avScan.some((scan) => scan.state === ScanState.Error)) { + return { label: 'One or more virus scanning tools failed.', colour: 'warning', icon: } + } + if (threatsFound(file)) { + return { + label: `Virus scan failed: ${plural(threatsFound(file), 'threat')} found`, + colour: 'error', + icon: , + } + } + return { label: 'Virus scan passed', colour: 'success', icon: } + }, []) + const avChip = useMemo(() => { - if (!isFileInterface(file) || file.avScan === undefined || file.avScan.state === ScanState.NotScanned) { + if ( + !isFileInterface(file) || + file.avScan === undefined || + file.avScan.every((scan) => scan.state === ScanState.NotScanned) + ) { return } - if (file.avScan.state !== ScanState.Complete) { + if (file.avScan.some((scan) => scan.state === ScanState.InProgress)) { return } - if (file.avScan.viruses && file.avScan.viruses.length > 0) { - return ( - <> - } - size='small' - onClick={(e) => setAnchorEl(e.currentTarget)} - label={`Virus scan failed: ${plural(file.avScan.viruses.length, 'threat')} found`} - /> - setAnchorEl(null)} - anchorOrigin={{ - vertical: 'bottom', - horizontal: 'center', - }} - transformOrigin={{ - vertical: 'top', - horizontal: 'center', - }} - > - - - The virus scan found the following {plural(file.avScan.viruses.length, 'threat')} - -
    {file.avScan.viruses && file.avScan.viruses.map((virus) =>
  • {virus}
  • )}
-
-
- - ) - } - return } size='small' label={'Virus scan passed'} /> - }, [anchorEl, file, open]) + return ( + <> + setAnchorEl(e.currentTarget)} + label={chipDetails(file).label} + /> + setAnchorEl(null)} + anchorOrigin={{ + vertical: 'bottom', + horizontal: 'center', + }} + transformOrigin={{ + vertical: 'top', + horizontal: 'center', + }} + > + }> + {file.avScan.map((scanResult) => ( + + {scanResult.isInfected ? ( + <> + + + + {scanResult.toolName} found the following threats: + + +
    {scanResult.viruses && scanResult.viruses.map((virus) =>
  • {virus}
  • )}
+ + ) : ( + + {scanResult.state === 'error' ? : } + + {scanResult.toolName} + {scanResult.state === 'error' ? 'was not able to be run' : 'did not find any threats'} + + + )} +
+ ))} +
+
+ + ) + }, [anchorEl, chipDetails, file, open]) - if (isUiConfigError) { - return + if (isScannersError) { + return } - if (isUiConfigLoading) { + if (isScannersLoading) { return } @@ -84,7 +136,7 @@ export default function FileDownload({ modelId, file }: FileDownloadProps) { - {uiConfig && uiConfig.avScanning && avChip} + {scanners.length > 0 && avChip} diff --git a/frontend/types/types.ts b/frontend/types/types.ts index bf116d90d..fb1f4989a 100644 --- a/frontend/types/types.ts +++ b/frontend/types/types.ts @@ -55,9 +55,6 @@ export interface UiConfig { text: string startTimestamp: string } - avScanning: { - enabled: boolean - } } export interface FileInterface { @@ -74,16 +71,19 @@ export interface FileInterface { complete: boolean // Older files may not have AV run against them - avScan?: { - state: ScanStateKeys - isInfected?: boolean - viruses?: Array - } + avScan?: AvScanResult[] createdAt: Date updatedAt: Date } +export interface AvScanResult { + state: ScanStateKeys + isInfected?: boolean + viruses?: Array + toolName: string +} + export const ScanState = { NotScanned: 'notScanned', InProgress: 'inProgress', diff --git a/infrastructure/helm/bailo/templates/bailo/bailo.configmap.yaml b/infrastructure/helm/bailo/templates/bailo/bailo.configmap.yaml index 13f9c43d0..17570373e 100644 --- a/infrastructure/helm/bailo/templates/bailo/bailo.configmap.yaml +++ b/infrastructure/helm/bailo/templates/bailo/bailo.configmap.yaml @@ -63,7 +63,6 @@ data: }, avScanning: { - enabled: {{ .Values.clamav.enabled }}, clamdscan: { host: '{{ include "bailo.fullname" . }}-clamav', port: {{ .Values.clamav.port }}, @@ -193,6 +192,10 @@ data: audit: { kind: '{{ .Values.connectors.audit.kind }}', }, + + fileScanners: { + kinds: {{ toJson .Values.connectors.fileScanners.kinds }} + }, }, log: { diff --git a/infrastructure/helm/bailo/values.yaml b/infrastructure/helm/bailo/values.yaml index eec90ca56..b4f86d26f 100644 --- a/infrastructure/helm/bailo/values.yaml +++ b/infrastructure/helm/bailo/values.yaml @@ -256,6 +256,9 @@ connectors: audit: kind: 'silly' + fileScanners: + kinds: ['clamAV'] + instrumentation: enabled: false debug: false