Skip to content

Getting started

Start with one upload route and one policy. This guide gets you from zero to a working upload gate in a few minutes.

Pompelmi is most useful when you treat uploaded files as untrusted input and decide what to do with them before storage or downstream processing.

  • Node.js 18 or higher
  • An existing Node.js app, or a new project (npm init -y)

Terminal window
npm install pompelmi

That is the only required dependency. No daemon to start and no API key to configure.


Create a file named scan-test.mjs:

import { scanBytes } from 'pompelmi';
import { readFileSync } from 'node:fs';
const buffer = readFileSync('./package.json');
const result = await scanBytes(buffer, {
filename: 'package.json',
mimeType: 'application/json',
});
console.log('Verdict:', result.verdict);
console.log('Reasons:', result.reasons);
console.log('Duration:', result.durationMs, 'ms');

Run it:

Terminal window
node scan-test.mjs

You should see Verdict: clean for a normal JSON file. Try the EICAR test string if you want a safe way to exercise a malicious verdict.


Install Express and Multer if needed:

Terminal window
npm install express multer

Create server.mjs:

import express from 'express';
import multer from 'multer';
import { scanBytes, STRICT_PUBLIC_UPLOAD } from 'pompelmi';
const app = express();
const upload = multer({
storage: multer.memoryStorage(),
limits: { fileSize: 10 * 1024 * 1024 },
});
app.post('/upload', upload.single('file'), async (req, res) => {
if (!req.file) {
return res.status(400).json({ error: 'No file provided' });
}
const result = await scanBytes(req.file.buffer, {
policy: STRICT_PUBLIC_UPLOAD,
filename: req.file.originalname,
mimeType: req.file.mimetype,
failClosed: true,
});
if (result.verdict !== 'clean') {
return res.status(422).json({
error: 'Upload rejected',
verdict: result.verdict,
reasons: result.reasons,
});
}
res.json({ ok: true, verdict: result.verdict });
});
app.listen(3000, () => console.log('Listening on http://localhost:3000'));

Start the server:

Terminal window
node server.mjs

Test it:

Terminal window
curl -F "file=@package.json;type=application/json" http://localhost:3000/upload
curl -F "file=@/path/to/anything.exe;type=application/octet-stream" http://localhost:3000/upload

In a real system, suspicious uploads often go to quarantine or manual review instead of an immediate permanent reject.


scanBytes returns a ScanReport:

{
verdict: 'clean' | 'suspicious' | 'malicious',
ok: boolean,
matches: Match[],
reasons: string[],
durationMs: number,
file: {
name?: string,
mimeType?: string,
size?: number,
sha256?: string,
},
}

Recommended handling:

  • clean -> continue to storage or downstream processing.
  • suspicious -> quarantine, hold for review, or reject based on your tolerance.
  • malicious -> reject, log, and investigate as needed.

STRICT_PUBLIC_UPLOAD is a good starting point for untrusted uploads. Other built-in packs include:

PolicyBest for
STRICT_PUBLIC_UPLOADAnonymous or untrusted uploaders
CONSERVATIVE_DEFAULTGeneral-purpose hardened default
DOCUMENTS_ONLYPDF, Word, Excel, and CSV portals
IMAGES_ONLYAvatar or image-only endpoints
ARCHIVESArchive handling with ZIP bomb protection
import { scanBytes, IMAGES_ONLY } from 'pompelmi';
const result = await scanBytes(buffer, { policy: IMAGES_ONLY });

You can also configure rules manually:

import { scanBytes } from 'pompelmi';
const result = await scanBytes(buffer, {
maxFileSizeBytes: 5 * 1024 * 1024,
allowedMimeTypes: ['image/jpeg', 'image/png', 'image/webp'],
includeExtensions: ['jpg', 'jpeg', 'png', 'webp'],
filename: file.originalname,
});

By default, Pompelmi can combine:

  1. Size and extension checks.
  2. Declared MIME and magic-byte validation.
  3. Structural heuristics for suspicious file content.
  4. Archive protections for ZIP expansion and traversal cases.
  5. Optional YARA matching when you add it.