feat(ClamAvService): Add stream scanning methods to ClamAvService

This commit is contained in:
2025-02-05 10:49:46 +01:00
parent 8acfedd7f3
commit 6300843616
9 changed files with 123 additions and 7 deletions

View File

@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@push.rocks/smartantivirus',
version: '1.1.2',
version: '1.2.0',
description: 'A Node.js package for integrating antivirus scanning capabilities using ClamAV, allowing in-memory file and data scanning.'
}

View File

@ -111,4 +111,77 @@ export class ClamAvService {
});
});
}
/**
* Scans data from a NodeJS stream using ClamAV daemon's INSTREAM command.
*/
public async scanStream(stream: NodeJS.ReadableStream): Promise<{ isInfected: boolean; reason?: string }> {
await this.ensureContainerStarted();
return new Promise((resolve, reject) => {
const client = new net.Socket();
client.connect(this.port, this.host, () => {
console.log('Connected to ClamAV daemon for stream scanning');
client.write('zINSTREAM\0');
stream.on('data', (chunk: Buffer) => {
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
const sizeBuf = Buffer.alloc(4);
sizeBuf.writeUInt32BE(buf.length, 0);
client.write(sizeBuf);
client.write(buf);
});
stream.on('end', () => {
const endOfStream = Buffer.alloc(4);
endOfStream.writeUInt32BE(0, 0);
console.log('Stream ended, sending end-of-stream signal');
client.write(endOfStream);
});
stream.on('error', (err) => {
console.error('Error reading stream:', err);
reject(err);
});
});
client.on('data', (data) => {
const response = data.toString();
console.log('Raw Response from ClamAV (stream):', response);
const isInfected = response.includes('FOUND');
const reason = isInfected ? response.split('FOUND')[0].trim() : undefined;
resolve({ isInfected, reason });
client.end();
});
client.on('error', (err) => {
console.error('Error with ClamAV stream scanning:', err);
reject(err);
});
});
}
/**
* Scans a file from a web URL as a stream using ClamAV daemon's INSTREAM command.
*/
public async scanFileFromWebAsStream(url: string): Promise<{ isInfected: boolean; reason?: string }> {
return new Promise((resolve, reject) => {
const protocol = url.startsWith('https') ? plugins.https : plugins.http;
protocol.get(url, (response) => {
this.scanStream(response).then(resolve).catch(reject);
}).on('error', (err) => {
console.error('Error fetching URL:', err);
reject(err);
});
});
}
/**
* Scans a web resource by URL using ClamAV daemon's INSTREAM command.
*/
public async scanWebStream(webstreamArg: ReadableStream): Promise<{ isInfected: boolean; reason?: string }> {
// Convert the web ReadableStream to a NodeJS ReadableStream
const nodeStream = plugins.smartstream.nodewebhelpers.convertWebReadableToNodeReadable(webstreamArg);
return this.scanStream(nodeStream);
}
}

View File

@ -1,2 +1,2 @@
export * from './classes.smartantivirus.js';
export * from './classes.clamavservice.js';
export * from './classes.clamav.manager.js';

View File

@ -5,6 +5,8 @@ import { exec, spawn } from 'child_process';
import { promisify } from 'util';
import { EventEmitter } from 'events';
import net from 'net';
import * as http from 'http';
import * as https from 'https';
export {
fs,
@ -13,19 +15,20 @@ export {
spawn,
promisify,
EventEmitter,
net
net,
http,
https
};
// @push.rocks scope
import * as smartpath from '@push.rocks/smartpath';
import * as smartfile from '@push.rocks/smartfile';
import { expect, tap } from '@push.rocks/tapbundle';
import * as smartstream from '@push.rocks/smartstream';
export {
smartpath,
smartfile,
expect,
tap
smartstream,
};
// Third party scope