How to virus scan file users upload using ClamAV

jfbloom22

Jonathan Flower

Posted on September 20, 2022

How to virus scan file users upload using ClamAV

Clamav is a popular open source virus scanning solution. Part of the server application is several gigs worth of virus definition files. Because of this and our micro services architecture, it made the most sense to setup a dedicated server to host ClamAV. This also allows me to share this one instance of ClamAV across several solutions needing virus scanning capabilities.

  1. Create a Linode with docker
  2. In the config, leave all the advanced docker options blank.
  3. Click to create the Linode
  4. After it is provisioned, open the console
  5. Login as the root user
  6. Run the following (check the latest ClamAV docker container versions):
docker pull clamav/clamav:0.105.1
docker volume create clam_db
docker run --interactive  --tty --restart=always --name "clam_container_01" --publish 3310:3310 \
    --publish 7357:7357 \
 --mount source=clam_db,target=/var/lib/clamav \
    --env 'CLAMAV_NO_FRESHCLAMD=true' \
 clamav/clamav:0.105.1
Enter fullscreen mode Exit fullscreen mode

This pulls the ClamAV docker container, creates a persistent docker volume, runs the ClamAV docker container with ports 7357 and 3310 exposed, sets the docker container to automatically restart when the Linode is restarted, and keeps the virus definitions in the persistent docker volume called clam_db.

After completing this I used a package to help scan for viruses in nodejs.
https://www.npmjs.com/package/clamscan

For example here is what worked for me:

  1. Run npm install clamscan
  2. Create a file called virus_scan.js:
const NodeClam = require(‘clamscan’)
const ClamScan = new NodeClam().init({
  removeInfected: true, // If true, removes infected files
  debugMode: false, // Whether or not to log info/debug/error msgs to the console
  scanRecursively: true, // If true, deep scan folders recursively
  clamdscan: {
    host: ‘111.111.111.11’, // IP of host to connect to TCP interface
    port: 3310, // Port of host to use when connecting via TCP interface
    timeout: 60000, // Timeout for scanning files
    localFallback: false, // Use local preferred binary to scan if socket/tcp fails
    multiscan: true, // Scan using all available cores! Yay!
    active: true, // If true, this module will consider using the clamdscan binary
    bypassTest: false, // Check to see if socket is available when applicable
  },
  preference: ‘clamdscan’, // If clamdscan is found and active, it will be used by default
})

// 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(‘./lib/virus.txt’)
    if (isInfected) console.log(`${file} is infected with ${viruses}!`)
  } catch (err) {
    // Handle any errors raised by the code in the try block
    throw err
  }
}).catch(console.error)

Enter fullscreen mode Exit fullscreen mode

Contents of virus.txt includes a virus signature to help make sure everything is working:

X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*
Enter fullscreen mode Exit fullscreen mode

When running node virus_scan.js you should see

null is infected with Eicar-Signature!
Enter fullscreen mode Exit fullscreen mode

From here you should be able to imagine how to scan files your users upload. I will not go into detail since that is going to be fairly unique to your use case.

💖 💪 🙅 🚩
jfbloom22
Jonathan Flower

Posted on September 20, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related