clamscan

repository·master·Indexed 43 days ago

https://github.com/kylefarris/clamscan

A Node.js utility for virus scanning using ClamAV version 2.4.0. It supports scanning files and directories via local binaries (clamscan/clamdscan) or via TCP and UNIX Domain sockets to a ClamAV daemon. Features include stream scanning with .scanStream(), a .passthrough() method for simultaneous piping and scanning, and support for recursive directory scans.

Tokens
9.2K
Snippets
32
Records
40
Agent score
47%

What's inside clamscan

  1. Use passthrough() to scan data while piping

    master

    The .passthrough() method returns a Transform stream. This allows you to pipe a ReadableStream through the scanner and into an output destination (like a file or S3) simultaneously.

    How it works:

    1. Data is forked to ClamAV via TCP or Domain Sockets.
    2. A chunk is only passed to the next output destination if it was successfully sent to and received by ClamAV.
    3. The returned Transform stream emits a special 'scan-complete' event when ClamAV finishes scanning the stream, providing the scan results.

    Example usage:

    const av = clamscan.passthrough();
    input.pipe(av).pipe(output);
    
    av.on('scan-complete', result => {
       const {isInfected, viruses} = result;
       // Handle results
    });
    const NodeClam = require('clamscan');
    const axios = require('axios');
    const fs = require('fs');
    
    const clamscan = new NodeClam().init({
        clamdscan: {
            socket: '/var/run/clamd.scan/clamd.sock',
            host: '127.0.0.1',
            port: 3310,
        }
    });
    
    const input = axios.get(someUrl);
    const output = fs.createWriteStream(someLocalFile);
    const av = clamscan.passthrough();
    
    input.pipe(av).pipe(output);
    
    av.on('scan-complete', result => {
       const {isInfected, viruses} = result;
    });
  2. Simultaneously scan and pipe data with .passthrough()

    master

    The .passthrough() method returns a PassthroughStream object. This allows you to pipe a ReadableStream through it to a destination (like a file or S3) while simultaneously forking the data to ClamAV via TCP or Domain Sockets.

    Key Features:

    • Efficiency: Data is written to the output destination at the same time it is being scanned, potentially speeding up uploads by up to 2x compared to the traditional "upload then scan" workflow.
    • Data Integrity: Each data chunk is only passed to the output if it was successfully sent to and received by ClamAV.
    • Event Notification: The returned PassthroughStream emits a special 'scan-complete' event when ClamAV finishes scanning the streamed data.

    Note: Unlike other methods, .passthrough() does not support a Promise or Callback API; it operates strictly as a Node.js Stream.

    const NodeClam = require('clamscan');
    const axios = require('Axios');
    const fs = require('fs');
    
    const clamscan = new NodeClam().init({
        clamdscan: {
            socket: '/var/run/clamd.scan/clamd.sock',
            host: '127.0.0.1',
            port: 3310,
        }
    });
    
    const input = axios.get(some_url);
    const output = fs.createWriteStream(some_local_file);
    const av = clamscan.passthrough();
    
    // Pipe: Input -> ClamAV Passthrough -> Output
    input.pipe(av).pipe(output);
    
    // Listen for scan results
    av.on('scan-complete', result => {
       const { isInfected, viruses } = result;
       console.log(isInfected ? 'Infected!' : 'Clean!');
    });
    
    // Listen for stream completion
    output.on('finish', () => {
        console.log('File write complete');
    });
  3. How remote ClamAV service configuration affects scanning

    master

    When using the socket or TCP method (communicating with a remote clamd daemon), certain local configuration options are not honored because the remote service dictates the behavior. These include:

    • removeInfected
    • quarantineInfected
    • scanLog
    • fileList
    • 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)
  4. Install ClamAV dependencies for local binary scanning

    master

    To use the local binary method (calling clamscan or clamdscan directly), you must install ClamAV on your host system:

    • Fedora-based distros: sudo yum install clamav
    • Debian-based distros: sudo apt-get install clamav clamav-daemon
    • macOS: sudo brew install clamav
    sudo apt-get install clamav clamav-daemon
  5. Configure clamscan.clamdscan options

    master

    Options for the clamdscan (daemon/socket/TCP) method:

    • socket: (string|boolean) Path to the UNIX Domain socket file.
    • host: (string) IP address or domain name of the remote daemon.
    • port: (number) Port of the daemon. If host is omitted, defaults to 'localhost'.
    • timeout: (number) Timeout for scanning in milliseconds.
    • localFallback: (boolean) If true, falls back to the local binary if socket/TCP fails.
    • path: (string) Path to the clamdscan binary.
    • configFile: (string) Path to the ClamAV config file.
    • multiscan: (boolean) Use all available cores.
    • reloadDb: (boolean) Reload the DB on every call (slower).
    • active: (boolean) Whether to allow using the clamdscan binary.
    • bypassTest: (boolean) Skip checking if the socket is available.
    • tls: (boolean) Use TLS to connect to clamd.
  6. Configure clamscan.clamscan options

    master

    Options for the local clamscan binary method:

    • path: (string) Path to the clamscan binary.
    • db: (string) Path to a custom virus definition database.
    • scanArchives: (boolean) If true, scans archives (zip, rar, tar, etc.).
    • active: (boolean) Whether to allow using the clamscan binary.
  7. Configure clamscan options

    master

    The init method accepts an options object to control scanning behavior.

    Top-level Options

    • removeInfected: (boolean) If true, removes infected files.
    • quarantineInfected: (string|boolean) If a string, moves files to this path. Note: removeInfected must be false for this to work.
    • scanLog: (string) Path to a writable log file.
    • debugMode: (boolean) Logs info/debug/error messages to the console.
    • fileList: (string) Path to a file containing a list of files to scan (used with scanFiles).
    • scanRecursively: (boolean) If true, performs a deep scan of folders.
    • preference: (string) 'clamdscan' or 'clamscan'. Determines the default method used if the preferred one is active.
  8. Configure ClamAV scanning modes

    master

    NodeClam supports two primary scanning modes via the preference setting:

    1. clamscan (Local Binary): Executes the clamscan binary directly on the host machine using child_process. This is useful if you do not have a running ClamAV daemon.
    2. clamdscan (TCP/Socket): Connects to a running ClamAV daemon (clamd) via a Unix domain socket or a TCP/TLS connection. This is generally faster and more efficient for high-volume scanning as the daemon stays resident in memory.

    If clamdscan is preferred but unavailable, the module can fall back to clamscan if clamdscan.localFallback is set to true (default behavior depends on configuration).

  9. Scan files using a configured fileList

    master

    If the clamscan instance is initialized with a fileList option (a path to a newline-delimited text file containing file paths), you can trigger a scan of all those files by calling .scanFiles() without any arguments.

    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();
    });
  10. Using clamscan in an async function

    master

    If you are already inside an async function, you can use await to initialize the scanner more cleanly.

    const NodeClam = require('clamscan');
    
    async some_function() {
        try {
            const clamscan = await new NodeClam().init(options);
            const {goodFiles, badFiles} = await clamscan.scanDir('/foo/bar');
        } catch (err) {
            // Handle errors
        }
    }
    
    some_function();
  11. Basic usage of clamscan instance

    master

    Since init() returns a Promise, you must resolve it to access the scanner instance. You can then call methods like getVersion() and isInfected().

    const NodeClam = require('clamscan');
    const ClamScan = new NodeClam().init(options);
    
    // Using .then()
    ClamScan.then(async clamscan => {
        try {
            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 errors
        }
    }).catch(err => {
        // Handle initialization errors
    });
    const NodeClam = require('clamscan');
    const ClamScan = new NodeClam().init(options);
    
    ClamScan.then(async clamscan => {
        try {
            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 errors
        }
    }).catch(err => {
        // Handle initialization errors
    });