Comparing version 1.0.0-beta.10 to 1.0.0
@@ -10,16 +10,18 @@ const request = require('request'); | ||
const config = { | ||
remove_infected: true, | ||
debug_mode: false, | ||
scan_recursively: false, | ||
clamdscan: { | ||
path: '/usr/bin/clamdscan', | ||
// config_file: '/etc/clamd.d/daemon.conf' | ||
}, | ||
preference: 'clamdscan' | ||
}; | ||
// Initialize the clamscan module | ||
const NodeClam = require('../index.js'); // Offically: require('clamscan'); | ||
async function test() { | ||
const clamscan = await new NodeClam().init({ | ||
remove_infected: true, | ||
debug_mode: false, | ||
scan_recursively: false, | ||
clamdscan: { | ||
path: '/usr/bin/clamdscan', | ||
config_file: '/etc/clamd.d/daemon.conf' | ||
}, | ||
preference: 'clamdscan' | ||
}); | ||
;(async () => { | ||
const clamscan = await new NodeClam().init(config); | ||
@@ -59,4 +61,2 @@ // Request a test file from the internet... | ||
}); | ||
} | ||
test(); | ||
})(); |
328
HISTORY.md
# Changes | ||
This file is a manually maintained list of changes for each release. Feel free | ||
to add your changes here when sending pull requests. Also send corrections if | ||
you spot any mistakes. | ||
This file is a manually maintained list of changes for each release. Feel free to add your changes here when sending pull requests. Also send corrections if you spot any mistakes. | ||
### 0.2.1 | ||
## 0.2.1 | ||
* ClamAV returns an exit code 1 when it detects a virus but `exec` was interpreting that response as an error. Checking the response with type-sensitive equivalence resolves this bug. | ||
- ClamAV returns an exit code 1 when it detects a virus but `exec` was interpreting that response as an error. Checking the response with type-sensitive equivalence resolves this bug. | ||
### 0.2.2 | ||
## 0.2.2 | ||
* Fixed documentation | ||
- Fixed documentation | ||
### 0.4.0 (2014-11-19) | ||
## 0.4.0 (2014-11-19) | ||
* Corrected the installation instructions for `clamav`. Thank you @jshamley! | ||
* Fixed major bug preventing the `scan_dir` method from working properly | ||
* Corrected documentation describing how to instantiate this module. | ||
- Corrected the installation instructions for `clamav`. Thank you @jshamley! | ||
- Fixed major bug preventing the `scan_dir` method from working properly | ||
- Corrected documentation describing how to instantiate this module. | ||
### 0.5.0 (2014-12-19) | ||
## 0.5.0 (2014-12-19) | ||
* Deprecated the `quarantine_path` option. Please only use `quarantine_infected` for now on. | ||
* Updated documentation to reflect above change. | ||
- Deprecated the `quarantine_path` option. Please only use `quarantine_infected` for now on. | ||
- Updated documentation to reflect above change. | ||
### 0.6.0 (2015-01-02) | ||
## 0.6.0 (2015-01-02) | ||
__NOTE:__ There are some breaking changes on this release. Since this is still a pre-version 1 release, I decided to only do a minor bump to 0.4.0 | ||
**NOTE:** There are some breaking changes on this release. Since this is still a pre-version 1 release, I decided to only do a minor bump to 0.4.0 | ||
* The ability to run "forked" instances of `clamscan` has been removed because of irregularities with different systems--namely if you had `max_forks` set to 3, it would sometimes only scan the first or last file in the group... not good. | ||
* Added the ability to use `clamdscan`. This ultimately negates the downside of removing the forking capability mentioned in item one. This is a really big improvement (many orders of magnitude) if your system has access to the `clamdscan` daemon. | ||
* Added a `file_list` option allowing one to specify a text file that lists (one per line) paths to files to be scanned. This is great if you need to scan hundreds or thousands of random files. | ||
* `clam_path` option has been moved to `clam.path` | ||
* `db` option has been moved to `clam.db` | ||
* `scan_archives` option has been moved to `clam.scan_archives` | ||
* `scan_files` now supports directories as well and will obey your `scan_recursively` option. | ||
- The ability to run "forked" instances of `clamscan` has been removed because of irregularities with different systems--namely if you had `max_forks` set to 3, it would sometimes only scan the first or last file in the group... not good. | ||
- Added the ability to use `clamdscan`. This ultimately negates the downside of removing the forking capability mentioned in item one. This is a really big improvement (many orders of magnitude) if your system has access to the `clamdscan` daemon. | ||
- Added a `file_list` option allowing one to specify a text file that lists (one per line) paths to files to be scanned. This is great if you need to scan hundreds or thousands of random files. | ||
- `clam_path` option has been moved to `clam.path` | ||
- `db` option has been moved to `clam.db` | ||
- `scan_archives` option has been moved to `clam.scan_archives` | ||
- `scan_files` now supports directories as well and will obey your `scan_recursively` option. | ||
### 0.6.1 (2015-01-05) | ||
## 0.6.1 (2015-01-05) | ||
* Updated description in package.json file. | ||
- Updated description in package.json file. | ||
### 0.6.2 (2015-01-05) | ||
## 0.6.2 (2015-01-05) | ||
* Fixed major bug in the scan_files method that was causing it to only scan half the files passed to it. | ||
- Fixed major bug in the scan_files method that was causing it to only scan half the files passed to it. | ||
### 0.6.3 (2015-01-05) | ||
## 0.6.3 (2015-01-05) | ||
* Removed the unnecessary "index_old.js" file put there for reference during the 0.5.0 -> 0.6.0 semi-rewrite. | ||
- Removed the unnecessary "index_old.js" file put there for reference during the 0.5.0 -> 0.6.0 semi-rewrite. | ||
### 0.6.4 (2015-01-26) | ||
## 0.6.4 (2015-01-26) | ||
* Fixed error messages | ||
- Fixed error messages | ||
### 0.7.0 (2015-06-01) | ||
## 0.7.0 (2015-06-01) | ||
* Fixed a bug caused by not passing a `file_cb` paramter to the `scan_file` method. Thanks nicolaspeixoto! | ||
* Added tests | ||
* Fixed poor validation of method parameters | ||
* Changed API of `scan_dir` such that the paramaters passed to the `end_cb` are different in certain defined situations. See the "NOTE" section of the `scan_dir` documentation for details. | ||
* Changed `err` paramter in all callbacks from a simple string to a proper javascript `Error` object. | ||
* Added documentation for how to use a file_list file for scanning. | ||
- Fixed a bug caused by not passing a `file_cb` paramter to the `scan_file` method. Thanks nicolaspeixoto! | ||
- Added tests | ||
- Fixed poor validation of method parameters | ||
- Changed API of `scan_dir` such that the paramaters passed to the `end_cb` are different in certain defined situations. See the "NOTE" section of the `scan_dir` documentation for details. | ||
- Changed `err` paramter in all callbacks from a simple string to a proper javascript `Error` object. | ||
- Added documentation for how to use a file_list file for scanning. | ||
### 0.7.1 (2015-06-05) | ||
## 0.7.1 (2015-06-05) | ||
* Added node dependency of > 0.12 to `package.json` file | ||
- Added node dependency of > 0.12 to `package.json` file | ||
### 0.8.0 (2015-06-05) | ||
## 0.8.0 (2015-06-05) | ||
* Removed item causing node > 0.12 dependency. | ||
* Removed dependency of node > 0.12 in `package.json` file. | ||
- Removed item causing node > 0.12 dependency. | ||
- Removed dependency of node > 0.12 in `package.json` file. | ||
### 0.8.1 (2015-06-09) | ||
## 0.8.1 (2015-06-09) | ||
* Fixed check for database file. Issue #6 | ||
- Fixed check for database file. Issue #6 | ||
### 0.8.2 (2015-08-14) | ||
## 0.8.2 (2015-08-14) | ||
* Updated to `execFile` instead of `exec` | ||
* Improved test suite | ||
- Updated to `execFile` instead of `exec` | ||
- Improved test suite | ||
### 0.9.0-beta (2015-07-01) - Never Released | ||
## 0.9.0-beta (2015-07-01) - Never Released | ||
* Added support for TCP/UNIX Domain socket communication to local or remote clamav services. | ||
* Added a `get_version` method. | ||
* NULL is now returned to the third paramter of the `is_infected` when file is neither infected or clean (i.e. on unexpected response) | ||
* Created alias: `scan_file` for `is_infected`. | ||
* Created a `scan_stream` method. | ||
* Minor code clean-up | ||
- Added support for TCP/UNIX Domain socket communication to local or remote clamav services. | ||
- Added a `get_version` method. | ||
- NULL is now returned to the third parameter of the `is_infected` when file is neither infected or clean (i.e. on unexpected response) | ||
- Created alias: `scan_file` for `is_infected`. | ||
- Created a `scan_stream` method. | ||
- Minor code clean-up | ||
### 1.0.0 | ||
## 1.0.0 (2019-05-02) | ||
* 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) | ||
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 to `is_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 | ||
```javascript | ||
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. | ||
```javascript | ||
const NodeClam = require('clamscan'); | ||
const ClamScan = new NodeClam().init(options); | ||
``` | ||
#### Making Method Calls | ||
##### Pre-1.0.0 | ||
```javascript | ||
clamscan.is_infected('/path/to/file.txt', (err, file, is_infected) => { | ||
// Do stuff | ||
}); | ||
``` | ||
##### 1.0.0 | ||
```javascript | ||
ClamScan.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: | ||
```javascript | ||
;(async () => { | ||
const clamscan = await new NodeClam().init(options); | ||
clamscan.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. | ||
```javascript | ||
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 | ||
```javascript | ||
const ClamScan = new NodeClam().init(options); | ||
ClamScan.then(clamscan => | ||
clamscan.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 | ||
```javascript | ||
;(async () => { | ||
try { | ||
const clamscan = await new NodeClam().init(options); | ||
const {good_files, bad_files} = await clamscan.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: | ||
```javascript | ||
;(async () => { | ||
try { | ||
const clamscan = await new NodeClam().init(options); | ||
const stream = new Readable(); | ||
rs.push('foooooo'); | ||
rs.push('barrrrr'); | ||
rs.push(null); | ||
const {is_infected, viruses} = await clamscan.scan_stream(stream); | ||
// Do stuff | ||
} catch (err) { | ||
// Handle any error | ||
} | ||
})(); | ||
``` | ||
###### Basic Callback Example: | ||
```javascript | ||
;(async () => { | ||
try { | ||
const clamscan = await new NodeClam().init(options); | ||
const stream = new Readable(); | ||
rs.push('foooooo'); | ||
rs.push('barrrrr'); | ||
rs.push(null); | ||
clamscan.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: | ||
```javascript | ||
;(async () => { | ||
try { | ||
const clamscan = await new NodeClam().init(options); | ||
const request = require('request'); | ||
const input = request.get(some_url); | ||
const output = fs.createWriteStream(some_local_file); | ||
const av = clamscan.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 => { | ||
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 | ||
} | ||
})(); | ||
``` |
{ | ||
"name": "clamscan", | ||
"version": "1.0.0-beta.10", | ||
"version": "1.0.0", | ||
"author": "Kyle Farris <kfarris@chomponllc.com> (http://chomponllc.com)", | ||
@@ -5,0 +5,0 @@ "description": "Use Node JS to scan files on your server with ClamAV's clamscan/clamdscan binary or via TCP to a remote server or local UNIX Domain socket. This is especially useful for scanning uploaded files provided by un-trusted sources.", |
683
README.md
@@ -1,6 +0,8 @@ | ||
## NodeJS Clamscan Virus Scanning Utility | ||
[![NPM Version][npm-version-image]][npm-url] [![NPM Downloads][npm-downloads-image]][npm-url] [![Node.js Version][node-image]][node-url] [![Build Status][travis-image]][travis-url] | ||
# NodeJS Clamscan Virus Scanning Utility | ||
Use Node JS to scan files on your server with ClamAV's clamscan/clamdscan binary or via TCP to a remote server or local UNIX Domain socket. This is especially useful for scanning uploaded files provided by un-trusted sources. | ||
## !!IMPORTANT!! | ||
# !!IMPORTANT!! | ||
@@ -11,6 +13,36 @@ If you are using a version prior to 0.8.2, please upgrade! There was a security vulnerability in previous versions that allows a malicious user to execute code on your server. Specific details on how the attack could be implemented will not be disclosed here. Please update to 0.8.2 or greater ASAP. No breaking changes are included, only the security patch. | ||
## Dependencies | ||
# Version 1.0.0 Information: | ||
### To use local binary method of scanning: | ||
If you are migrating from v0.8.5 or less to v1.0.0 or greater, please read the [release notes](https://github.com/kylefarris/clamscan/releases/tag/v1.0.0) as there are some breaking changes (but also some awesome new features!). | ||
# Table of Contents | ||
- [Dependencies](#dependencies) | ||
- [Local Binary Method](#to-use-local-binary-method-of-scanning) | ||
- [TCP/Domain Socket Method](#to-use-clamav-using-tcp-sockets) | ||
- [How to Install](#how-to-install) | ||
- [License Info](#license-info) | ||
- [Getting Started](#getting-started) | ||
- [A note about using this module via sockets or TCP](#a-note-about-using-this-module-via-sockets-or-tcp) | ||
- [Basic Usage Example](#basic-usage-example) | ||
- [API](#api) | ||
- [get_version](#get_version) | ||
- [is_infected (alias: scan_file)](#is_infected) | ||
- [scan_dir](#scan_dir) | ||
- [scan_files](#scan_files) | ||
- [scan_stream](#scan_stream) | ||
- [passthrough](#passthrough) | ||
- [Contribute](#contribute) | ||
- [Resources used to help develop this module](#resources-used-to-help-develop-this-module) | ||
# Dependencies | ||
## To use local binary method of scanning: | ||
You will need to install ClamAV's clamscan binary and/or have clamdscan daemon running on your server. On linux, it's quite simple. | ||
@@ -20,13 +52,19 @@ | ||
sudo yum install clamav | ||
``` | ||
sudo yum install clamav | ||
``` | ||
Debian-based distros: | ||
sudo apt-get install clamav | ||
``` | ||
sudo apt-get install clamav | ||
``` | ||
For OS X, you can install clamav with brew: | ||
sudo brew install clamav | ||
``` | ||
sudo brew install clamav | ||
``` | ||
### To use ClamAV using TCP sockets: | ||
## To use ClamAV using TCP sockets: | ||
@@ -36,24 +74,29 @@ You will need access to either: | ||
1. A local UNIX Domain socket for a local instance of `clamd` | ||
* Follow instructions in [To use local binary method of scanning](#user-content-to-use-local-binary-method-of-scanning). | ||
* Socket file is usually: `/var/run/clamd.scan/clamd.sock` | ||
* Make sure `clamd` is running on your local server | ||
1. A local/remote `clamd` daemon | ||
* Must know the port the daemon is running on | ||
* If running on remote server, you must have the IP address/domain name | ||
* If running on remote server, it's firewall must have the appropriate TCP port(s) open | ||
* Make sure `clamd` is running on your local/remote server | ||
__NOTE:__ This module is not intended to work on a Windows server. This would be a welcome addition if someone wants to add that feature (I may get around to it one day but have no urgent need for this). | ||
- Follow instructions in [To use local binary method of scanning](#user-content-to-use-local-binary-method-of-scanning). | ||
- Socket file is usually: `/var/run/clamd.scan/clamd.sock` | ||
- Make sure `clamd` is running on your local server | ||
## How to Install | ||
2. A local/remote `clamd` daemon | ||
npm install clamscan | ||
- Must know the port the daemon is running on | ||
- If running on remote server, you must have the IP address/domain name | ||
- If running on remote server, it's firewall must have the appropriate TCP port(s) open | ||
- Make sure `clamd` is running on your local/remote server | ||
## Licence info | ||
**NOTE:** This module is not intended to work on a Windows server. This would be a welcome addition if someone wants to add that feature (I may get around to it one day but have no urgent need for this). | ||
# How to Install | ||
``` | ||
npm install clamscan | ||
``` | ||
# License Info | ||
Licensed under the MIT License: | ||
* http://www.opensource.org/licenses/mit-license.php | ||
- <http://www.opensource.org/licenses/mit-license.php> | ||
## Getting Started | ||
# Getting Started | ||
@@ -65,3 +108,4 @@ All of the values listed in the example below represent the default values for their respective configuration item. | ||
```javascript | ||
var clam = require('clamscan')(); | ||
const NodeClam = require('clamscan'); | ||
const ClamScan = new NodeClam().init(); | ||
``` | ||
@@ -71,6 +115,7 @@ | ||
__BUT__: If you want more control, you can specify all sorts of options. | ||
**BUT**: If you want more control, you can specify all sorts of options. | ||
```javascript | ||
var clam = require('clamscan')({ | ||
const NodeClam = require('clamscan'); | ||
const ClamScan = new NodeClam().init({ | ||
remove_infected: false, // If true, removes infected files | ||
@@ -92,8 +137,10 @@ quarantine_infected: false, // False: Don't quarantine, Path: Moves files to this place. | ||
port: false, // Port of host to use when connecting via TCP interface | ||
timeout: 60000, // Timeout for scanning files | ||
local_fallback: false, // Do no fail over to binary-method of scanning | ||
path: '/usr/bin/clamdscan', // Path to the clamdscan binary on your server | ||
local_fallback: false, // Do no fail over to binary-method of scanning | ||
config_file: '/etc/clamd.conf', // Specify config file if it's in an unusual place | ||
config_file: null, // Specify config file if it's in an unusual place | ||
multiscan: true, // Scan using all available cores! Yay! | ||
reload_db: false, // If true, will re-load the DB on every call (slow) | ||
active: true // If true, this module will consider using the clamdscan binary | ||
bypass_test: false, // Check to see if socket is available when applicable | ||
}, | ||
@@ -104,16 +151,17 @@ preference: 'clamdscan' // If clamdscan is found and active, it will be used by default | ||
Here is a _non-default values example_ (to help you get an idea of what the proper-looking values should be): | ||
Here is a _non-default values example_ (to help you get an idea of what proper-looking values could be): | ||
```javascript | ||
var clam = require('clamscan')({ | ||
const NodeClam = require('clamscan'); | ||
const ClamScan = new NodeClam().init({ | ||
remove_infected: true, // Removes files if they are infected | ||
quarantine_infected: '~/infected/', // Move file here. remove_infected must be FALSE, though. | ||
scan_recursively: false, // Choosing false here will save some CPU cycles | ||
scan_log: '/var/log/node-clam', // You're a detail-oriented security professional. | ||
debug_mode: true // This will put some debug info in your js console | ||
file_list: '/home/webuser/scan_files.txt', // path to file containing list of files to scan | ||
scan_recursively: false, // Choosing false here will save some CPU cycles | ||
clamscan: { | ||
path: '/usr/bin/clam', // I dunno, maybe your clamscan is just call "clam" | ||
scan_archives: false, // Choosing false here will save some CPU cycles | ||
db: '/usr/bin/better_clam_db', // Path to a custom virus definition database | ||
scan_archives: false, // Choosing false here will save some CPU cycles | ||
active: false // you don't want to use this at all because it's evil | ||
@@ -125,8 +173,10 @@ }, | ||
port: 12345, // Because, why not | ||
timeout: 300000, // 5 minutes | ||
local_fallback: true, // Use local preferred binary to scan if socket/tcp fails | ||
path: '/bin/clamdscan', // Special path to the clamdscan binary on your server | ||
local_fallback: true, // Use local preferred binary to scan if socket/tcp fails | ||
config_file: '/etc/clamd.d/daemon.conf', // A fairly typical config location | ||
multiscan: false, // You hate speed and multi-threaded awesome-sauce | ||
reload_db: true, // You want your scans to run slow like with clamscan | ||
active: false // you don't want to use this at all because it's evil | ||
active: false, // you don't want to use this at all because it's evil | ||
bypass_test: true, // Don't check to see if socket is available. You should probably never set this to true. | ||
}, | ||
@@ -137,73 +187,147 @@ preference: 'clamscan' // If clamscan is found and active, it will be used by default | ||
#### A note about using this module via sockets or TCP | ||
## A note about using this module via sockets or TCP | ||
As of version 0.9, this module supports communication with a local or remote ClamAV daemon through Unix Domain sockets or a TCP host/port combo. If you supply both in your configuration object, the UNIX Domain socket option will be used. The module *not* not fallback to using the alternative Host/Port method. If you wish to connect via Host/Port and not a Socket, please either omit the `socket` property in the config object or use `socket: null`. | ||
As of version v1.0.0, this module supports communication with a local or remote ClamAV daemon through Unix Domain sockets or a TCP host/port combo. If you supply both in your configuration object, the UNIX Domain socket option will be used. The module _will not_ not fallback to using the alternative Host/Port method. If you wish to connect via Host/Port and not a Socket, please either omit the `socket` property in the config object or use `socket: null`. | ||
If you specify a valid clamscan/clamdscan binary in your config and you set `clamdscan.local_fallback: true` in your config, this module *will* fallback to the traditional way this module has worked—using a binary directly/locally. | ||
If you specify a valid clamscan/clamdscan binary in your config and you set `clamdscan.local_fallback: true` in your config, this module _will_ fallback to the traditional way this module has worked--using a binary directly/locally. | ||
Also, there are some caveats to using the socket/tcp based approach: | ||
* The following configuration items are not honored (unless the module falls back to binary method): | ||
* `remove_infected` - remote clamd service config will dictate this | ||
* `quarantine_infected` - remote clamd service config will dictate this | ||
* `scan_log` - remote clamd service config will dictate this | ||
* `file_list` - this simply won't be available | ||
* `clamscan.db` - only available on fallback | ||
* `clamscan.scan_archives` - only available on fallback | ||
* `clamscan.path` - only available on fallback | ||
* `clamdscan.config_file` - only available on fallback | ||
* `clamdscan.path` - only available on fallback | ||
- The following configuration items are not honored (unless the module falls back to binary method): | ||
## API | ||
- `remove_infected` - remote clamd service config will dictate this | ||
- `quarantine_infected` - remote clamd service config will dictate this | ||
- `scan_log` - remote clamd service config will dictate this | ||
- `file_list` - this simply won't be available | ||
- `clamscan.db` - only available on fallback | ||
- `clamscan.scan_archives` - only available on fallback | ||
- `clamscan.path` - only available on fallback | ||
- `clamdscan.config_file` - only available on fallback | ||
- `clamdscan.path` - only available on fallback | ||
### .get_version(callback) | ||
# Basic Usage Example | ||
This method allows you to determine the version of clamav you are interfacing with | ||
For the sake of brevity, all the examples in the [API](#api) section will be shortened to just the relevant parts related specifically to that example. In those examples, we'll assume you already have an instance of the `clamscan` object. Since initializing the module returns a promise, you'll have to resolve that promise to get an instance of the `clamscan` object. | ||
#### Parameters: | ||
**Below is the _full_ example of how you could get that instance and run some methods:** | ||
* `callback` (function) (optional) Will be called when the scan is complete. It takes 2 parameters: | ||
* `err` (object or null) A standard javascript Error object (null if no error) | ||
* `version` (string) The version of the clamav server you're interfacing with | ||
```javascript | ||
const NodeClam = require('clamscan'); | ||
const ClamScan = new NodeClam().init(options); | ||
#### Example: | ||
// Get instance by resolving ClamScan promise object | ||
ClamScan.then(async clamscan => { | ||
try { | ||
// You can re-use the `clamscan` object as many times as you want | ||
const version = await clamscan.get_version(); | ||
console.log(`ClamAV Version: ${version}`); | ||
const {is_infected, file, viruses} = await clamscan.is_infected('/some/file.zip'); | ||
if (is_infected) console.log(`${file} is infected with ${viruses}!`); | ||
} catch (err) { | ||
// Handle any errors raised by the code in the try block | ||
} | ||
}).catch(err => { | ||
// Handle errors that may have occurred during initialization | ||
}); | ||
``` | ||
**If you're writing your code within an async function, getting an instance can be one less step:** | ||
```javascript | ||
clam.get_version(function(err, version) { | ||
if (err) { | ||
console.log(err); | ||
const NodeClam = require('clamscan'); | ||
async some_function() { | ||
try { | ||
// Get instance by resolving ClamScan promise object | ||
const clamscan = await new NodeClam().init(options); | ||
const {good_files, bad_files} = await clamscan.scan_dir('/foo/bar'); | ||
} catch (err) { | ||
// Handle any errors raised by the code in the try block | ||
} | ||
console.log("ClamAV Version: " + version); | ||
} | ||
some_function(); | ||
``` | ||
# API | ||
Complete/functional examples for various use-cases can be found in the [examples folder](https://github.com/kylefarris/clamscan/tree/master/examples). | ||
[]() | ||
## .get_version([callback]) | ||
This method allows you to determine the version of ClamAV you are interfacing with. It supports a callback and Promise API. If no callback is supplied, a Promise will be returned. | ||
### Parameters: | ||
- `callback` (function) (optional) Will be called when the scan is complete. It receives 2 parameters: | ||
- `err` (object or null) A standard javascript Error object (null if no error) | ||
- `version` (string) The version of the clamav server you're interfacing with | ||
### Returns: | ||
- Promise | ||
- Promise resolution returns: `version` (string) The version of the clamav server you're interfacing with | ||
### Callback Example: | ||
```javascript | ||
clamscan.get_version((err, version) => { | ||
if (err) return console.error(err); | ||
console.log(`ClamAV Version: ${version}`); | ||
}); | ||
``` | ||
### Promise Example: | ||
### .is_infected(file_path, callback) | ||
```javascript | ||
clamscan.get_version().then(version => { | ||
console.log(`ClamAV Version: ${version}`); | ||
}).catch(err => { | ||
console.error(err); | ||
}); | ||
``` | ||
This method allows you to scan a single file. | ||
[]() | ||
#### Alias: | ||
## .is_infected(file_path[,callback]) | ||
This method allows you to scan a single file. It supports a callback and Promise API. If no callback is supplied, a Promise will be returned. This method will likely be the most common use-case for this module. | ||
### Alias: | ||
`.scan_file` | ||
#### Parameters: | ||
### Parameters: | ||
* `file_path` (string) Represents a path to the file to be scanned. | ||
* `callback` (function) (optional) Will be called when the scan is complete. It takes 3 parameters: | ||
* `err` (object or null) A standard javascript Error object (null if no error) | ||
* `file` (string) The original `file_path` passed into the `is_infected` method. | ||
* `is_infected` (boolean) __True__: File is infected; __False__: File is clean. __NULL__: Unable to scan. | ||
- `file_path` (string) Represents a path to the file to be scanned. | ||
- `callback` (function) (optional) Will be called when the scan is complete. It takes 3 parameters: | ||
- `err` (object or null) A standard javascript Error object (null if no error) | ||
- `file` (string) The original `file_path` passed into the `is_infected` method. | ||
- `is_infected` (boolean) **True**: File is infected; **False**: File is clean. **NULL**: Unable to scan. | ||
- `viruses` (array) An array of any viruses found in the scanned file. | ||
#### Example: | ||
### Returns: | ||
- Promise | ||
- Promise resolution returns: `result` (object): | ||
- `file` (string) The original `file_path` passed into the `is_infected` method. | ||
- `is_infected` (boolean) **True**: File is infected; **False**: File is clean. **NULL**: Unable to scan. | ||
- `viruses` (array) An array of any viruses found in the scanned file. | ||
### Callback Example: | ||
```javascript | ||
clam.is_infected('/a/picture/for_example.jpg', function(err, file, is_infected) { | ||
if (err) { | ||
console.log(err); | ||
return false; | ||
} | ||
clamscan.is_infected('/a/picture/for_example.jpg', (err, file, is_infected, viruses) => { | ||
if (err) return console.error(err); | ||
if (is_infected) { | ||
res.send({msg: "File is infected!"}); | ||
} else { | ||
res.send({msg: "File is clean!"}); | ||
console.log(`${file} is infected with ${viruses.join(', ')}.`); | ||
} | ||
@@ -213,40 +337,75 @@ }); | ||
### .scan_dir(dir_path, end_callback, file_callback) | ||
### Promise Example: | ||
```javascript | ||
clamscan.is_infected('/a/picture/for_example.jpg').then(result => { | ||
const {file, is_infected, viruses} = result; | ||
if (is_infected) console.log(`${file} is infected with ${viruses.join(', ')}.`); | ||
}).then(err => { | ||
console.error(err); | ||
}) | ||
``` | ||
### Async/Await Example: | ||
```javascript | ||
const {file, is_infected, viruses} = await clamscan.is_infected('/a/picture/for_example.jpg'); | ||
``` | ||
[]() | ||
## .scan_dir(dir_path[,end_callback[,file_callback]]) | ||
Allows you to scan an entire directory for infected files. This obeys your `recursive` option even for `clamdscan` which does not have a native way to turn this feature off. If you have multiple paths, send them in an array to `scan_files`. | ||
__TL;DR:__ For maximum speed, don't supply a `file_callback`. | ||
**TL;DR:** For maximum speed, don't supply a `file_callback`. | ||
If you choose to supply a `file_callback`, the scan will run a little bit slower (depending on number of files to be scanned) for `clamdscan`. If you are using `clamscan`, while it will work, I'd highly advise you to NOT pass a `file_callback`... it will run incredibly slow. | ||
#### NOTE: | ||
### NOTE: | ||
The `good_files` and `bad_files` parameters of the `end_callback` callback in this method will only contain the directories that were scanned in __all__ __but__ the following scenarios: | ||
The `good_files` and `bad_files` parameters of the `end_callback` callback in this method will only contain the directories that were scanned in **all** **but** the following scenarios: | ||
* A `file_callback` callback is provided, and `scan_recursively` is set to _true_. | ||
* The scanner is set to `clamdscan` and `scan_recursively` is set to _false_. | ||
- A `file_callback` callback is provided, and `scan_recursively` is set to _true_. | ||
- The scanner is set to `clamdscan` and `scan_recursively` is set to _false_. | ||
#### Parameters | ||
### Parameters | ||
* `dir_path` (string) (required) Full path to the directory to scan. | ||
* `end_callback` (function) (required) Will be called when the entire directory has been completely scanned. This callback takes 3 parameters: | ||
* `err` (object) A standard javascript Error object (null if no error) | ||
* `good_files` (array) List of the full paths to all files that are _clean_. | ||
* `bad_files` (array) List of the full paths to all files that are _infected_. | ||
* `file_callback` (function) (optional) Will be called after each file in the directory has been scanned. This is useful for keeping track of the progress of the scan. This callback takes 3 parameters: | ||
* `err` (object or null) A standard Javascript Error object (null if no error) | ||
* `file` (string) Path to the file that just got scanned. | ||
* `is_infected` (boolean) __True__: File is infected; __False__: File is clean. __NULL__: Unable to scan file. | ||
- `dir_path` (string) (required) Full path to the directory to scan. | ||
- `end_callback` (function) (optional) Will be called when the entire directory has been completely scanned. This callback takes 3 parameters: | ||
#### Example | ||
- `err` (object) A standard javascript Error object (null if no error) | ||
- `good_files` (array) List of the full paths to all files that are _clean_. | ||
- `bad_files` (array) List of the full paths to all files that are _infected_. | ||
- `viruses` (array) List of all the viruses found (feature request: associate to the bad files). | ||
- `file_callback` (function) (optional) Will be called after each file in the directory has been scanned. This is useful for keeping track of the progress of the scan. This callback takes 3 parameters: | ||
- `err` (object or null) A standard Javascript Error object (null if no error) | ||
- `file` (string) Path to the file that just got scanned. | ||
- `is_infected` (boolean) **True**: File is infected; **False**: File is clean. **NULL**: Unable to scan file. | ||
### Returns: | ||
- Promise | ||
- Promise resolution returns: `result` (object): | ||
- `path` (string) The original `dir_path` passed into the `scan_dir` method. | ||
- `is_infected` (boolean) **True**: File is infected; **False**: File is clean. **NULL**: Unable to scan. | ||
- `good_files` (array) List of the full paths to all files that are _clean_. | ||
- `bad_files` (array) List of the full paths to all files that are _infected_. | ||
- `viruses` (array) List of all the viruses found (feature request: associate to the bad files). | ||
### Callback Example | ||
```javascript | ||
clam.scan_dir('/some/path/to/scan', function(err, good_files, bad_files) { | ||
if (!err) { | ||
if (bad_files.length > 0) { | ||
res.send({msg: "Your directory was infected. The offending files have been quarantined."}); | ||
} else { | ||
res.send({msg: "Everything looks good! No problems here!."}); | ||
} | ||
clamscan.scan_dir('/some/path/to/scan', (err, good_files, bad_files, viruses) { | ||
if (err) return console.error(err); | ||
if (bad_files.length > 0) { | ||
console.log(`${path} was infected. The offending files (${bad_files.join (', ')}) have been quarantined.`); | ||
console.log(`Viruses Found: ${viruses.join(', ')}`); | ||
} else { | ||
// Do some error handling | ||
console.log("Everything looks good! No problems here!."); | ||
} | ||
@@ -256,26 +415,59 @@ }); | ||
### .scan_files(files, end_callback, file_callback) | ||
### Promise Example | ||
```javascript | ||
clamscan.scan_dir('/some/path/to/scan').then(results => { | ||
const {path, is_infected, good_files, bad_files, viruses} = results; | ||
//... | ||
}).catch(err => { | ||
return console.error(err); | ||
}); | ||
``` | ||
### Async/Await Example | ||
```javascript | ||
const {path, is_infected, good_files, bad_files, viruses} = await clamscan.scan_dir('/some/path/to/scan'); | ||
``` | ||
[]() | ||
## .scan_files(files[,end_callback[,file_callback]]) | ||
This allows you to scan many files that might be in different directories or maybe only certain files of a single directory. This is essentially a wrapper for `is_infected` that simplifies the process of scanning many files or directories. | ||
#### Parameters | ||
### Parameters | ||
* `files` (array) A list of strings representing full paths to files you want scanned. | ||
* `end_callback` (function) Will be called when the entire directory has been completely scanned. This callback takes 3 parameters: | ||
* `err` (object) A standard javascript Error object (null if no error) | ||
* `good_files` (array) List of the full paths to all files that are _clean_. | ||
* `bad_files` (array) List of the full paths to all files that are _infected_. | ||
* `file_callback` (function) Will be called after each file in the directory has been scanned. This is useful for keeping track of the progress of the scan. This callback takes 3 parameters: | ||
* `err` (object or null)A standard javascript Error object (null if no error) | ||
* `file` (string) Path to the file that just got scanned. | ||
* `is_infected` (boolean) __True__: File is infected; __False__: File is clean. __NULL__: Unable to scan file. | ||
- `files` (array) (optional) A list of strings representing full paths to files you want scanned. If not supplied, the module will check for a `file_list` config option. If neither is found, the method will throw an error. | ||
- `end_callback` (function) (optional) Will be called when the entire list of files has been completely scanned. This callback takes 3 parameters: | ||
#### Example | ||
- `err` (object or null) A standard JavaScript Error object (null if no error) | ||
- `good_files` (array) List of the full paths to all files that are _clean_. | ||
- `bad_files` (array) List of the full paths to all files that are _infected_. | ||
- `file_callback` (function) (optional) Will be called after each file in the list has been scanned. This is useful for keeping track of the progress of the scan. This callback takes 3 parameters: | ||
- `err` (object or null) A standard JavaScript Error object (null if no error) | ||
- `file` (string) Path to the file that just got scanned. | ||
- `is_infected` (boolean) **True**: File is infected; **False**: File is clean. **NULL**: Unable to scan file. | ||
### Returns | ||
- Promise | ||
- Promise resolution returns: `result` (object): | ||
- `good_files` (array) List of the full paths to all files that are _clean_. | ||
- `bad_files` (array) List of the full paths to all files that are _infected_. | ||
- `errors` (object) Per-file errors keyed by the filename in which the error happened. (ex. `{'foo.txt': Error}`) | ||
- `viruses` (array) List of all the viruses found (feature request: associate to the bad files). | ||
### Callback Example | ||
```javascript | ||
var scan_status = { | ||
const scan_status = { | ||
good: 0, | ||
bad: 0 | ||
}; | ||
var files = [ | ||
const files = [ | ||
'/path/to/file/1.jpg', | ||
@@ -285,45 +477,108 @@ '/path/to/file/2.mov', | ||
]; | ||
clam.scan_files(files, function(err, good_files, bad_files) { | ||
if (!err) { | ||
if (bad_files.length > 0) { | ||
res.send({ | ||
msg: good_files.length + ' files were OK. ' + bad_files.length + ' were infected!', | ||
bad: bad_files, | ||
good: good_files | ||
}); | ||
} else { | ||
res.send({msg: "Everything looks good! No problems here!."}); | ||
} | ||
clamscan.scan_files(files, (err, good_files, bad_files, viruses) => { | ||
if (err) return console.error(err); | ||
if (bad_files.length > 0) { | ||
console.log({ | ||
msg: `${good_files.length} files were OK. ${bad_files.length} were infected!`, | ||
bad_files, | ||
good_files, | ||
viruses, | ||
}); | ||
} else { | ||
// Do some error handling | ||
res.send({msg: "Everything looks good! No problems here!."}); | ||
} | ||
}, function(err, file, is_infected) { | ||
if (is_infected) { | ||
scan_status.bad++; | ||
} else { | ||
scan_status.good++; | ||
} | ||
console.log("Scan Status: " + (scan_status.bad + scan_status.good) + "/" + files.length); | ||
}, (err, file, is_infected, viruses) => { | ||
;(is_infected ? scan_status.bad++ : scan_status.good++); | ||
console.log(`${file} is ${(is_infected ? `infected with ${viruses}` : 'ok')}.`); | ||
console.log('Scan Status: ', `${(scan_status.bad + scan_status.good)}/${files.length}`); | ||
}); | ||
``` | ||
### .scan_stream(stream, callback) | ||
### Promise Example | ||
This method allows you to scan a binary stream. __NOTE__: This method will only work if the scanning method is a TCP or UNIX Domain socket. In other words, this will not work if you are using the local binary method. | ||
**Note:** There is currently no way to get per-file notifications with the Promise API. | ||
#### Parameters | ||
```javascript | ||
clamscan.scan_files(files).then(results => { | ||
const {good_files, bad_files, errors, viruses} = results; | ||
// ... | ||
}).catch(err => { | ||
console.error(err); | ||
}) | ||
``` | ||
* `stream` (stream) A nodejs stream object | ||
* `callback` (function) Will be called after the stream has been scanned (or attempted to be scanned): | ||
* `err` (object or null) A standard javascript Error object (null if no error) | ||
* `is_infected` (boolean) __True__: Stream is infected; __False__: Stream is clean. __NULL__: Unable to scan file. | ||
### Async/Await Example | ||
#### Examples | ||
```javascript | ||
const {good_files, bad_files, errors, viruses} = await clamscan.scan_files(files); | ||
``` | ||
__Contrived Example:__ | ||
#### Scanning files listed in file_list | ||
If this modules is configured with a valid path to a file containing a newline-delimited list of files, it will use the list in that file when scanning if the first paramter passed is falsy. | ||
**Files List Document:** | ||
``` | ||
/some/path/to/file.zip | ||
/some/other/path/to/file.exe | ||
/one/more/file/to/scan.rb | ||
``` | ||
**Script:** | ||
```javascript | ||
var Readable = require('stream').Readable; | ||
var rs = Readable(); | ||
const ClamScan = new NodeClam().init({ | ||
file_list: '/path/to/file_list.txt' | ||
}); | ||
ClamScan.then(async clamscan => { | ||
// Supply nothing to first parameter to use `file_list` | ||
const {good_files, bad_files, errors, viruses} = await clamscan.scan_files(); | ||
}); | ||
``` | ||
[]() | ||
## .scan_stream(stream[,callback]) | ||
This method allows you to scan a binary stream. **NOTE**: This method will only work if you've configured the module to allow the use of a TCP or UNIX Domain socket. In other words, this will not work if you only have access to a local ClamAV binary. | ||
### Parameters | ||
- `stream` (stream) A readable stream object | ||
- `callback` (function) (optional) Will be called after the stream has been scanned (or attempted to be scanned): | ||
- `err` (object or null) A standard JavaScript Error object (null if no error) | ||
- `is_infected` (boolean) **True**: Stream is infected; **False**: Stream is clean. **NULL**: Unable to scan file. | ||
### Returns | ||
- Promise | ||
- Promise resolution returns: `result` (object): | ||
- `good_files` (array) List of the full paths to all files that are _clean_. | ||
- `bad_files` (array) List of the full paths to all files that are _infected_. | ||
- `errors` (object) Per-file errors keyed by the filename in which the error happened. (ex. `{'foo.txt': Error}`) | ||
- `viruses` (array) List of all the viruses found (feature request: associate to the bad files). | ||
### Examples | ||
**Callback Example:** | ||
```javascript | ||
const NodeClam = require('clamscan'); | ||
// You'll need to specify your socket or TCP connection info | ||
const clamscan = new NodeClam().init({ | ||
clamdscan: { | ||
socket: '/var/run/clamd.scan/clamd.sock', | ||
host: '127.0.0.1', | ||
port: 3310, | ||
} | ||
}); | ||
const Readable = require('stream').Readable; | ||
const rs = Readable(); | ||
rs.push('foooooo'); | ||
@@ -333,83 +588,80 @@ rs.push('barrrrr'); | ||
clam.scan_stream(stream, function(err, is_infected) { | ||
if (err) { | ||
console.log(err); | ||
} else { | ||
if (is_infected) { | ||
console.log("Stream is infected! Booo!"); | ||
} else { | ||
console.log("Stream is not infected! Yay!"); | ||
} | ||
} | ||
clamscan.scan_stream(stream, (err, is_infected) => { | ||
if (err) return console.error(err); | ||
if (is_infected) return console.log("Stream is infected! Booo!"); | ||
console.log("Stream is not infected! Yay!"); | ||
}); | ||
``` | ||
__Slightly More "Realistic" Example:__ | ||
**Promise Example:** | ||
This example shows how to scan every HTTP request as it comes in to your server using middleware funcionality in express (definitely not advisable!). | ||
```javascript | ||
clamscan.scan_stream(stream).then(is_infected => { | ||
if (is_infected) return console.log("Stream is infected! Booo!"); | ||
console.log("Stream is not infected! Yay!"); | ||
}).catch(err => { | ||
console.error(err); | ||
}; | ||
``` | ||
**Promise Example:** | ||
```javascript | ||
var express = require('express'); | ||
var app = express(); | ||
const is_infected = await clamscan.scan_stream(stream); | ||
``` | ||
app.use(function (req, res, next) { | ||
clam.scan_stream(req, function(err, is_infected) { | ||
if (err) { | ||
console.log("Unable to scan request for viruses!", err}); | ||
res.status(500).send({error: "There was an error accepting your request!"}); | ||
} else { | ||
if (is_infected) { | ||
res.status(500).send({error: "Your request is virus-infected!"); | ||
} else { | ||
next(); | ||
} | ||
} | ||
}); | ||
}); | ||
[]() | ||
app.get('/', function(req, res) { | ||
// should never get here if request has virus. | ||
}); | ||
## .passthrough() | ||
var server = app.listen(3000); | ||
``` | ||
The `passthrough` method returns a PassthroughStream object which allows you pipe a ReadbleStream through it and on to another output. In the case of this module's passthrough implementation, it's actually forking the data to also go to ClamAV via TCP or Domain Sockets. Each data chunk is only passed on to the output if that chunk was successfully sent to and received by ClamAV. The PassthroughStream object returned from this method has a special event that is emitted when ClamAV finishes scanning the streamed data so that you can decide if there's anything you need to do with the final output destination (ex. delete a file or S3 object). | ||
#### Scanning files listed in file list | ||
Typically, a file is uploaded to the local filesytem and then subsequently scanned. In this case, you have to wait for the upload to complete _and then_ for the scan to complete. This method could theoretically speed up user uploads intended to be scanned by up to 2x because the files are simultaneously scanned and written to any WriteableStream output (examples: filesystem, S3, gzip, etc...). | ||
If this modules is configured with a valid path to a file containing a newline-delimited list of files, it will use the list in that file when scanning if the first paramter passed is falsy. | ||
As for the theoretical gains, your mileage my vary and I'd love to hear feedback on this to see where things can still be improved. | ||
__Files List:__ | ||
Pleae note that 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 (a practical working example can be found in the examples directory of this module): | ||
``` | ||
/some/path/to/file.zip | ||
/some/other/path/to/file.exe | ||
/one/more/file/to/scan.rb | ||
``` | ||
### Example | ||
__Script:__ | ||
```javascript | ||
var clam = require('clamscan')({ | ||
file_list: '/path/to/file_list.txt' | ||
}); | ||
const NodeClam = require('clamscan'); | ||
clam.scan_files(null, function(err, good_files, bad_files) { | ||
// doo stuff... | ||
// You'll need to specify your socket or TCP connection info | ||
const clamscan = new NodeClam().init({ | ||
clamdscan: { | ||
socket: '/var/run/clamd.scan/clamd.sock', | ||
host: '127.0.0.1', | ||
port: 3310, | ||
} | ||
}); | ||
``` | ||
#### Changing Configuration After Instantiation | ||
// For example's sake, we're using the RequestJS module | ||
const request = require('request'); | ||
You can set settings directly on an instance of this module using the following syntax: | ||
// Get a readable stream for a URL request | ||
const input = request.get(some_url); | ||
```javascript | ||
var clam = require('clamscan')({ /** Some configs here... */}); | ||
// Create a writable stream to a local file | ||
const output = fs.createWriteStream(some_local_file); | ||
// will quarantine files | ||
clam.settings.quarantine_infected = true; | ||
clam.is_infected('/some/file.txt'); | ||
// Get instance of this module's PassthroughStream object | ||
const av = clamscan.passthrough(); | ||
// will not quarantine files | ||
clam.settings.quarantine_infected = false; | ||
clam.is_infected('/some/file.txt'); | ||
// 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 => { | ||
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 | ||
}); | ||
// NOTE: no errors are being handled in this example but standard errors will be emitted according to NodeJS's Stream specifications | ||
``` | ||
@@ -419,15 +671,20 @@ | ||
## Contribute | ||
# Contribute | ||
Got a missing feature you'd like to use? Found a bug? Go ahead and fork this repo, build the feature and issue a pull request. | ||
## Resources used to help develop this module: | ||
# Resources used to help develop this module: | ||
* https://stuffivelearned.org/doku.php?id=apps:clamav:general:remoteclamdscan | ||
* http://cpansearch.perl.org/src/JMEHNLE/ClamAV-Client-0.11/lib/ClamAV/Client.pm | ||
* https://github.com/yongtang/clamav.js | ||
- <https://stuffivelearned.org/doku.php?id=apps:clamav:general:remoteclamdscan> | ||
- <http://cpansearch.perl.org/src/JMEHNLE/ClamAV-Client-0.11/lib/ClamAV/Client.pm> | ||
- <https://github.com/yongtang/clamav.js> | ||
- <https://nodejs.org/dist/latest-v10.x/docs/api/stream.html> | ||
- <https://manpages.debian.org/jessie/clamav-daemon/clamd.8.en.html> | ||
### Items for version 1.0 release: | ||
* Slight change of API to allow for a completely asynchronous module (ie, removal of all `fs.xxSync` items). | ||
* Allow the ability to scan Buffers, Streams, and Strings directly. | ||
[node-image]: https://img.shields.io/node/v/clamscan.svg | ||
[node-url]: https://nodejs.org/en/download | ||
[npm-downloads-image]: https://img.shields.io/npm/dm/clamscan.svg | ||
[npm-url]: https://npmjs.org/package/clamscan | ||
[npm-version-image]: https://img.shields.io/npm/v/clamscan.svg | ||
[travis-image]: https://img.shields.io/travis/kylefarris/node-querybuilder/master.svg | ||
[travis-url]: https://travis-ci.org/kylefarris/clamscan |
@@ -458,2 +458,35 @@ const fs = require('fs'); | ||
}); | ||
it('should respond with an empty array of viruses when file is NOT infected', done => { | ||
clamscan.is_infected(good_scan_file, (err, file, is_infected, viruses) => { | ||
check(done, () => { | ||
expect(err).to.not.be.instanceof(Error); | ||
expect(viruses).to.be.an('array'); | ||
expect(viruses).to.have.length(0); | ||
}); | ||
}); | ||
}); | ||
it('should respond with name of virus when file is infected', done => { | ||
request(fake_virus_url, (error, response, body) => { | ||
if (!error && response.statusCode == 200) { | ||
fs.writeFileSync(bad_scan_file, body); | ||
clamscan.is_infected(bad_scan_file, (err, file, is_infected, viruses) => { | ||
check(done, () => { | ||
expect(viruses).to.be.an('array'); | ||
expect(viruses).to.have.length(1); | ||
expect(viruses[0]).to.match(/^Eicar\-Test\-Signature/); | ||
if (fs.existsSync(bad_scan_file)) { | ||
fs.unlinkSync(bad_scan_file); | ||
} | ||
}); | ||
}); | ||
} else { | ||
console.log("Could not download test virus file!"); | ||
console.error(error); | ||
} | ||
}); | ||
}); | ||
}); | ||
@@ -494,3 +527,14 @@ | ||
it('should respond with TRUE when non-archive file is infected', done => { | ||
it('should respond with an empty array of viruses when file is NOT infected', done => { | ||
clamscan.is_infected(good_scan_file).then(result => { | ||
const {viruses} = result; | ||
expect(viruses).to.be.an('array'); | ||
expect(viruses).to.have.length(0); | ||
done(); | ||
}).catch(err => { | ||
done(err); | ||
}); | ||
}); | ||
it('should respond with name of virus when file is infected', done => { | ||
prequest(fake_virus_url).then(result => { | ||
@@ -508,5 +552,6 @@ const {body, statusCode} = result; | ||
}).then(result => { | ||
const {file, is_infected} = result; | ||
expect(is_infected).to.be.a('boolean'); | ||
expect(is_infected).to.eql(true); | ||
const {viruses} = result; | ||
expect(viruses).to.be.an('array'); | ||
expect(viruses).to.have.length(1); | ||
expect(viruses[0]).to.match(/^Eicar\-Test\-Signature/); | ||
@@ -556,2 +601,29 @@ done(); | ||
}); | ||
it('should respond with an empty array of viruses when file is NOT infected', async () => { | ||
try { | ||
const {viruses} = await clamscan.is_infected(good_scan_file); | ||
expect(viruses).to.be.an('array'); | ||
expect(viruses).to.have.length(0); | ||
} catch (err) { | ||
throw err; | ||
} | ||
}); | ||
it('should respond with name of virus when file is infected', async () => { | ||
const {body, statusCode} = await request(fake_virus_url); | ||
if (statusCode == 200 && body && typeof body === 'string') { | ||
fs.writeFileSync(bad_scan_file, body); | ||
try { | ||
const {viruses} = await clamscan.is_infected(bad_scan_file); | ||
expect(viruses).to.be.an('array'); | ||
expect(viruses).to.have.length(1); | ||
expect(viruses[0]).to.match(/^Eicar\-Test\-Signature/); | ||
} catch (err) { | ||
throw err; | ||
} finally { | ||
if (fs.existsSync(bad_scan_file)) fs.unlinkSync(bad_scan_file); | ||
} | ||
} | ||
}); | ||
}); | ||
@@ -574,3 +646,3 @@ }); | ||
it('should behave just like is_infected (callback)', done => { | ||
clamscan.scan_file(good_scan_file, (err, file, is_infected) => { | ||
clamscan.scan_file(good_scan_file, (err, file, is_infected, viruses) => { | ||
check(done, () => { | ||
@@ -581,2 +653,6 @@ expect(err).to.not.be.instanceof(Error); | ||
file.should.eql(good_scan_file); | ||
expect(is_infected).to.be.a('boolean'); | ||
expect(is_infected).to.eql(false); | ||
expect(viruses).to.be.an('array'); | ||
expect(viruses).to.have.length(0); | ||
}); | ||
@@ -587,6 +663,10 @@ }); | ||
clamscan.scan_file(good_scan_file).then(result => { | ||
const {file, is_infected} = result; | ||
const {file, is_infected, viruses} = result; | ||
expect(file).to.not.be.empty; | ||
file.should.be.a('string'); | ||
file.should.eql(good_scan_file); | ||
expect(is_infected).to.be.a('boolean'); | ||
expect(is_infected).to.eql(false); | ||
expect(viruses).to.be.an('array'); | ||
expect(viruses).to.have.length(0); | ||
done(); | ||
@@ -598,5 +678,10 @@ }).catch(err => { | ||
it('should behave just like is_infected (async/await)', async () => { | ||
const {file, is_infected} = await clamscan.scan_file(good_scan_file); | ||
const {file, is_infected, viruses} = await clamscan.scan_file(good_scan_file); | ||
expect(file).to.not.be.empty; | ||
expect(file).to.be.a('string'); | ||
expect(file).to.eql(good_scan_file); | ||
expect(is_infected).to.be.a('boolean'); | ||
expect(is_infected).to.eql(false); | ||
expect(viruses).to.be.an('array'); | ||
expect(viruses).to.have.length(0); | ||
}); | ||
@@ -839,2 +924,47 @@ }); | ||
}); | ||
it('should provide an empty array for the "viruses" parameter if no infected files are found', done => { | ||
clamscan.scan_files(good_scan_file, (err, good_files, bad_files, error_files, viruses) => { | ||
check(done, () => { | ||
expect(err).to.not.be.instanceof(Error); | ||
expect(viruses).to.be.an('array'); | ||
expect(viruses).to.have.length(0); | ||
}); | ||
}); | ||
}); | ||
it('should provide a list of viruses found if the any of the files in the list is infected', done => { | ||
request(fake_virus_url, (error, response, body) => { | ||
if (!error && response.statusCode == 200) { | ||
fs.writeFileSync(bad_scan_file, body); | ||
clamscan.scan_files([bad_scan_file, `${__dirname}/good_scan_dir/good_file_1.txt`], (err, good_files, bad_files, error_files, viruses) => { | ||
check(done, () => { | ||
expect(err).to.not.be.instanceof(Error); | ||
expect(good_files).to.not.be.empty; | ||
expect(good_files).to.be.an('array'); | ||
expect(good_files).to.have.length(1); | ||
expect(bad_files).to.not.be.empty; | ||
expect(bad_files).to.be.an('array'); | ||
expect(bad_files).to.have.length(1); | ||
expect(error_files).to.be.eql({}); | ||
expect(viruses).to.not.be.empty; | ||
expect(viruses).to.be.an('array'); | ||
expect(viruses).to.have.length(1); | ||
expect(viruses[0]).to.match(/^Eicar\-Test\-Signature/); | ||
if (fs.existsSync(bad_scan_file)) fs.unlinkSync(bad_scan_file); | ||
}); | ||
}); | ||
} else { | ||
console.log("Could not download test virus file!"); | ||
console.error(error); | ||
} | ||
}); | ||
}); | ||
}); | ||
@@ -942,2 +1072,27 @@ }); | ||
}); | ||
it('should supply an array with viruses found when directory has infected files', done => { | ||
request(fake_virus_url, (error, response, body) => { | ||
if (!error && response.statusCode == 200) { | ||
fs.writeFileSync(bad_scan_file, body); | ||
clamscan.scan_dir(bad_scan_dir, (err, good_files, bad_files, viruses) => { | ||
check(done, () => { | ||
expect(err).to.not.be.instanceof(Error); | ||
expect(viruses).to.not.be.empty; | ||
expect(viruses).to.be.an('array'); | ||
expect(viruses).to.have.length(1); | ||
expect(viruses[0]).to.match(/^Eicar\-Test\-Signature/); | ||
if (fs.existsSync(bad_scan_file)) fs.unlinkSync(bad_scan_file); | ||
}); | ||
}); | ||
} else { | ||
console.log("Could not download test virus file!"); | ||
console.error(error); | ||
} | ||
}); | ||
}); | ||
// TODO: Write tests for file_callback | ||
}); | ||
@@ -944,0 +1099,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
202104
2951
1
677