Getting started with antivirus scanning in Node.js (5 minutes)

You've built a Node.js app that accepts file uploads. Great. Now how do you make sure those files don't contain viruses?

The answer is ClamAV — a free, open-source antivirus engine that has been around since 2001, is maintained by Cisco, and is used by thousands of applications worldwide. It runs entirely on your own machine. No subscription, no API key, no files leaving your server.

pompelmi is a tiny Node.js package that calls ClamAV for you and gives back a clean result you can act on in JavaScript. Let's set it up.

Step 1 — Install ClamAV

ClamAV is a system-level tool. Install it with your operating system's package manager, not via npm.

macOS — Homebrew
brew install clamav
Linux — Debian / Ubuntu
sudo apt-get update && sudo apt-get install -y clamav
Linux — RHEL / Fedora / CentOS
sudo dnf install -y clamav clamav-update
Windows — Chocolatey
choco install clamav -y

Verify the installation worked:

clamscan --version
# ClamAV 1.x.x/...

Now download the virus database. ClamAV ships without definitions — you need to fetch them before any scan can work:

macOS and Windows
freshclam
Linux
sudo freshclam
The first freshclam download takes a few minutes — the database is several hundred megabytes. Subsequent runs are incremental and fast. For detailed platform-specific instructions see How to install ClamAV for Node.js on macOS, Linux and Windows.

Step 2 — Install pompelmi

In your Node.js project directory:

npm install pompelmi

pompelmi has zero runtime dependencies. It uses Node's built-in child_process module to call clamscan and nothing else. No transitive packages to audit or update.

Step 3 — Scan your first file

Create a file called scan.js:

const { scan, Verdict } = require('pompelmi');

async function main() {
  // Pass any file path you want to scan
  const filePath = process.argv[2];

  if (!filePath) {
    console.error('Usage: node scan.js <file>');
    process.exit(1);
  }

  console.log('Scanning:', filePath);

  const verdict = await scan(filePath);

  if (verdict === Verdict.Clean) {
    console.log('Result: Clean — no threats found.');
  } else if (verdict === Verdict.Malicious) {
    console.log('Result: Malicious — malware detected!');
    process.exit(1);
  } else if (verdict === Verdict.ScanError) {
    console.log('Result: ScanError — could not complete scan.');
    process.exit(2);
  }
}

main().catch(err => {
  console.error('Error:', err.message);
  process.exit(3);
});

Run it against any file:

node scan.js /etc/hosts
# Scanning: /etc/hosts
# Result: Clean — no threats found.

That's it. Three verdicts, one function, zero configuration.

Step 4 — Test malware detection with EICAR

How do you test that the malicious path actually works without using real malware? The EICAR test file is a harmless string that every antivirus engine is required to detect as "malicious" for exactly this purpose. It was invented in 1991 and contains no actual malicious code.

# Create the EICAR test file
echo 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' \
  > /tmp/eicar.txt

# Scan it
node scan.js /tmp/eicar.txt
# Scanning: /tmp/eicar.txt
# Result: Malicious — malware detected!
If you see "Result: Malicious" on the EICAR file, your setup is working correctly. If you see "Clean", run freshclam again — the virus database may not have downloaded properly.

What's next?

You now have a working scanner. The next step is wiring it into your web framework so uploads are scanned automatically.