pompelmi/pompelmi
Minimal Node.js wrapper around ClamAV — scan any file and get Clean, Malicious, or ScanError. Handles installation and database updates automatically.
ClamAV for humans
A minimal Node.js wrapper around ClamAV that scans any file and returns a typed Verdict Symbol: Verdict.Clean, Verdict.Malicious, or Verdict.ScanError. No daemons. No cloud. No native bindings. Zero runtime dependencies.
npm install pompelmi
const { scan, Verdict } = require('pompelmi');
const result = await scan('/path/to/file.zip');
if (result === Verdict.Malicious) {
throw new Error('File rejected: malware detected');
}
No download data available
No tracked packages depend on this.
clamscan --no-summary <filePath> as a child process and reads the exit code.No stdout parsing. No regex. No surprises.
pompelmi.scan(filePath, [options])scan(filePath: string, options?: { host?: string; port?: number; timeout?: number }): Promise<symbol>
// resolves to one of: Verdict.Clean | Verdict.Malicious | Verdict.ScanError
| Parameter | Type | Description |
|---|---|---|
filePath | string | Absolute or relative path to the file. |
options | object | Optional. Omit to use the local clamscan CLI. Pass host / port to scan via a clamd TCP socket instead. See docs/api.html for the full reference. |
Resolves to one of:
| Result | ClamAV exit code | Meaning |
|---|---|---|
Verdict.Clean | 0 | No threats found. |
Verdict.Malicious | 1 | A known virus or malware signature was matched. |
Verdict.ScanError | 2 | The scan itself failed (I/O error, encrypted archive, permission denied). File status is unknown — treat as untrusted. |
Reading the verdict as a string — each Verdict Symbol carries a
.descriptionproperty (Verdict.Clean.description === 'Clean') for logging or serialisation without comparing against raw strings in application logic.
Rejects with an Error in these cases:
| Condition | Error message |
|---|---|
filePath is not a string | filePath must be a string |
| File does not exist | File not found: <path> |
clamscan is not in PATH | ENOENT (from the OS) |
| ClamAV returns an unknown exit code | Unexpected exit code: N |
clamscan process is killed by a signal | Process killed by signal: <SIGNAL> |
Example — full error handling:
const { scan, Verdict } = require('pompelmi');
const path = require('path');
async function safeScan(filePath) {
try {
const result = await scan(path.resolve(filePath));
if (result === Verdict.ScanError) {
// The scan could not complete — treat the file as untrusted.
console.warn('Scan incomplete, rejecting file as precaution.');
return null;
}
return result; // Verdict.Clean or Verdict.Malicious
} catch (err) {
console.error('Scan failed:', err.message);
return null;
}
}
If ClamAV runs in a Docker container (or anywhere on the network), pass host and port — everything else stays the same.
const result = await pompelmi.scan('/path/to/upload.zip', {
host: '127.0.0.1',
port: 3310,
});
See docs/docker.md for the docker-compose.yml snippet and first-boot notes.
The examples/ directory contains standalone, runnable scripts for common use cases. Each file can be run directly with node examples/<name>.js.
basic-scan.js — Scan a single file and log the Verdictscan-on-upload-express.js — Express route: receive upload, scan before savingscan-on-upload-fastify.js — Fastify route: same patternscan-with-options.js — Scan via a remote clamd instance with custom host, port, and timeouthandle-scan-error.js — Gracefully handle every Verdict including ScanError and hard rejectionsdelete-on-malicious.js — Auto-delete file if Verdict.Maliciousquarantine-on-malicious.js — Move infected file to a quarantine/ folderscan-multiple-files.js — Scan an array of files concurrently with Promise.allscan-directory.js — Walk a directory recursively and scan every filescan-buffer.js — Scan an in-memory Buffer via a temp-file shiminstall-clamav.js — Programmatically trigger ClamAV installationupdate-virus-database.js — Programmatically run freshclam / DB updatescan-with-timeout.js — Timeout patterns for both local clamscan and remote clamdscan-pdf.js — Scan a PDF upload with extension validationscan-image.js — Scan an image upload with extension validationscan-zip.js — Scan a ZIP archive upload (ClamAV recurses automatically)rest-api-server.js — Minimal HTTP server exposing POST /scans3-scan-before-upload.js — Scan locally, then upload to AWS S3 only if cleancli-scan.js — Accept file paths as CLI arguments, print verdicts, exit non-zero on threatstypescript-usage.ts — TypeScript example with inline type declarationsThese modules are not part of the public npm API but are used internally to set up the ClamAV environment on a fresh machine.
ClamAVInstaller()Installs ClamAV using the platform's native package manager. Skips silently if ClamAV is already installed.
ClamAVInstaller(): Promise<string>
| Platform | Package manager | Command |
|---|---|---|
| macOS | Homebrew | brew install clamav |
| Linux | apt-get | sudo apt-get install -y clamav clamav-daemon |
| Windows | Chocolatey | choco install clamav -y |
updateClamAVDatabase()Downloads or updates the ClamAV virus definition database by running freshclam. Skips if main.cvd is already present on disk.
updateClamAVDatabase(): Promise<string>
freshclam exits with a non-zero code or if spawning fails.| Platform | Database path |
|---|---|
| macOS | /usr/local/share/clamav/main.cvd |
| Linux | /var/lib/clamav/main.cvd |
| Windows | C:\ProgramData\ClamAV\main.cvd |
| OS | ClamAV install | DB path checked |
|---|---|---|
| macOS | brew install clamav | /usr/local/share/clamav/main.cvd |
| Linux | apt-get install clamav | /var/lib/clamav/main.cvd |
| Windows | choco install clamav -y | C:\ProgramData\ClamAV\main.cvd |
ClamAV must be installed on the host system. pompelmi does not bundle or download it.
# macOS
brew install clamav && freshclam
# Linux (Debian / Ubuntu)
sudo apt-get install -y clamav clamav-daemon && sudo freshclam
# Windows (Chocolatey)
choco install clamav -y
npm test
The test suite has two parts:
test/unit.test.js) — run with Node's built-in test runner. Mock nativeSpawn from src/spawn.js and platform dependencies via require-cache injection; no ClamAV installation required.test/scan.test.js) — spawn real clamscan processes against EICAR test files. Skipped automatically if clamscan is not found in PATH.git checkout -b feat/your-change.npm test to verify.main.Please read CODE_OF_CONDUCT.md before contributing.
To report a vulnerability, see SECURITY.md.
ISC — © pompelmi contributors