pompelmi 0.23.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +142 -67
- package/dist/pompelmi.cjs +10 -8
- package/dist/pompelmi.cjs.map +1 -1
- package/dist/pompelmi.esm.js +10 -8
- package/dist/pompelmi.esm.js.map +1 -1
- package/package.json +36 -20
package/README.md
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<!-- Language Selector -->
|
|
4
|
+
<p>
|
|
5
|
+
<strong>Read this in other languages:</strong><br/>
|
|
6
|
+
<a href="docs/i18n/README.it.md">🇮🇹 Italiano</a> •
|
|
7
|
+
<a href="docs/i18n/README.fr.md">🇫🇷 Français</a> •
|
|
8
|
+
<a href="docs/i18n/README.es.md">🇪🇸 Español</a> •
|
|
9
|
+
<a href="docs/i18n/README.de.md">🇩🇪 Deutsch</a> •
|
|
10
|
+
<a href="docs/i18n/README.ja.md">🇯🇵 日本語</a> •
|
|
11
|
+
<a href="docs/i18n/README.zh-CN.md">🇨🇳 简体中文</a> •
|
|
12
|
+
<a href="docs/i18n/README.ko.md">🇰🇷 한국어</a> •
|
|
13
|
+
<a href="docs/i18n/README.pt-BR.md">🇧🇷 Português</a> •
|
|
14
|
+
<a href="docs/i18n/README.ru.md">🇷🇺 Русский</a> •
|
|
15
|
+
<a href="docs/i18n/README.tr.md">🇹🇷 Türkçe</a>
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
> 💡 **Translation Note:** Help improve translations by opening a PR. The English README is the source of truth.
|
|
19
|
+
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
1
24
|
<!-- HERO START -->
|
|
2
25
|
|
|
3
26
|
<p align="center">
|
|
@@ -14,21 +37,33 @@
|
|
|
14
37
|
<a href="https://bytes.dev/archives/429"><img alt="Featured in Bytes #429" src="https://img.shields.io/badge/featured-Bytes%20%23429-111111"></a>
|
|
15
38
|
<a href="https://dev.to/sonotommy/secure-nodejs-file-uploads-in-minutes-with-pompelmi-3jfe"><img alt="Featured on DEV.to" src="https://img.shields.io/badge/featured-DEV.to-0A0A0A?logo=devdotto"></a>
|
|
16
39
|
<br/>
|
|
40
|
+
<a href="https://github.com/sorrycc/awesome-javascript"><img alt="Mentioned in Awesome JavaScript" src="https://awesome.re/mentioned-badge.svg"></a>
|
|
41
|
+
<a href="https://github.com/dzharii/awesome-typescript"><img alt="Mentioned in Awesome TypeScript" src="https://awesome.re/mentioned-badge-flat.svg"></a>
|
|
42
|
+
<br/>
|
|
17
43
|
|
|
18
44
|
</p>
|
|
19
45
|
|
|
20
46
|
<h1 align="center">pompelmi</h1>
|
|
21
47
|
|
|
48
|
+
<p align="center">
|
|
49
|
+
<strong>Fast, Private, and Powerful File Malware Scanning for Node.js</strong>
|
|
50
|
+
</p>
|
|
22
51
|
|
|
52
|
+
<p align="center">
|
|
53
|
+
⚡ Zero-config setup • 🔒 Privacy-first • 🧩 Composable scanners • 📦 Deep ZIP inspection • 🎯 Framework adapters
|
|
54
|
+
</p>
|
|
23
55
|
|
|
24
56
|
<p align="center">
|
|
57
|
+
<strong>YARA</strong> integration • <strong>ZIP bomb</strong> protection • Drop-in middleware for <strong>Express</strong>, <strong>Koa</strong>, <strong>NestJS</strong>, <strong>Fastify</strong>, and <strong>Next.js</strong> • <strong>CLI</strong> for CI/CD
|
|
58
|
+
</p>
|
|
25
59
|
|
|
26
|
-
<
|
|
60
|
+
<p align="center">
|
|
61
|
+
<em>Scan files before they hit disk. Keep user data private. Ship with confidence.</em>
|
|
27
62
|
</p>
|
|
28
63
|
|
|
29
64
|
**Keywords:** file upload security · malware detection · YARA · Node.js middleware · Express · Koa · Next.js · ZIP bomb protection
|
|
30
65
|
|
|
31
|
-
|
|
66
|
+
---
|
|
32
67
|
|
|
33
68
|
<p align="center">
|
|
34
69
|
<a href="https://www.npmjs.com/package/pompelmi"><img alt="npm version" src="https://img.shields.io/npm/v/pompelmi?label=version&color=0a7ea4&logo=npm"></a>
|
|
@@ -39,6 +74,13 @@
|
|
|
39
74
|
<a href="https://snyk.io/test/github/pompelmi/pompelmi"><img alt="Known Vulnerabilities" src="https://snyk.io/test/github/pompelmi/pompelmi/badge.svg"></a>
|
|
40
75
|
</p>
|
|
41
76
|
|
|
77
|
+
<p align="center">
|
|
78
|
+
<a href="https://www.npmjs.com/package/@pompelmi/cli"><img alt="CLI version" src="https://img.shields.io/npm/v/@pompelmi/cli?label=CLI&color=0a7ea4&logo=npm"></a>
|
|
79
|
+
<a href="https://www.npmjs.com/package/@pompelmi/nestjs-integration"><img alt="NestJS version" src="https://img.shields.io/npm/v/@pompelmi/nestjs-integration?label=NestJS&color=E0234E&logo=nestjs"></a>
|
|
80
|
+
<a href="https://www.npmjs.com/package/@pompelmi/express-middleware"><img alt="Express version" src="https://img.shields.io/npm/v/@pompelmi/express-middleware?label=Express&color=000000&logo=express"></a>
|
|
81
|
+
<a href="https://www.npmjs.com/package/@pompelmi/next-upload"><img alt="Next.js version" src="https://img.shields.io/npm/v/@pompelmi/next-upload?label=Next.js&color=000000&logo=nextdotjs"></a>
|
|
82
|
+
</p>
|
|
83
|
+
|
|
42
84
|
<p align="center">
|
|
43
85
|
<img alt="node" src="https://img.shields.io/badge/node-%3E%3D18-339933?logo=node.js&logoColor=white">
|
|
44
86
|
<img alt="types" src="https://img.shields.io/badge/types-TypeScript-3178C6?logo=typescript&logoColor=white">
|
|
@@ -108,9 +150,12 @@
|
|
|
108
150
|
- [Express](#express)
|
|
109
151
|
- [Koa](#koa)
|
|
110
152
|
- [Next.js (App Router)](#nextjs-app-router)
|
|
111
|
-
- [
|
|
153
|
+
- [Adapters](#adapters)
|
|
154
|
+
- [GitHub Action](#github-action)
|
|
112
155
|
- [Configuration](#configuration)
|
|
156
|
+
- [YARA Getting Started](#yara-getting-started)
|
|
113
157
|
- [Security Notes](#security-notes)
|
|
158
|
+
|
|
114
159
|
- [Testing & Development](#testing--development)
|
|
115
160
|
- [FAQ](#faq)
|
|
116
161
|
- [Contributing](#contributing)
|
|
@@ -118,6 +163,25 @@
|
|
|
118
163
|
|
|
119
164
|
---
|
|
120
165
|
|
|
166
|
+
## 🌍 Translations
|
|
167
|
+
|
|
168
|
+
pompelmi documentation is available in multiple languages to help developers worldwide:
|
|
169
|
+
|
|
170
|
+
- 🇮🇹 **[Italiano (Italian)](docs/i18n/README.it.md)** — Documentazione completa in italiano
|
|
171
|
+
- 🇫🇷 **[Français (French)](docs/i18n/README.fr.md)** — Documentation complète en français
|
|
172
|
+
- 🇪🇸 **[Español (Spanish)](docs/i18n/README.es.md)** — Documentación completa en español
|
|
173
|
+
- 🇩🇪 **[Deutsch (German)](docs/i18n/README.de.md)** — Vollständige Dokumentation auf Deutsch
|
|
174
|
+
- 🇯🇵 **[日本語 (Japanese)](docs/i18n/README.ja.md)** — 日本語による完全なドキュメント
|
|
175
|
+
- 🇨🇳 **[简体中文 (Simplified Chinese)](docs/i18n/README.zh-CN.md)** — 完整的简体中文文档
|
|
176
|
+
- 🇰🇷 **[한국어 (Korean)](docs/i18n/README.ko.md)** — 완전한 한국어 문서
|
|
177
|
+
- 🇧🇷 **[Português (Brasil)](docs/i18n/README.pt-BR.md)** — Documentação completa em português
|
|
178
|
+
- 🇷🇺 **[Русский (Russian)](docs/i18n/README.ru.md)** — Полная документация на русском
|
|
179
|
+
- 🇹🇷 **[Türkçe (Turkish)](docs/i18n/README.tr.md)** — Türkçe tam dokümantasyon
|
|
180
|
+
|
|
181
|
+
**Help improve translations:** We welcome contributions to improve and maintain translations. The English README is the authoritative source. To contribute, please open a Pull Request with your improvements.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
121
185
|
## 🚀 Overview
|
|
122
186
|
|
|
123
187
|
**pompelmi** scans untrusted file uploads **before** they hit disk. A tiny, TypeScript-first toolkit for Node.js with composable scanners, deep ZIP inspection, and optional signature engines.
|
|
@@ -130,7 +194,13 @@
|
|
|
130
194
|
|
|
131
195
|
**📦 ZIP hardening** — traversal/bomb guards, polyglot & macro hints
|
|
132
196
|
|
|
133
|
-
**🔌 Drop-in adapters** — Express, Koa, Fastify, Next.js
|
|
197
|
+
**🔌 Drop-in adapters** — Express, Koa, Fastify, Next.js, **NestJS**
|
|
198
|
+
|
|
199
|
+
**🌊 Stream-based scanning** — memory-efficient processing with configurable buffer limits
|
|
200
|
+
|
|
201
|
+
**⚙️ CLI for CI/CD** — standalone command-line tool for scanning files and directories
|
|
202
|
+
|
|
203
|
+
**🔍 Polyglot detection** — advanced magic bytes analysis and embedded script detection
|
|
134
204
|
|
|
135
205
|
**📘 Typed & tiny** — modern TS, minimal surface, tree-shakeable
|
|
136
206
|
|
|
@@ -144,8 +214,16 @@
|
|
|
144
214
|
|
|
145
215
|
**🔍 Built‑in scanners** — drop‑in **CommonHeuristicsScanner** (PDF risky actions, Office macros, PE header) and **Zip‑bomb Guard**; add your own or YARA via a tiny `{ scan(bytes) }` contract.
|
|
146
216
|
|
|
217
|
+
**🔬 Polyglot & embedded script detection** — advanced magic bytes analysis detects mixed-format files and embedded scripts with **30+ file signatures**.
|
|
218
|
+
|
|
219
|
+
**🌊 Memory-efficient streaming** — scan large files without loading them entirely into memory with automatic stream routing.
|
|
220
|
+
|
|
147
221
|
**⚙️ Compose scanning** — run multiple scanners in parallel or sequentially with timeouts and short‑circuiting via `composeScanners()`.
|
|
148
222
|
|
|
223
|
+
**🏗️ Framework integrations** — native modules for **NestJS**, Express, Koa, Next.js, and Fastify with first-class TypeScript support.
|
|
224
|
+
|
|
225
|
+
**🔧 Production-ready CLI** — standalone tool for CI/CD pipelines with watch mode, multiple output formats (JSON, table, minimal).
|
|
226
|
+
|
|
149
227
|
**☁️ Zero cloud** — scans run in‑process. Keep bytes private. Perfect for GDPR/HIPAA compliance.
|
|
150
228
|
|
|
151
229
|
**👨💻 DX first** — TypeScript types, ESM/CJS builds, tiny API, adapters for popular web frameworks.
|
|
@@ -181,7 +259,6 @@
|
|
|
181
259
|
\* You can run YARA alongside ClamAV, but it’s not built‑in.
|
|
182
260
|
|
|
183
261
|
---
|
|
184
|
-
|
|
185
262
|
## 💬 What Developers Say
|
|
186
263
|
|
|
187
264
|
> "pompelmi made it incredibly easy to add malware scanning to our Express API. The TypeScript support is fantastic!"
|
|
@@ -233,10 +310,6 @@ Validate customer document uploads (ID verification, tax forms) without exposing
|
|
|
233
310
|
|
|
234
311
|
Protect learning management systems from malicious file uploads while maintaining student privacy.
|
|
235
312
|
|
|
236
|
-
### 📱 SaaS Applications
|
|
237
|
-
|
|
238
|
-
Add secure file upload capabilities to your multi-tenant platform with per-tenant policy customization.
|
|
239
|
-
|
|
240
313
|
### 🏢 Enterprise Document Management
|
|
241
314
|
|
|
242
315
|
Scan files at ingestion time for corporate file sharing platforms, wikis, and collaboration tools.
|
|
@@ -249,6 +322,13 @@ Validate user-generated content uploads (images, videos, documents) before proce
|
|
|
249
322
|
|
|
250
323
|
## 🔧 Installation
|
|
251
324
|
|
|
325
|
+
**pompelmi** is a privacy-first Node.js library for local file scanning.
|
|
326
|
+
|
|
327
|
+
**Requirements:**
|
|
328
|
+
- Node.js 18+
|
|
329
|
+
- Optional: ClamAV binaries (for signature-based scanning)
|
|
330
|
+
- Optional: YARA libraries (for custom rules)
|
|
331
|
+
|
|
252
332
|
<table>
|
|
253
333
|
<tr>
|
|
254
334
|
<td><b>npm</b></td>
|
|
@@ -268,7 +348,7 @@ Validate user-generated content uploads (images, videos, documents) before proce
|
|
|
268
348
|
</tr>
|
|
269
349
|
</table>
|
|
270
350
|
|
|
271
|
-
|
|
351
|
+
#### 📦 Optional Framework Adapters
|
|
272
352
|
|
|
273
353
|
```bash
|
|
274
354
|
# Express
|
|
@@ -280,8 +360,14 @@ npm i @pompelmi/koa-middleware
|
|
|
280
360
|
# Next.js
|
|
281
361
|
npm i @pompelmi/next-upload
|
|
282
362
|
|
|
363
|
+
# NestJS
|
|
364
|
+
npm i @pompelmi/nestjs-integration
|
|
365
|
+
|
|
283
366
|
# Fastify (alpha)
|
|
284
367
|
npm i @pompelmi/fastify-plugin
|
|
368
|
+
|
|
369
|
+
# Standalone CLI
|
|
370
|
+
npm i -g @pompelmi/cli
|
|
285
371
|
```
|
|
286
372
|
|
|
287
373
|
> **Note:** Core library works standalone. Install adapters only if using specific frameworks.
|
|
@@ -389,75 +475,66 @@ export const dynamic = 'force-dynamic';
|
|
|
389
475
|
export const POST = createNextUploadHandler({ ...policy, scanner });
|
|
390
476
|
```
|
|
391
477
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
## 🖥️ CLI Tool
|
|
395
|
-
|
|
396
|
-
**pompelmi** includes a modern command-line interface for scanning files directly from your terminal. Perfect for CI/CD pipelines, security audits, and local development.
|
|
397
|
-
|
|
398
|
-
### Installation
|
|
399
|
-
|
|
400
|
-
```bash
|
|
401
|
-
# Install globally
|
|
402
|
-
npm install -g @pompelmi/cli
|
|
478
|
+
### NestJS
|
|
403
479
|
|
|
404
|
-
|
|
405
|
-
|
|
480
|
+
```ts
|
|
481
|
+
// app.module.ts
|
|
482
|
+
import { Module } from '@nestjs/common';
|
|
483
|
+
import { PompelmiModule } from '@pompelmi/nestjs-integration';
|
|
484
|
+
import { CommonHeuristicsScanner } from 'pompelmi';
|
|
485
|
+
|
|
486
|
+
@Module({
|
|
487
|
+
imports: [
|
|
488
|
+
PompelmiModule.forRoot({
|
|
489
|
+
includeExtensions: ['pdf', 'zip', 'png', 'jpg'],
|
|
490
|
+
allowedMimeTypes: ['application/pdf', 'application/zip', 'image/png', 'image/jpeg'],
|
|
491
|
+
maxFileSizeBytes: 10 * 1024 * 1024,
|
|
492
|
+
scanners: [CommonHeuristicsScanner],
|
|
493
|
+
}),
|
|
494
|
+
],
|
|
495
|
+
})
|
|
496
|
+
export class AppModule {}
|
|
497
|
+
|
|
498
|
+
// upload.controller.ts
|
|
499
|
+
import { Controller, Post, UploadedFile, UseInterceptors } from '@nestjs/common';
|
|
500
|
+
import { FileInterceptor } from '@nestjs/platform-express';
|
|
501
|
+
import { PompelmiInterceptor, PompelmiResult } from '@pompelmi/nestjs-integration';
|
|
502
|
+
|
|
503
|
+
@Controller('upload')
|
|
504
|
+
export class UploadController {
|
|
505
|
+
@Post()
|
|
506
|
+
@UseInterceptors(FileInterceptor('file'), PompelmiInterceptor)
|
|
507
|
+
async uploadFile(@UploadedFile() file: Express.Multer.File & { pompelmi?: PompelmiResult }) {
|
|
508
|
+
return {
|
|
509
|
+
success: true,
|
|
510
|
+
verdict: file.pompelmi?.verdict
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
}
|
|
406
514
|
```
|
|
407
515
|
|
|
408
|
-
|
|
516
|
+
> See `packages/nestjs-integration/README.md` for full documentation.
|
|
409
517
|
|
|
410
|
-
|
|
411
|
-
⚡ **Fast Scanning** — Parallel file processing with real-time feedback
|
|
412
|
-
📊 **Detailed Reports** — Human-readable scan summaries with timing
|
|
413
|
-
🎯 **Smart Detection** — Built-in heuristics for common threats
|
|
414
|
-
🛡️ **Safe Defaults** — ZIP bomb protection and file size limits
|
|
415
|
-
|
|
416
|
-
### Usage
|
|
518
|
+
### CLI Usage
|
|
417
519
|
|
|
418
520
|
```bash
|
|
419
521
|
# Scan a single file
|
|
420
|
-
pompelmi scan
|
|
522
|
+
pompelmi scan file.pdf
|
|
421
523
|
|
|
422
|
-
# Scan
|
|
423
|
-
pompelmi scan
|
|
524
|
+
# Scan directory recursively
|
|
525
|
+
pompelmi scan ./uploads --recursive
|
|
424
526
|
|
|
425
527
|
# Watch directory for changes
|
|
426
528
|
pompelmi watch ./uploads
|
|
427
529
|
|
|
428
|
-
#
|
|
429
|
-
pompelmi --
|
|
430
|
-
```
|
|
431
|
-
|
|
432
|
-
### Example Output
|
|
433
|
-
|
|
434
|
-
```
|
|
435
|
-
🛡️ Pompelmi Security Scanner v0.23.0
|
|
436
|
-
|
|
437
|
-
📁 Scanning: document.pdf
|
|
438
|
-
🔍 Checking file safety...
|
|
439
|
-
✅ File passed all security checks
|
|
530
|
+
# JSON output for CI/CD
|
|
531
|
+
pompelmi scan ./dist --output json --exit-on-suspicious
|
|
440
532
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
• Clean: 1 ✅
|
|
444
|
-
• Suspicious: 0 ⚠️
|
|
445
|
-
• Malicious: 0 ❌
|
|
533
|
+
# Full options
|
|
534
|
+
pompelmi scan --help
|
|
446
535
|
```
|
|
447
536
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
Use the CLI in your build pipelines:
|
|
451
|
-
|
|
452
|
-
```yaml
|
|
453
|
-
# GitHub Actions
|
|
454
|
-
- name: Security Scan
|
|
455
|
-
run: npx @pompelmi/cli scan-dir ./dist
|
|
456
|
-
|
|
457
|
-
# GitLab CI
|
|
458
|
-
script:
|
|
459
|
-
- npx @pompelmi/cli scan build.zip
|
|
460
|
-
```
|
|
537
|
+
> See `packages/cli/README.md` for comprehensive CLI documentation.
|
|
461
538
|
|
|
462
539
|
---
|
|
463
540
|
|
|
@@ -858,9 +935,7 @@ You should see an HTTP **422 Unprocessable Entity** (blocked by policy). Clean f
|
|
|
858
935
|
|
|
859
936
|
---
|
|
860
937
|
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
## 🔔 Releases & security
|
|
938
|
+
## Releases & security
|
|
864
939
|
|
|
865
940
|
- **Changelog / releases:** see [GitHub Releases](https://github.com/pompelmi/pompelmi/releases).
|
|
866
941
|
- **Security disclosures:** please use [GitHub Security Advisories](https://github.com/pompelmi/pompelmi/security/advisories). We’ll coordinate a fix before public disclosure.
|
package/dist/pompelmi.cjs
CHANGED
|
@@ -54,9 +54,10 @@ function createPresetScanner(preset, opts = {}) {
|
|
|
54
54
|
opts.decompilationDepth || 'basic';
|
|
55
55
|
if (!opts.decompilationEngine || opts.decompilationEngine === 'binaryninja-hlil' || opts.decompilationEngine === 'both') {
|
|
56
56
|
try {
|
|
57
|
-
// Dynamic import to avoid bundling issues
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
// Dynamic import to avoid bundling issues - using Function to bypass TypeScript type checking
|
|
58
|
+
const importModule = new Function('specifier', 'return import(specifier)');
|
|
59
|
+
importModule('@pompelmi/engine-binaryninja').then((mod) => {
|
|
60
|
+
const binjaScanner = mod.createBinaryNinjaScanner({
|
|
60
61
|
timeout: opts.decompilationTimeout || opts.timeout || 30000,
|
|
61
62
|
depth,
|
|
62
63
|
pythonPath: opts.pythonPath,
|
|
@@ -73,9 +74,10 @@ function createPresetScanner(preset, opts = {}) {
|
|
|
73
74
|
}
|
|
74
75
|
if (!opts.decompilationEngine || opts.decompilationEngine === 'ghidra-pcode' || opts.decompilationEngine === 'both') {
|
|
75
76
|
try {
|
|
76
|
-
// Dynamic import for Ghidra engine (when implemented)
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
// Dynamic import for Ghidra engine (when implemented) - using Function to bypass TypeScript type checking
|
|
78
|
+
const importModule = new Function('specifier', 'return import(specifier)');
|
|
79
|
+
importModule('@pompelmi/engine-ghidra').then((mod) => {
|
|
80
|
+
const ghidraScanner = mod.createGhidraScanner({
|
|
79
81
|
timeout: opts.decompilationTimeout || opts.timeout || 30000,
|
|
80
82
|
depth,
|
|
81
83
|
ghidraPath: opts.ghidraPath,
|
|
@@ -2272,13 +2274,13 @@ class HipaaComplianceManager {
|
|
|
2272
2274
|
constructor(config) {
|
|
2273
2275
|
this.auditEvents = [];
|
|
2274
2276
|
this.config = {
|
|
2275
|
-
enabled: true,
|
|
2276
2277
|
sanitizeErrors: true,
|
|
2277
2278
|
sanitizeFilenames: true,
|
|
2278
2279
|
encryptTempFiles: true,
|
|
2279
2280
|
memoryProtection: true,
|
|
2280
2281
|
requireSecureTransport: true,
|
|
2281
|
-
...config
|
|
2282
|
+
...config,
|
|
2283
|
+
enabled: config.enabled !== undefined ? config.enabled : true
|
|
2282
2284
|
};
|
|
2283
2285
|
this.sessionId = this.generateSessionId();
|
|
2284
2286
|
}
|