Code Monkey home page Code Monkey logo

clamscan's Introduction

NodeJS Clamscan Virus Scanning Utility

NPM Version NPM Downloads Node.js Version Test Suite

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

If you are using a version prior to 1.2.0, please upgrade! There was a security vulnerability in previous versions that can cause false negative in some edge cases. Specific details on how the attack could be implemented will not be disclosed here. Please update to 1.2.0 or greater ASAP. No breaking changes are included, only the security patch.

All older versions in NPM have been deprecated.

Version 1.0.0 Information

If you are migrating from v0.8.5 or less to v1.0.0 or greater, please read the release notes as there are some breaking changes (but also some awesome new features!).

Table of Contents

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.

Fedora-based distros:

sudo yum install clamav

Debian-based distros:

sudo apt-get install clamav clamav-daemon

For OS X, you can install clamav with brew:

sudo brew install clamav

To use ClamAV using TCP sockets

You will need access to either:

  1. A local UNIX Domain socket for a local instance of clamd
  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).

How to Install

npm install clamscan

License Info

Licensed under the MIT License:

Getting Started

All of the values listed in the example below represent the default values for their respective configuration item.

You can simply do this:

const NodeClam = require('clamscan');
const ClamScan = new NodeClam().init();

And, you'll be good to go.

BUT: If you want more control, you can specify all sorts of options.

const NodeClam = require('clamscan');
const ClamScan = new NodeClam().init({
    removeInfected: false, // If true, removes infected files
    quarantineInfected: false, // False: Don't quarantine, Path: Moves files to this place.
    scanLog: null, // Path to a writeable log file to write scan results into
    debugMode: false, // Whether or not to log info/debug/error msgs to the console
    fileList: null, // path to file containing list of files to scan (for scanFiles method)
    scanRecursively: true, // If true, deep scan folders recursively
    clamscan: {
        path: '/usr/bin/clamscan', // Path to clamscan binary on your server
        db: null, // Path to a custom virus definition database
        scanArchives: true, // If true, scan archives (ex. zip, rar, tar, dmg, iso, etc...)
        active: true // If true, this module will consider using the clamscan binary
    },
    clamdscan: {
        socket: false, // Socket file for connecting via TCP
        host: false, // IP of host to connect to TCP interface
        port: false, // Port of host to use when connecting via TCP interface
        timeout: 60000, // Timeout for scanning files
        localFallback: true, // Use local preferred binary to scan if socket/tcp fails
        path: '/usr/bin/clamdscan', // Path to the clamdscan binary on your server
        configFile: null, // Specify config file if it's in an unusual place
        multiscan: true, // Scan using all available cores! Yay!
        reloadDb: false, // If true, will re-load the DB on every call (slow)
        active: true, // If true, this module will consider using the clamdscan binary
        bypassTest: false, // Check to see if socket is available when applicable
        tls: false, // Use plaintext TCP to connect to clamd
    },
    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 proper-looking values could be):

const NodeClam = require('clamscan');
const ClamScan = new NodeClam().init({
    removeInfected: true, // Removes files if they are infected
    quarantineInfected: '~/infected/', // Move file here. removeInfected must be FALSE, though.
    scanLog: '/var/log/node-clam', // You're a detail-oriented security professional.
    debugMode: true, // This will put some debug info in your js console
    fileList: '/home/webuser/scanFiles.txt', // path to file containing list of files to scan
    scanRecursively: false, // Choosing false here will save some CPU cycles
    clamscan: {
        path: '/usr/bin/clam', // I dunno, maybe your clamscan is just call "clam"
        scanArchives: false, // Choosing false here will save some CPU cycles
        db: '/usr/bin/better_clam_db', // Path to a custom virus definition database
        active: false // you don't want to use this at all because it's evil
    },
    clamdscan: {
        socket: '/var/run/clamd.scan/clamd.sock', // This is pretty typical
        host: '127.0.0.1', // If you want to connect locally but not through socket
        port: 12345, // Because, why not
        timeout: 300000, // 5 minutes
        localFallback: false, // Do no fail over to binary-method of scanning
        path: '/bin/clamdscan', // Special path to the clamdscan binary on your server
        configFile: '/etc/clamd.d/daemon.conf', // A fairly typical config location
        multiscan: false, // You hate speed and multi-threaded awesome-sauce
        reloadDb: 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
        bypassTest: true, // Don't check to see if socket is available. You should probably never set this to true.
        tls: true, // Connect to clamd over TLS
    },
    preference: 'clamscan' // If clamscan is found and active, it will be used by default
});

NOTE: If a valid port is provided but no host value is provided, the clamscan will assume 'localhost' for host.

A note about using this module via sockets or TCP

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.localFallback: 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):

    • removeInfected - remote clamd service config will dictate this
    • quarantineInfected - remote clamd service config will dictate this
    • scanLog - remote clamd service config will dictate this
    • fileList - this simply won't be available
    • clamscan.db - only available on fallback
    • clamscan.scanArchives - only available on fallback
    • clamscan.path - only available on fallback
    • clamdscan.configFile - only available on fallback
    • clamdscan.path - only available on fallback

Basic Usage Example

For the sake of brevity, all the examples in the 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.

Below is the full example of how you could get that instance and run some methods:

const NodeClam = require('clamscan');
const ClamScan = new NodeClam().init(options);

// 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.getVersion();
        console.log(`ClamAV Version: ${version}`);

        const {isInfected, file, viruses} = await clamscan.isInfected('/some/file.zip');
        if (isInfected) 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:

const NodeClam = require('clamscan');

async some_function() {
    try {
        // Get instance by resolving ClamScan promise object
        const clamscan = await new NodeClam().init(options);
        const {goodFiles, badFiles} = await clamscan.scanDir('/foo/bar');
    } catch (err) {
        // Handle any errors raised by the code in the try block
    }
}

some_function();

API

Complete/functional examples for various use-cases can be found in the examples folder.

