/* |-------------------------------------------------------------------------- | Preloaded File - node ace make:preload rules/fileScan |-------------------------------------------------------------------------- |*/ import { FieldContext } from '@vinejs/vine/types'; import vine, { errors } from '@vinejs/vine'; // import { VineString } from '@vinejs/vine'; import { VineMultipartFile, isBodyParserFile } from '#providers/vinejs_provider'; import type { MultipartFile } from '@adonisjs/core/bodyparser'; import ClamScan from 'clamscan'; /** * Options accepted by the unique rule */ // type Options = { // mainLanguageField: string; // typeField: string; // }; type Options = { // size: string | number; // extnames: string[]; removeInfected: boolean; // debugMode?: boolean; // scanRecursively?: boolean; host?: string; port?: number; // clamdscan: { // active: boolean; // host: string; // port: number; // multiscan: boolean; // }; // preference: string; }; async function fileScan(file: VineMultipartFile | unknown, options: Options, field: FieldContext) { // if (typeof value !== 'string' && typeof value != 'number') { // return; // } if (!isBodyParserFile(file)) { return; } const validatedFile = file as MultipartFile; try { await scanFileForViruses(validatedFile.tmpPath, options.host, options.port); //, 'gitea.lan', 3310); // await this.scanFileForViruses("/tmp/testfile.txt"); } catch (error) { // If the file is infected or there's an error scanning the file, throw a validation exception // throw error; field.report(`Upload error. Code: ${error.code} message: ${error.messages.uploadError}`, 'fileScan', field); } } async function scanFileForViruses(filePath: string | undefined, host?: string, port?: number): Promise { // const clamscan = await (new ClamScan().init()); const opts: ClamScan.Options = { removeInfected: true, // If true, removes infected files debugMode: false, // Whether or not to log info/debug/error msgs to the console scanRecursively: true, // If true, deep scan folders recursively clamdscan: { active: true, // If true, this module will consider using the clamdscan binary host, port, multiscan: true, // Scan using all available cores! Yay! }, preference: 'clamdscan', // If clamdscan is found and active, it will be used by default }; return new Promise(async (resolve, reject) => { try { const clamscan = await new ClamScan().init(opts); // You can re-use the `clamscan` object as many times as you want // const version = await clamscan.getVersion(); // console.log(`ClamAV Version: ${version}`); const { file, isInfected, viruses } = await clamscan.isInfected(filePath); if (isInfected) { console.log(`${file} is infected with ${viruses}!`); // reject(new ValidationException(true, { 'upload error': `File ${file} is infected!` })); reject(new errors.E_VALIDATION_ERROR({ uploadError: `File ${file} is infected!` })); } else { resolve(); } } catch (error) { // If there's an error scanning the file, throw a validation exception // reject(new ValidationException(true, { 'upload error': `${error.message}` })); reject(new errors.E_VALIDATION_ERROR({ uploadError: `${error.message}!` })); } }); } export const fileScanRule = vine.createRule(fileScan); declare module '#providers/vinejs_provider' { interface VineMultipartFile { fileScan(options: Options): this; } } VineMultipartFile.macro('fileScan', function (this: VineMultipartFile, options: Options) { return this.use(fileScanRule(options)); });