-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1514 from gchq/feature/BAI-1458-add-file-scanning…
…-connector File scanning connector to handle multiple AV tools
- Loading branch information
Showing
73 changed files
with
499 additions
and
260 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<FileScanResult[]> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<FileScanResult[]> { | ||
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, | ||
}, | ||
] | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<GetFileScanningInfoResponse>) => { | ||
return res.json({ scanners: scanners.info() }) | ||
}, | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -70,8 +70,4 @@ export interface UiConfig { | |
text: string | ||
startTimestamp: string | ||
} | ||
|
||
avScanning: { | ||
enabled: boolean | ||
} | ||
} |
Oops, something went wrong.