1. Upload enters
Client file arrives
Browser MIME, extension, and size are useful hints, but they are not enough to trust the file yet.
Open-source upload security for Node.js. Inspect first, store later.
Open-source core · MIT · Node.js upload security
Pompelmi gives Node.js teams a route-level upload boundary. It helps the route validate claimed file types, catch archive abuse, flag risky document or binary structure, and decide between accept, quarantine, or reject before bytes become durable state.
Minimal route decision
npm install pompelmiimport { scanBytes, STRICT_PUBLIC_UPLOAD } from 'pompelmi';
const report = await scanBytes(req.file.buffer, {
filename: req.file.originalname,
mimeType: req.file.mimetype,
policy: STRICT_PUBLIC_UPLOAD,
failClosed: true,
});
if (report.verdict !== 'clean') {
return rejectOrQuarantine(report);
} Start with a clean route verdict, then wire it into reject, quarantine, or promotion logic. Framework adapters stay optional.
Do not trust public uploads enough to write them straight into the live bucket or a normal disk path.
Validate claimed type, archive behavior, risky PDF or Office structure, SVG content, and optional YARA rules.
Let the route decide between clean, suspicious, or malicious while it still knows the workflow and user context.
Only accepted files reach durable state. Suspicious files can wait in a review or promotion flow instead.
Mentioned in
Upload flow
The project is not a generic after-the-fact malware queue. It belongs at the point where the application still knows the route policy, the expected file class, and whether a suspicious file should be rejected or quarantined.
1. Upload enters
Browser MIME, extension, and size are useful hints, but they are not enough to trust the file yet.
2. Route gate
Apply route limits for count, size, claimed type, and whether the route should fail closed.
3. Inspection engine
Detected type, archive boundaries, risky PDF or SVG structure, and optional YARA-style signals feed a route-aware verdict.
4A. Clean path
Only clean files reach object storage, previews, or downstream processors.
4B. Flagged path
Keep suspicious files out of the live bucket and UI path until the workflow is ready to trust them.
Before storage, quarantine, and promotion
For simple request-response uploads, the usual sequence is receive, inspect, then store only on `clean`.
For larger or direct-to-storage flows, the safe default is quarantine first, then promote only after the scan and policy checks pass.
That keeps risky uploads out of the live bucket, preview path, OCR pipeline, or downstream parser until the application is ready to trust them.
Good fits
Why route-level decisions matter
Browser hints, type validation, and antivirus all help. The missing layer is usually the application decision that turns those signals into accept, reject, quarantine, or promotion.
Helpful client hints are not the same thing as a backend trust decision.
See why file upload validation and deeper inspection belong in separate layers.
Understand which problems validation solves and where scanner-style checks still matter.
Upload routes still need before-storage policy even when YARA or ClamAV exists.
Evaluation table
Useful for
Fast UX feedback before submit
Still missing
Attacker-controlled filenames and client MIME claims are not enough to decide trust.
Useful for
Checking that bytes look like the route expects
Still missing
It still does not answer archive abuse, risky structure, or route-specific storage policy.
Useful for
Known-malware matches and environment-specific signatures
Still missing
Routes still need type validation, archive controls, and before-storage handling.
Useful for
The first application decision before storage, preview, OCR, or promotion
Still missing
Nothing upstream. This is the orchestration layer that combines the other signals.
Browser preview
The preview is intentionally honest. It is a local browser-only look at the UX and signal surface, not a claim that client-side inspection can replace the backend route.
What it shows
What real backend integration adds
Where to go next
Client-side preview only
This widget reads selected files locally. It never uploads them, and it does not claim to run the full backend policy path.
Sample scenarios
Useful for a quick product walkthrough before choosing a real file.
Files stay in your browser for this preview.
Drag files here to preview a verdict
Useful tests: a normal PDF or PNG, a renamed file, or a text file containing the EICAR test string.
What makes this preview useful
Search-intent entry points
The shortest path is not always “install the package.” Sometimes the right first page is the comparison, framework guide, or storage pattern that matches the route you are trying to secure.
Start with the architecture, then branch into the framework or storage flow you actually run.
Memory-backed parsing, scan-before-storage, and clearer verdict handling for Express routes.
App Router upload routes with clean, suspicious, and quarantine-aware decisions.
Module, interceptor, and service patterns for business apps that accept documents.
Early `preHandler` checks before object storage or background processing takes over.
Quarantine-first storage and promotion after a clean verdict instead of trusting direct uploads.
See where signature scanning helps and where the application still needs a real upload boundary.
Treat SVG as active content, not just another image extension on a generic upload route.
Choose your path
Pompelmi is easiest to evaluate when you start with the actual route stack or storage boundary you already run instead of a generic sample that does not match production.
Multer with a memory-backed route guard and verdict-aware handler flow.
Node runtime App Router routes with inspect-first-store-later handling.
Module and interceptor patterns for document-heavy apps and internal systems.
Early blocking in `preHandler` before persistence or downstream jobs.
Middleware-oriented upload inspection for lightweight Node.js stacks.
Nitro handlers that keep the first upload decision inside the app path.
Safer defaults for unauthenticated or lightly trusted uploads.
Quarantine, review, and promotion flows around S3-style storage.
Use cases
Public avatar uploads, PDF intake portals, ZIP imports, and direct-to-storage promotion flows should not all share the same rules. The use-case pages map risk to route design.
Tighter defaults when uploads come from unknown or lightly trusted users.
PDF, Office, and mixed document routes that need quarantine or review options.
Separate raster images from SVG and keep active content out of generic avatar flows.
Archive-specific limits for depth, expansion, traversal, and entry counts.
Review queues and inspect-first-store-later handling for suspicious files.
Delay object publication until a clean verdict or reviewer approval exists.
Trust and proof
The proof is intentionally verifiable: public code, public docs, public examples, and public references that evaluators can inspect quickly.
MIT-licensed repository, public docs, and examples that teams can audit before adopting the upload path.
Inspect GitHub
Express, Next.js, NestJS, Fastify, Koa, Nuxt/Nitro, and storage-oriented flows are documented publicly.
Browse the docs
Use the demo routes and examples directory to see the request path, verdict handling, and storage decisions.
View examples
Public references
Interview with Tommaso Bertocchi by Ryan Donovan.
Dedicated coverage of the project and its Node.js adapters.
Included in Help Net Security's monthly open-source roundup.
FAQ
Pompelmi is the application-layer upload boundary for Node.js routes. It inspects untrusted files before disk, object storage, or downstream processors see them.
No. It covers route-level upload validation and structural checks. Teams can also combine it with YARA, ClamAV, quarantine, and review flows when they need deeper detection.
Yes. The public docs include framework-specific integration guides for Express, Next.js, NestJS, Fastify, Koa, and Nuxt/Nitro.
No. The preview reads selected files locally in the browser to demonstrate the verdict UX. Real upload security decisions still belong on the backend route.
Ship with context intact
That means cleaner storage boundaries, better observability, and less guesswork when the route needs to reject, quarantine, or promote an upload.