.getVersion([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

clamscan.getVersion((err, version) => {
    if (err) return console.error(err);
    console.log(`ClamAV Version: ${version}`);
});

Promise Example

clamscan.getVersion().then(version => {
    console.log(`ClamAV Version: ${version}`);
}).catch(err => {
    console.error(err);
});

.isInfected(filePath[,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

  • filePath (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 filePath passed into the isInfected method.
    • isInfected (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.

Returns

  • Promise

    • Promise resolution returns: result (object):

      • file (string) The original filePath passed into the isInfected method.
      • isInfected (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

clamscan.isInfected('/a/picture/for_example.jpg', (err, file, isInfected, viruses) => {
    if (err) return console.error(err);

    if (isInfected) {
        console.log(`${file} is infected with ${viruses.join(', ')}.`);
    }
});

Promise Example

clamscan.isInfected('/a/picture/for_example.jpg').then(result => {
    const {file, isInfected, viruses} =  result;
    if (isInfected) console.log(`${file} is infected with ${viruses.join(', ')}.`);
}).then(err => {
    console.error(err);
})

Async/Await Example

const {file, isInfected, viruses} = await clamscan.isInfected('/a/picture/for_example.jpg');

.scanDir(dirPath[,endCallback[,fileCallback]])

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 scanFiles.

TL;DR: For maximum speed, don't supply a fileCallback.

If you choose to supply a fileCallback, 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 fileCallback... it will run incredibly slow.

NOTE

The goodFiles parameter of the endCallback callback in this method will only contain the directory that was scanned in all but the following scenarios:

  • A fileCallback callback is provided, and scanRecursively is set to true.
  • The scanner is set to clamdscan and scanRecursively is set to false.
  • The scanned directory contains 1 or more viruses. In this case, the goodFiles array will be empty.

There will, however, be a total count of the good files which is calculated by determining the total number of files scanned and subtracting the number of bad files from that count. We simply can't provide a list of all good files due to the potential large memory usage implications of scanning a directory with, for example, millions of files.

Parameters

  • dirPath (string) (required) Full path to the directory to scan.

  • endCallback (function) (optional) 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)
    • goodFiles (array) An empty array if path is infected. An array containing the directory name that was passed in if clean.
    • badFiles (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).
    • numGoodFiles (number) Number of files that were found to be clean.
  • fileCallback (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.
    • isInfected (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 scanDir method.
      • isInfected (boolean) True: File is infected; False: File is clean. NULL: Unable to scan.
      • goodFiles (array) An empty array if path is infected. An array containing the directory name that was passed in if clean.
      • badFiles (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).
      • numGoodFiles (number) Number of files that were found to be clean.

Callback Example

clamscan.scanDir('/some/path/to/scan', (err, goodFiles, badFiles, viruses, numGoodFiles) {
    if (err) return console.error(err);

    if (badFiles.length > 0) {
        console.log(`${path} was infected. The offending files (${badFiles.join (', ')}) have been quarantined.`);
        console.log(`Viruses Found: ${viruses.join(', ')}`);
    } else {
        console.log(`${goodFiles[0]} looks good! ${numGoodFiles} file scanned and no problems found!.`);
    }
});

Promise Example

clamscan.scanDir('/some/path/to/scan').then(results => {
    const { path, isInfected, goodFiles, badFiles, viruses, numGoodFiles } = results;
    //...
}).catch(err => {
    return console.error(err);
});

Async/Await Example

const { path, isInfected, goodFiles, badFiles, viruses, numGoodFiles } = await clamscan.scanDir('/some/path/to/scan');

.scanFiles(files[,endCallback[,fileCallback]])

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 isInfected that simplifies the process of scanning many files or directories.

Parameters

  • files (array) (optional) A list of strings representing full paths to files you want scanned. If not supplied, the module will check for a fileList config option. If neither is found, the method will throw an error.

  • endCallback (function) (optional) Will be called when the entire list of files has been completely scanned. This callback takes 3 parameters:

    • err (object or null) A standard JavaScript Error object (null if no error)
    • goodFiles (array) List of the full paths to all files that are clean.
    • badFiles (array) List of the full paths to all files that are infected.
  • fileCallback (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.
    • isInfected (boolean) True: File is infected; False: File is clean. NULL: Unable to scan file.

Returns

  • Promise

    • Promise resolution returns: result (object):

      • goodFiles (array) List of the full paths to all files that are clean.
      • badFiles (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

const scan_status = { good: 0, bad: 0 };
const files = [
    '/path/to/file/1.jpg',
    '/path/to/file/2.mov',
    '/path/to/file/3.rb'
];
clamscan.scanFiles(files, (err, goodFiles, badFiles, viruses) => {
    if (err) return console.error(err);
    if (badFiles.length > 0) {
        console.log({
            msg: `${goodFiles.length} files were OK. ${badFiles.length} were infected!`,
            badFiles,
            goodFiles,
            viruses,
        });
    } else {
        res.send({msg: "Everything looks good! No problems here!."});
    }
}, (err, file, isInfected, viruses) => {
    ;(isInfected ? scan_status.bad++ : scan_status.good++);
    console.log(`${file} is ${(isInfected ? `infected with ${viruses}` : 'ok')}.`);
    console.log('Scan Status: ', `${(scan_status.bad + scan_status.good)}/${files.length}`);
});

Promise Example

Note: There is currently no way to get per-file notifications with the Promise API.

clamscan.scanFiles(files).then(results => {
    const { goodFiles, badFiles, errors, viruses } = results;
    // ...
}).catch(err => {
    console.error(err);
})

Async/Await Example

const { goodFiles, badFiles, errors, viruses } = await clamscan.scanFiles(files);

Scanning files listed in fileList

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:

const ClamScan = new NodeClam().init({
    fileList: '/path/to/fileList.txt'
});

ClamScan.then(async clamscan => {
    // Supply nothing to first parameter to use `fileList`
    const { goodFiles, badFiles, errors, viruses } = await clamscan.scanFiles();
});

.scanStream(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)
    • isInfected (boolean) True: Stream is infected; False: Stream is clean. NULL: Unable to scan file.

Returns

  • Promise

    • Promise resolution returns: result (object):

      • file (string) NULL as no file path can be provided with the stream
      • isInfected (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.

Examples

Callback Example:

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');
rs.push('barrrrr');
rs.push(null);

clamscan.scanStream(stream, (err, { isInfected. viruses }) => {
    if (err) return console.error(err);
    if (isInfected) return console.log('Stream is infected! Booo!', viruses);
    console.log('Stream is not infected! Yay!');
});

Promise Example:

clamscan.scanStream(stream).then(({isInfected}) => {
    if (isInfected) return console.log("Stream is infected! Booo!");
    console.log("Stream is not infected! Yay!");
}).catch(err => {
    console.error(err);
};

Promise Example:

const { isInfected, viruses } = await clamscan.scanStream(stream);

.passthrough()

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).

In typical, non-passthrough setups, a file is uploaded to the local filesytem and then subsequently scanned. With that setup, you have to wait for the upload to complete and then wait again for the scan to complete. Using this module's passthrough method, you 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...).

As for these theoretical gains, your mileage my vary and I'd love to hear feedback on this to see where things can still be improved.

Please 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):

Example

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,
    }
});

// For example's sake, we're using the Axios module
const axios = require('Axios');

// Get a readable stream for a URL request
const input = axios.get(some_url);

// Create a writable stream to a local file
const output = fs.createWriteStream(some_local_file);

// Get instance of this module's PassthroughStream object
const av = clamscan.passthrough();

// Send output of Axios stream to ClamAV.
// Send output of Axios 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 { isInfected, 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 (or other events) are being handled in this example but standard errors will be emitted according to NodeJS's Stream specifications

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

clamscan's People

Contributors

adam-beck avatar alromh87 avatar annubis45 avatar bastienjard06 avatar benzino77 avatar biggillystyle avatar carboneater avatar dependabot[bot] avatar dietervds avatar dyarmosh avatar georgecalvert avatar huntr-helper avatar jaffparker avatar jamieslome avatar jiab77 avatar kylefarris avatar mihui avatar mufeedvh avatar ngraef avatar psilves1 avatar saltwaterc avatar sloanholzman avatar tamil-rss avatar vlaurin avatar zertz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

clamscan's Issues

Logs are not creating in the tcp host mode

Hi,
I am trying to use the clamscan in the tcp mode and want to write the logs local file, but it works on local mode and not in the server using the tcp mode but logs are not appearing in the log file.
my configuration is, I am trying but don't know what is going on.
image
image

Invalid information provided to connect to clamav service

@kylefarris it seems like #57 and #58 does not resolve the issue I had described. Below is the stack trace:

Invalid information provided to connect to clamav service. A unix socket or port (+ optional host) is required!
Error: Invalid information provided to connect to clamav service. A unix socket or port (+ optional host) is required!
   at /var/www/production/source/node_modules/clamscan/index.js:1649:29
   at new Promise (<anonymous>)
   at NodeClam.scan_stream (/var/www/production/source/node_modules/clamscan/index.js:1636:16)
   at /var/www/production/source/node_modules/spamscanner/index.js:400:32
   at Array.map (<anonymous>)
   at SpamScanner.getVirusResults (/var/www/production/source/node_modules/spamscanner/index.js:392:26)
   at async Promise.all (index 4)
   at async SpamScanner.scan (/var/www/production/source/node_modules/spamscanner/index.js:1144:9)
   at async MessageSplitter.<anonymous> (/var/www/production/source/index.js:1087:18)

You can reference the source code at https://github.com/spamscanner/spamscanner to see how I implemented it, and let me know maybe if my usage is wrong? This to me seems like a bug with clamscan, since the config passed clearly has the socket defined, and the error message indicates it is instead not defined.

This is most likely from stuff happening in parallel to me it seems at least.

Check on file chunk

I wanna check for viruses on upload process, and the uploader is sending file to server in chunks.
Can the clamscan package verify the health of a chunk and is it safe to assume, if a chunk of file is healthy, so the whole file would be healthy too?

Tear connection when timing out

Hi,

If a timeout occurs when scanning a file in clamdscan mode, the client still waits for the scan to end. It is because in the the client.end() method is called when handling the timeout event on https://github.com/kylefarris/clamscan/blob/master/index.js#L467 instead of calling client.destroy().

I understand the need to still keep waiting for the scan result even if it took too much time, but would it be possible to implement some "hard timeout" option that effectively destroys the socket and throws an error? It would be useful for case when the ClamAV service is saturated and we would postpone the scan, for example.

Occasionally getting the 502 Proxy error while running this clamscan service on the Prod server.

I am getting this below mentioned error some times on our stage and production server:

<title>502 Proxy Error</title>

Proxy Error

The proxy server received an invalid response from an upstream server.
The proxy server could not handle the request

Reason: Error reading from remote server

We have load balance and multiple app servers behind our VM host.
NOTE: Looking for your valuable input on this issue

Need the ability to destroy initialized Clamscan instance

We have an microservice endpoint that us using clamscan to connect to a running clamAV container instance. We have had some issues with the running container going down from time to time and in the process of debugging the container, we had issues with the globally instantiated clamscan instance holding on to previous connections and not showing the true state of the container connection (we have a health check endpoint that retrieves the container clamAV version to ensure the container is up an running). Instead of a globally instantiated option, we wanted to create instances when a particular endpoint is hit. However, this causes issues with what I believe to be sockets that are hung up from the previous instance. The first request works just fine, as expected. But I did notice the following log:

node-clam: ClamAV socket has been closed! false

Every subsequent request throws this error:

node-clam: Could not verify the clamdscan binary. [Error: ENOENT: no such file or directory, access 'C:\usr\bin\clamdscan'] {
  errno: -4058,
  code: 'ENOENT',
  syscall: 'access',
  path: 'C:\\usr\\bin\\clamdscan'
}

It tries to fall back to the local binary (we need this to work only with TCP, not local) because it cannot establish a connection following the previous request. I assume, it is because that socket was not closed. Either please point me to the documentation that shows me how to close the socket/destroy an instance or provide the means to do so, in order that it can be used in more than just a global instantiation.

Filepath of an uploaded file

Hi Guys,

First of all, thanks for the brilliant library. It really helped us a lot!

My issue/question/doubt

I understand that the .is_infected (or .scan_file) function takes in the absolute path of the file - which works perfectly fine in local.

But, what if the application has an file-upload feature (<input type="file">). The application obviously can't get the absolute path of the user uploaded file. In that case, how do we pass the filepath to the .is_infected or use another method perhaps ?

It might be a basic question, please bear with me.

No error when server unavailable

Hi,

I'm setting up clamscan to work with a clamd via a TCP socket using the passthrough() stream to check files that are uploaded. It's working fine but while I was testing different error conditions, I was unable to handle the error when the clamd server is not available. No error is thrown, it just logs a message and then waits until it times out.

This is the log message:

node-clam: Error initiating socket to ClamAV:  { Error: connect ECONNREFUSED 127.0.0.1:3310
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1106:14)
  errno: 'ECONNREFUSED',
  code: 'ECONNREFUSED',
  syscall: 'connect',
  address: '127.0.0.1',
  port: 3310 }

Here's the basic setup (some parts omitted for brevity):

try {
  let clamScanPromise = new NodeClam().init({
    clamdscan: {
      host: opts.clamavHost, // 127.0.0.1
      port: opts.clamavPort // 3310
    }
  })

  const clamscan = await this.clamScanPromise
  let avStream = clamscan.passthrough();
  avStream.on('error', cb)
  avStream.on('scan-complete', result => {
    // not reached
  })

  let scanPromise = new Promise(resolve => {
    avStream.on('scan-complete', resolve)
  })
  file.stream.pipe(avStream)
  // omitted avStream being piped to s3
} catch (err) {
  // not reached
}

The error seems to originate here in the code and it doesn't look like this error is propagated out in any way. Is there any way to react to the error? It seems like clamscan learns of the error, but my code waits for a timeout. If the server is unavailable, I'd like to reject the request with an error status instead of waiting.

Thanks in advance for the help!

API to scan a byte stream

I don't see an API mentioned in documentation that will allow me to pass the byte stream. Only option seems to be file location. Just want to know if there is any way to pass the full byte stream of the file to the scan API.

Note: We don't want to store the file on local before calling scan due to limited size of disk space.

Could not connect to clamd on LocalSocket

"ERROR: Could not connect to clamd on LocalSocket /var/run/clamd.scan/clamd.sock: Permission denied"

Running this from a directory in the home folder.

Any ideas?

Configuration is broken when no local clamav binary available

Initializing

    clamScan = await new NodeClam().init({
        debug_mode: true,
        clamdscan: {
            host: config.clamav.host,
            port: config.clamav.port,
            socket: null,
        },
    })

Further access fails with

(node:2119) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'path' of undefined
    at local_fallback (/Users/vasyas/projects/elpaso/server/node_modules/clamscan/index.js:625:61)
    at Promise (/Users/vasyas/projects/elpaso/server/node_modules/clamscan/index.js:674:24)
    at Promise._execute (/Users/vasyas/projects/elpaso/server/node_modules/bluebird/js/release/debuggability.js:313:9)

Getting Error while running clamdscan

Can someone help to on why I am getting below error while using clamdscan though clamscan working fine.

Error: Error: Command failed: /usr/local/bin/clamdscan --no-summary --fdpass --config-file=/usr/local/etc/clamav/clamd.conf --multiscan /Users/xxxxx/POC/dog.jpeg

Below is test code I am using

var clam = require('clamscan')({
  remove_infected: false, // If true, removes infected files
  quarantine_infected: false, // False: Don't quarantine, Path: Moves files to this place.
  scan_log: null, // Path to a writeable log file to write scan results into
  debug_mode: false, // Whether or not to log info/debug/error msgs to the console
  file_list: null, // path to file containing list of files to scan (for scan_files method)
  scan_recursively: true, // If true, deep scan folders recursively
  clamscan: {
    path: '/usr/local/bin/clamscan', // Path to clamscan binary on your server
    db: null, // Path to a custom virus definition database
    scan_archives: true, // If true, scan archives (ex. zip, rar, tar, dmg, iso, etc...)
    active: true, // If true, this module will consider using the clamscan binary,
    multiscan: true
  },
  clamdscan: {
    path: '/usr/local/bin/clamdscan', // Path to the clamdscan binary on your server
    config_file: '/usr/local/etc/clamav/clamd.conf', // 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
  },
  preference: 'clamdscan' // If clamdscan is found and active, it will be used by default
});

clam.is_infected('/Users/xxxxx/POC/dog.jpeg', function(err, file, is_infected) {
  if(err) {
    console.log(err);
    return false;
  }

  if(is_infected) {
    console.log({msg: "File is infected!"});
  } else {
    console.log({msg: "File is clean!"});
  }
});

get_version() throws ENOENT error

Hello,

calling get_version() throws

{
cmd: "/usr/bin/clamscan --no-summary --stdout --remove=no --scan-archive=yes -r --version",
code: "ENOENT", 
errno: "ENOENT",
path: "/usr/bin/clamscan --no-summary --stdout --remove=no --scan-archive=yes -r --version",
spawnargs: [],
stderr: "",
stdout: "",
syscall: "spawn /usr/bin/clamscan --no-summary --stdout --remove=no --scan-archive=yes -r --version"
}

After digging in I found out that the error originates in index.js

const {stdout, stderr} = await cp_execfile(command);

I got it to work by changing it to

...
try {
                    const {stdout, stderr} = await cp_exec(command);
...

Is there I am doing something wrong when calling it?

Custom config_file not respected by scanner verification

When I initialize a NodeClam with a custom binary and config file for clamdscan, the scanner verification process does not respect the custom config file setting.

Here's my call to init:

const clamscan = await new NodeClam().init({
    clamdscan: {
      path: '/opt/bin/clamdscan',
      config_file: '/opt/etc/myapp.conf',
      active: true,
    },
    preference: 'clamdscan',
    debug_mode: true,
  });

and here's the logger output:

node-clam: Could not verify the clamdscan binary. Error: Command failed: /opt/bin/clamdscan --version
ERROR: Can't parse clamd configuration file /opt/etc/clamd.conf

    at ChildProcess.exithandler (child_process.js:308:12)
    at ChildProcess.emit (events.js:314:20)
    at ChildProcess.EventEmitter.emit (domain.js:483:12)
    at maybeClose (internal/child_process.js:1022:16)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:287:5)
    at Process.callbackTrampoline (internal/async_hooks.js:126:14) {
  killed: false,
  code: 2,
  signal: null,
  cmd: '/opt/bin/clamdscan --version',
  stdout: '',
  stderr: "ERROR: Can't parse clamd configuration file /opt/etc/clamd.conf\n"
}

As you can see, clamdscan is still looking for /opt/etc/clamd.conf (what I believe is the default config file location, and which doesn't exist in my environment) instead of the custom file I provided, /opt/etc/myapp.conf. The binary itself has been correctly overridden to the value I provided in init.

I believe the behavior comes from the _is_clamav_binary function in index.js

async _is_clamav_binary(scanner) {
        const path = this.settings[scanner].path || null;
        if (!path) {
            if (this.settings.debug_mode) console.log(`${this.debug_label}: Could not determine path for clamav binary.`);
            return false;
        }

       // Right here
       // vvvvvvvvvvvv
        const version_cmds = {
            clamdscan: `${path} --version`,
            clamscan: `${path} --version`,
        };
       // ^^^^^^^^^^^^^^

        try {
            await fs_access(path, fs.constants.R_OK);
            const version_cmds_exec = version_cmds[scanner].split(' ');
            const {stdout} = await cp_execfile(version_cmds_exec[0], [version_cmds_exec[1]]);
            if (stdout.toString().match(/ClamAV/) === null) {
                if (this.settings.debug_mode) console.log(`${this.debug_label}: Could not verify the ${scanner} binary.`);
                return false;
            }
            return true;
        } catch (err) {
            if (this.settings.debug_mode) console.log(`${this.debug_label}: Could not verify the ${scanner} binary.`, err);
            return false;
        }
    }

The version_cmds commands do not include a -c/--config-file option, and my log output suggests that clamdscan still requires a working config file even just to run --version.

Can the package be modified to use the custom config file during scanner verification? I could take on this task if necessary.

huntr.dev - Command Injection

Vulnerability Description

Affected versions of this package are vulnerable to Command Injection. It is possible to inject arbitrary commands as part of the _is_clamav_binary function located within Index.js.

It should be noted that this vulnerability requires a pre-requisite that a folder should be created with the same command that will be chained to execute. This lowers the risk of this issue.

POC

var Root = require("clamscan"); 
var fs = require("fs"); 
var attack_code = "echo vulnerable > create.txt"; 
var root = new Root(); 
fs.mkdir(attack_code + "&", function(){}); 
root.init({"clamscan": {'path': attack_code + "&"}});

Bug Bounty

We have opened up a bounty for this issue on our bug bounty platform. Want to solve this vulnerability and get rewarded ๐Ÿ’ฐ? Go to https://huntr.dev/

TypeError: clam.is_infected is not a function

while scanning single file, I'm getting this error.

var clam = require('clamscan');
 clam.is_infected('./upload/' + req.files[0].originalname, function(err, file, is_infected) {
            if (err) {
                console.log(err);
                return false;
            }

            if (is_infected) {
                virus_detected = true;
                res.send({ msg: "File is infected! upload blocked.!" });
            }
            else{
//some goes here
}
});

Docker support

Hi, nice repo

Any plans to add a docker image ready to use with the API and clamav binary inside?

Clamdscan throwing error on local run

Hey,

currently i'm building a node express server which is currently just running locally. When i try to scan a file / folder it always resolves into an error:

----------- SCAN SUMMARY -----------
Infected files: 0
Total errors: 1
Time: 0.000 sec (0 m 0 s)
Start Date: 2021:09:21 16:45:55
End Date: 2021:09:21 16:45:55
FAIL: 2

cmd: /usr/local/bin/clamdscan --no-summary --fdpass --remove --config-file=./config/clamd.dev.conf --multiscan ./clamav/test.png'

Using MacOS Big Sur

Any experience on this and how i can fix this for local testing? Thanks in advance :)

ClamAv produces a warning then file is assumed to be clean

Hi,

Wondering if this has ever cropped up before:

If you were to run the example is_infected code on the repo page, and ClamAv was to produce a single warning in it's output the file been checked would be assumed to be clean without it looking at the scanning result from stdout.

This is because if any warnings are raised they are received on stdErr. Resulting in line 216 been invoked:

return callback(err, '', null);   ** Note:  err === null, if the error comes from stderr  **

Following through to the example on the main repo page. 'File is clean' would be returned.

clam.is_infected('/a/picture/for_example.jpg', function(err, file, is_infected) {
if(err) {
console.log(err);
return false;
}
if(is_infected) {
res.send({msg: "File is infected!"});
} else {
res.send({msg: "File is clean!"});
}
});

Thanks
Rich.

Error when try to build

When I try to build my Laravel project with clamscan running npm run dev, i get an error:

Module not found: Error: Can't resolve 'child_process'.

So i put in webpackconfig this:
node: {
child_process: 'empty'
}

and i get another error:

Module not found: Error: Can't resolve 'fs'.

Then i put in webpackconfig:

node: {
child_process: 'empty',
fs: 'empty'
}

and i get the error:
fs.existsSync is not a function

please tell me whats is going on, and what i have to do

TypeError: Assignment to constant variable.

After trying to check directory got this error

viruses = Array.from(new Set(viruses));
TypeError: Assignment to constant variable.

And after some investigation, I found an issue in scan_files method.

line 1087

let viruses = [];

line 1093

const viruses = stdout.trim().split(String.fromCharCode(10)).map(v => /FOUND\n?$/.test(v) ? v.replace(/(.+):\s+(.+)FOUND\n?$/, "$2").trim() : null).filter(v => !!v);

line 1119

viruses = Array.from(new Set(viruses));

A pretty easy solution will be to rename a variable in the line 1093

Error thrown using clamdscan with scan_recursively false

I'm using the default config, the only changes are to the clamdscan config_file and scan_recursively which is set to false so I can get the lists with good / bad files.
This is the error that I get:

/var/www/aws-cleaner/node_modules/clamscan/index.js:535
                        if (info.isFile()) good_files.push(file);
                                ^

TypeError: Cannot read property 'isFile' of undefined
    at /var/www/aws-cleaner/node_modules/clamscan/index.js:535:33
    at FSReqWrap.oncomplete (fs.js:82:15)

It works fine using the other 'option' to get the 2 lists with good/bad files, using a file_cb function and scan_recursively set to true.

I have identified a core bug from clamscan init being called in parallel

It seems like this error occurs if clamscan init is being called in parallel, which would mean that the promise is not being shared, and false information being returned.

 Error (NodeClamError) {
    data: {},
    date: Date 2020-08-12 01:02:33 797ms UTC {},
    message: 'No valid & active virus scanning binaries are active and available and no host/socket option provided!',
  }

https://travis-ci.com/github/spamscanner/spamscanner/builds/179356176

Related to #55

ERROR: Could not lookup : Servname not supported for ai_socktype

I was trying to run a scan using my node app, during the callback I got an error as follow

ERROR: Communication error

So I tried to run the scan manually, the error message is
ERROR: Could not lookup : Servname not supported for ai_socktype

The command used for run is:
clamdscan --no-summary --fdpass --config-file=/etc/clamav/clamd.conf /home/ubuntu/testing/up/testing12324

Anyone encountered this before?
Thank you :)

Socket support

What do you think about support scan using tcp socket?

3.8 GB file gives issue - RangeError [ERR_OUT_OF_RANGE]: The value of "value" is out of range

Using latest version of clamscan, I am getting the following error when I try to load a 3.8 GB file.

node-clam: Provided stream is readable.
node-clam: Attempting to establish socket/TCP connection for "scanStream"
node-clam: using local unix domain socket: /var/run/clamav/clamd.ctl
internal/buffer.js:69
throw new ERR_OUT_OF_RANGE('value', range, value);
^

RangeError [ERR_OUT_OF_RANGE]: The value of "value" is out of range. It must be >= -2147483648 and <= 2147483647. Received 3875796802
at checkInt (internal/buffer.js:69:11)
at writeU_Int32BE (internal/buffer.js:799:3)
at Buffer.writeInt32BE (internal/buffer.js:887:10)
at NodeClamTransform._transform (/home/repo-l2/Documents/projects/ge-clam/node_modules/clamscan/lib/NodeClamTransform.js:44:14)
at NodeClamTransform.Transform._read (internal/streams/transform.js:205:10)
at NodeClamTransform.Transform._write (internal/streams/transform.js:193:12)
at writeOrBuffer (internal/streams/writable.js:358:12)
at NodeClamTransform.Writable.write (internal/streams/writable.js:303:10)
at Readable.ondata (internal/streams/readable.js:726:22)
at Readable.emit (events.js:375:28) {
code: 'ERR_OUT_OF_RANGE'
}

Could not find file to scan

Trying to upload a file with space in the file name, it's throwing this error 'Could not find file to scan!'
After investigation, found that it is replacing the spaces with '\ '
After that it is failing at fs_access function.

using scan_files without file_cb

Hello,
I found a bug when i'm trying to use scan_files without file_cb parameter:

clam.scan_files(files, function(err, good_files, bad_files) {});

This was printed to console:
if(this.settings.debug_mode === true)
^
TypeError: Cannot read property 'debug_mode' of undefined
at home/user/dev/node_modules/clamscan/index.js:262:22

After i had correted this, it was printed:
return end_cb(err, path, null);
^
ReferenceError: path is not defined
at home/user/dev/node_modules/clamscan/index.js:264:25

node 16 and aws-sdk

When using node 16 the following code fails with
Error [ERR_MULTIPLE_CALLBACK]: Callback called multiple times
It does not happen on node 14.

It seems to be a problem with the highWaterMark. When it is low enough, it works.

const fs = require('fs');
const NodeClam = require('clamscan');

const {Writable} = require('stream');

class MyWritable extends Writable {
constructor(options) {
super(options);
}
_write(chunk, encoding, callback) {
// console.log("xxxxxx", arguments);
callback();
}
}

async function test() {
const source = fs.createReadStream('./README.md', {highWaterMark: 512});
const clamscan = await new NodeClam().init({clamdscan: {host: 'localhost', port: 3310}, debugMode: true});
const av = clamscan.passthrough();
av.on('finish', () => {console.log('--------------av finished');});
av.on('error', (error) => {console.log('--------------av error', error);});
av.on('scan-complete', (result) => {console.log('--------------av scan-complete', result);});
const mywritable = new MyWritable();
mywritable.on('error', (error) => {console.log('--------------mw error', error);});
mywritable.on('finish', () => {console.log('--------------mv finished');});
source.pipe(av).pipe(mywritable).on('finish', () => {console.log('all done', arguments);});
}

test();

Scanning files with passthrough times out

Describe the bug

I'm implementing a function that receives a file URL, creates a readable stream with axios, and then pipes that stream to clamscan with the passthrough method. However, I always receive COMMAND READ TIMED OUT in the debug logs and is_infected is null. It usually takes around 3 minutes to respond.

Code to reproduce

This is the simplified code.

const nodeClam = new NodeClam()
const clam = await nodeClam.init(clamScanOptions)

const scanRemoteFile = async (url: string): Promise<ScanResult> => {
  let fileStream: ReadStream
  try {
    // Get a readable stream from this URL
    const res = await axios({
      method: 'GET',
      url,
      responseType: 'stream',
    })
    fileStream = res.data
  } catch (e) {
    console.error(e)
  }
  
  // Send output of this file stream to ClamAV
  const av = clam.passthrough()
  fileStream.pipe(av)

  return new Promise((resolve, reject) => {
    av.on('scan-complete', (result) => {
      resolve(result)
    })
    // other listeners
  })
}

Debug logs

node-clam: DEBUG MODE ON
node-clam: Could not verify the clamdscan binary. [Error: ENOENT: no such file or directory, access '/usr/bin/clamdscan'] {
  errno: -2,
  code: 'ENOENT',
  syscall: 'access',
  path: '/usr/bin/clamdscan'
}
node-clam: Could not verify the clamscan binary. [Error: ENOENT: no such file or directory, access '/usr/bin/clamscan'] {
  errno: -2,
  code: 'ENOENT',
  syscall: 'access',
  path: '/usr/bin/clamscan'
}
node-clam: Initially testing socket/tcp connection to clamscan server.
node-clam: using remote server: xxx.xxx.xxx.xxx:3310
node-clam: Established connection to clamscan server!
node-clam: PONG!
node-clam: Established connection to clamscan server!
node-clam: Socket/Host connection closed.
node-clam: using remote server: xxx.xxx.xxx.xxx:3310
node-clam: ClamAV Socket Initialized...
node-clam: Doing initial transform!
node-clam: Got result! COMMAND READ TIMED OUT

node-clam: Error Response:  COMMAND READ TIMED OUT
node-clam: File may be INFECTED!
node-clam: Processed Result:  {
  is_infected: null,
  viruses: [],
  file: null,
  resultString: 'COMMAND READ TIMED OUT'
} COMMAND READ TIMED OUT

node-clam: ClamAV socket has received the last chunk!
node-clam: Error Response:  COMMAND READ TIMED OUT
node-clam: File may be INFECTED!
node-clam: Result of scan: {
  is_infected: null,
  viruses: [],
  file: null,
  resultString: 'COMMAND READ TIMED OUT'
}
node-clam: It took false seconds to scan the file(s).

Expected behavior

Files can be scanned normally.

Other information

  • clamscan: v1.3.3
  • docker-clamav: ClamAV 0.102.4/26090/Wed Feb 24 12:09:42 2021 that comes with this image mkodockx/docker-clamav@sha256:498cc6f2d2b56974f1cdcb8e15267cc736576f960ce99ce862b7b82ff7aa6a4d (sorry I retagged the image so I lost the original tag, but still have this hash and it's pullable from Docker Hub).

There is EOF error on scanning an infected file by using passthrough

Scanning an infected file by using 'is_infected' worked well. But there is uncaught error when using passthrough.
W20200120-14:47:30.649(8)? (STDERR) events.js:183
W20200120-14:47:30.650(8)? (STDERR) throw er; // Unhandled 'error' event
W20200120-14:47:30.650(8)? (STDERR) ^
W20200120-14:47:30.650(8)? (STDERR)
W20200120-14:47:30.650(8)? (STDERR) Error: stream.push() after EOF
W20200120-14:47:30.650(8)? (STDERR) at readableAddChunk (_stream_readable.js:240:30)
W20200120-14:47:30.650(8)? (STDERR) at NodeClamTransform.Readable.push (_stream_readable.js:208:10)
W20200120-14:47:30.651(8)? (STDERR) at NodeClamTransform.Transform.push (_stream_transform.js:147:32)
W20200120-14:47:30.651(8)? (STDERR) at NodeClamTransform._flush (/npm/node_modules/clamscan/NodeClamTransform.js:38:14)
W20200120-14:47:30.651(8)? (STDERR) at NodeClamTransform.prefinish (_stream_transform.js:137:10)
W20200120-14:47:30.651(8)? (STDERR) at emitNone (events.js:106:13)
W20200120-14:47:30.651(8)? (STDERR) at NodeClamTransform.emit (events.js:208:7)
W20200120-14:47:30.651(8)? (STDERR) at prefinish (_stream_writable.js:602:14)
W20200120-14:47:30.651(8)? (STDERR) at finishMaybe (_stream_writable.js:610:5)
W20200120-14:47:30.651(8)? (STDERR) at endWritable (_stream_writable.js:621:3)
W20200120-14:47:30.651(8)? (STDERR) at NodeClamTransform.Writable.end (_stream_writable.js:572:5)
W20200120-14:47:30.652(8)? (STDERR) at NodeClamTransform.Duplex._destroy (_stream_duplex.js:118:8)
W20200120-14:47:30.652(8)? (STDERR) at NodeClamTransform.Transform._destroy (_stream_transform.js:196:29)
W20200120-14:47:30.652(8)? (STDERR) at NodeClamTransform.destroy (internal/streams/destroy.js:32:8)
W20200120-14:47:30.652(8)? (STDERR) at handle_error (/npm/node_modules/clamscan/index.js:927:44)

Init does not reject errors.

We were trying to catch startup errors in the init method but not seeing them. Thanks to our team member @saijian2008, we found the problem is index.js:571. It should use reject not resolve, i.e.:

return (has_cb ? cb(err, false) : reject(err));

Package requires 0.12

It's a bit too soon to be requiring this version--especially since the reason for requiring it is not entirely necessary.

streams

Is there any way to scan an input stream instead of a file?

Error: spawn /usr/bin/clamdscan --no-summary --fdpass --multiscan /usr/src/app/scandir ENOENT

When running clamscan inside clamav docker container (Alpine based), it is giving error.

Steps to reproduce

  • Create Docker file
FROM clamav/clamav:unstable_base
WORKDIR /usr/src/app
COPY . .
RUN apk --no-cache add --virtual native-deps \
    bash g++ gcc libgcc libstdc++ linux-headers make \
    --update nodejs npm 
EXPOSE 3000
  • Create DockerCompose file
version: "3.2"
services:
  #=================================================================
  # ClamAV for Virus Scanning
  #=================================================================
  clamav:
    build: .
    container_name: clamav
    volumes:
      - clam-db:/var/lib/clamav
      - scandir:/usr/src/app/scandir
      - express-app:/usr/src/app
      - express-app\node_modules:/usr/src/app/node_modules
    ports: 
      - 3000:3000
  • NodeJs App
const NodeClam = require('clamscan');

async function startScan() {
  try {
      const clamscan = await new NodeClam().init();
      const {good_files, bad_files} = await clamscan.scan_dir(`${process.cwd()}/scandir`);
      console.log("๐Ÿš€ ~ file: app.js ~ startScan ~ good_files", good_files)
      console.log("๐Ÿš€ ~ file: app.js ~ startScan ~ bad_files", bad_files)
  } catch (err) {
      console.log("๐Ÿš€ ~ file: app.js ~ startScan ~ err", err)
  }
}

startScan();

Error

Error: spawn /usr/bin/clamdscan --no-summary --fdpass --multiscan /usr/src/app/scandir ENOENT
    at parse_stdout (/usr/src/app/node_modules/clamscan/index.js:1221:87)
    at /usr/src/app/node_modules/clamscan/index.js:1239:37
    at exithandler (child_process.js:397:5)
    at ChildProcess.errorhandler (child_process.js:409:5)
    at ChildProcess.emit (events.js:400:28)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:275:12)
    at onErrorNT (internal/child_process.js:467:16)
    at processTicksAndRejections (internal/process/task_queues.js:82:21)
    at emitUnhandledRejectionWarning (internal/process/promises.js:168:15)
    at processPromiseRejections (internal/process/promises.js:247:11)
    at processTicksAndRejections (internal/process/task_queues.js:96:32)
(node:183) Error: Error: spawn /usr/bin/clamdscan --no-summary --fdpass --multiscan /usr/src/app/scandir ENOENT
    at parse_stdout (/usr/src/app/node_modules/clamscan/index.js:1221:87)
    at /usr/src/app/node_modules/clamscan/index.js:1239:37
    at exithandler (child_process.js:397:5)
    at ChildProcess.errorhandler (child_process.js:409:5)
    at ChildProcess.emit (events.js:400:28)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:275:12)
    at onErrorNT (internal/child_process.js:467:16)
    at processTicksAndRejections (internal/process/task_queues.js:82:21)
(node:183) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
    at emitDeprecationWarning (internal/process/promises.js:180:11)
    at processPromiseRejections (internal/process/promises.js:249:13)
    at processTicksAndRejections (internal/process/task_queues.js:96:32)

Using clamdscan with scan_recursively not working as expected

I think that the issue with clamdscan and scan_recursively is not entirely fixed or I didn't understood right what needs to output. Now it works, but only shows the files that are directly in that folder, the ones that are in subfolders are not shown, they are not even scanned.

real time scanning on upload

I wonder if this is supported? Users will upload base64 image strings and the string could be a malware in disguise. Thanks

sudo freshclam error

Tue May 19 00:23:11 2020 -> !Failed to load new database: Malformed database
Tue May 19 00:23:12 2020 -> ^Database load exited with "Test failed" (8)
Tue May 19 00:23:12 2020 -> !Database test FAILED.
Tue May 19 00:23:12 2020 -> !Unexpected error when attempting to update database: daily
Tue May 19 00:23:12 2020 -> ^fc_update_databases: fc_update_database failed: Test failed (8)
Tue May 19 00:23:12 2020 -> !Database update process failed: Test failed (8)
Tue May 19 00:23:12 2020 -> !Update failed.

Installation

For installation, do you need to install ClamAV on to a local machine or server?

[NFR] Add max_buffer in settings to control nodejs maxBuffer size

If you try to scan folders with more than 4k files there's a chance to get an error like this There was an error! Error: stdout maxBuffer exceeded., this is because nodejs's buffer is set to 200*1024 bytes.
It would be nice to have a way to control this, without hacking the code inside the module.

TypeError: clamscan.passthrough is not a function

I am trying out the passthrough() example but unfortunately I am getting this

TypeError: clamscan.passthrough is not a function

I am initializing the connection options as

const clamscan = new NodeClam().init({
    clamdscan: {
        host: '127.0.0.1',
        port: 3310,
    }
});

as I am running the clamav service in a docker container (telnet 127.0.0.1 3310 works btw).

Ideas?

Unknown Command: PING!

When checking the availability of the clamd service if socket or host/port are provided, the following code in index.js is executed: client.write('PING!');, expecting a response back: PONG.

However, the current version of clamd is looking for a PING command without the exclamation point. Hence, clamd seems to reject this as an "UNKNOWN COMMAND". Running the current test suite, we can see the following error in the debug logs each time:

Fri Sep 13 10:03:23 2019 -> Command PING has trailing garbage!
Fri Sep 13 10:03:23 2019 -> got command PING! (5, 0), argument:
Fri Sep 13 10:03:23 2019 -> Receive thread: closing conn (FD 9), group finished
Fri Sep 13 10:03:23 2019 -> Consumed entire command

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.