Skip to content

pompelmi/pompelmi

Repository files navigation

pompelmi logo

pompelmi

ClamAV for humans

npm version license platform zero dependencies


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.

Table of contents


Quickstart

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

How it works

  1. Validate — pompelmi checks that the argument is a string and that the file exists before spawning anything.
  2. Scan — pompelmi spawns clamscan --no-summary <filePath> as a child process and reads the exit code.
  3. Map — the exit code is mapped to a result string. Unknown codes and spawn errors reject the Promise.

No stdout parsing. No regex. No surprises.

API

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 .description property (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;
  }
}

Docker / remote scanning

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.


Internal utilities

These 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>
  • Resolves with a status message string on success or skip.
  • Rejects if the install process exits with a non-zero code or if spawning the package manager fails.
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>
  • Resolves with a status message string on success or skip.
  • Rejects if 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

Supported platforms

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.

Installing ClamAV manually

# macOS
brew install clamav && freshclam

# Linux (Debian / Ubuntu)
sudo apt-get install -y clamav clamav-daemon && sudo freshclam

# Windows (Chocolatey)
choco install clamav -y

Testing

npm test

The test suite has two parts:

  • Unit tests (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.
  • Integration tests (test/scan.test.js) — spawn real clamscan processes against EICAR test files. Skipped automatically if clamscan is not found in PATH.

Contributing

  1. Fork the repository at https://github.com/pompelmi/pompelmi.
  2. Create a feature branch: git checkout -b feat/your-change.
  3. Make your changes and run npm test to verify.
  4. Open a pull request against main.

Please read CODE_OF_CONDUCT.md before contributing.

Security

To report a vulnerability, see SECURITY.md.

License

ISC — © pompelmi contributors

About

Minimal Node.js wrapper around ClamAV — scan any file and get Clean, Malicious, or ScanError. Handles installation and database updates automatically.

Topics

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors