v1.0.0
1.0.0
This is essentially complete re-write of the clamscan module that supports a full socket pipeline allowing one to use this module as a middleman to scan the files on its way to its final destination.
A good example would be that a user uploads a file from a form on a website. As the file is being uploaded to the server, it can be scanned and piped on to AWS S3... never really touching the disk on the receiving server. If at any point the module detects a virus, the upload will stop.
This release also introduces a new initialization API, Promise support, scanning of streams and buffers, modern code, no third-party dependencies, and a host of other benefits, features, and bug fixes detailed below.
This is a huge major release in which this module was essentially completely re-written. This version introduces some breaking changes and major new features. Please read the release notes below carefully.
-
Now requires at least Node v10.0.0
-
Code re-written in ES2018 code
-
Now supports a hybrid Promise/Callback API (supports async/await)
-
Now properly supports TCP/UNIX Domain socket communication to local or remote clamav services (with optional fallback to local binary via child process).
-
Added new
scan_stream
method which allows you to pass an input stream. -
Added new
get_version
method which allows you to check the version of ClamAV that you'll be communicating with. -
Added new
passthrough
method which allows you to pipe a stream "through" the clamscan module and on to another destination (ex. S3). -
Added new alias
scan_file
that points tois_infected
. -
In order to provide the name of any viruses found, a new standard
viruses
array is now be provided to the callback for:is_infected
&scan_file
methods (callback format:(err, file, is_infected, viruses) => { ... }
).scan_files
method (callback format:(err, good_files, bad_files, error_files, viruses) => { ... }
).scan_dir
method (callback format:(err, good_files, bad_files, viruses) => { ... }
).
-
In all cases, the
viruses
parameter will be an empty array on error or when no viruses are found. -
scan_files
now has another additional parameter in its callback:error_files
: An object keyed by the filenames that presented errors while scanning. The value of those keys will be the error message for that file.
-
Introduces new API to instantiate the module (NOTE: The old way will no longer work! See below for more info).
API Changes with 1.0.0:
For some full-fledged examples of how the new API works, checkout the /examples
directory in the module root directory.
Module Initialization
Pre-1.0.0
const clamscan = require('clamscan')(options);
1.0.0
NOTE: Due to the new asynchronous nature of the checks that are performed upon initialization of the module, the initialization method now returns a Promise instead of the actual instantiated object. Resolving the Promise with then
will return the object like before.
const ClamScan = require('clamscan');
const scanner = new NodeClam().init(options);
Making Method Calls
Pre-1.0.0
scanner.is_infected('/path/to/file.txt', (err, file, is_infected) => {
// Do stuff
});
1.0.0
scanner.then(clamscan => {
clamscan.is_infected('/path/to/file.txt', (err, file, is_infected, viruses) => {
// Do stuff
});
});
If you prefer the async/await style of coding:
;(async () => {
const scanner = await new ClamScan().init(options);
scanner.is_infected('/path/to/file.txt', (err, file, is_infected, viruses) => {
// Do stuff
});
})();
New Way to Get Results
Pre-1.0.0
The only way to get results/errors in pre-1.0.0 was through callbacks.
const clamscan = require('clamscan')(options);
clamscan.scan_dir('/path/to/directory', (err, good_files, bad_files) => {
// Do stuff inside callback
});
1.0.0
In version 1.0.0 and beyond, you will now be able to use Promises as well (and, of course, async/await).
Promises
const clamscan = new ClamScan().init(options);
clamscan.then(scanner =>
scanner.scan_dir('/path/to/directory').then(result => {
const {good_files, bad_files} = result;
// Do stuff
}).catch(err => {
// Handle scan error
});
}).catch(err => {
// Handle initialization error
});
Async/Await
;(async () => {
try {
const scanner = await new ClamScan().init(options);
const {good_files, bad_files} = await scanner.scan_dir('/path/to/directory');
// Do stuff
} catch (err) {
// Handle any error
}
})();
New Methods
scan_stream
The scan_stream
method allows you supply a readable stream to have it scanned. Theoretically any stream can be scanned this way. Like all methods, it supports callback and Promise response styles (full documentation is in README).
Basic Promise (async/await) Example:
;(async () => {
try {
const scanner = await new ClamScan().init(options);
const stream = new Readable();
rs.push('foooooo');
rs.push('barrrrr');
rs.push(null);
const {is_infected, viruses} = await scanner.scan_stream(stream);
// Do stuff
} catch (err) {
// Handle any error
}
})();
Basic Callback Example:
;(async () => {
try {
const scanner = await new ClamScan().init(options);
const stream = new Readable();
rs.push('foooooo');
rs.push('barrrrr');
rs.push(null);
scanner.scan_stream(stream, (err, results) => {
if (err) {
// Handle error
} else {
const {is_infected, viruses} = results;
// Do stuff
}
});
// Do stuff
} catch (err) {
// Handle any error
}
})();
passthrough
The passthrough
method allows you supply a readable stream that will be "passed-through" the clamscan module and onto another destination. In reality, the passthrough method works more like a fork stream whereby the input stream is simultaneously streamed to ClamAV and whatever is the next destination. Events are created when ClamAV is done and/or when viruses are detected so that you can decide what to do with the data on the next destination (delete if virus detected, for instance). Data is only passed through to the next generation if the data has been successfully received by ClamAV. If anything halts the data going to ClamAV (including issues caused by ClamAV), the entire pipeline is halted and events are fired.
Normally, a file is uploaded and then scanned. This method should theoretically speed up user uploads intended to be scanned by up to 2x because the files are simultaneously scanned and written to disk. Your mileage my vary.
This method is different than all the others in that it returns a PassthroughStream object and does not support a Promise or Callback API. This makes sense once you see the example below (full documentation is in README).
Basic Example:
;(async () => {
try {
const scanner = await new ClamScan().init(options);
const request = require('request');
const input = request.get(some_url);
const output = fs.createWriteStream(some_local_file);
const av = scanner.passthrough();
// Send output of RequestJS stream to ClamAV.
// Send output of RequestJS to `some_local_file` if ClamAV receives data successfully
input.pipe(av).pipe(output);
// What happens when scan is completed
av.on('scan-complete', result => {
check(done, () => {
const {is_infected, viruses} = result;
// Do stuff if you want
});
});
// What happens when data has been fully written to `output`
output.on('finish', () => {
// Do stuff if you want
});
} catch (err) {
// Handle any error
}
})();