pompelmi 0.5.0 → 0.6.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.
Files changed (2) hide show
  1. package/README.md +141 -228
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,166 +1,74 @@
1
1
  <p align="center">
2
2
  <a href="https://github.com/pompelmi/pompelmi" target="_blank" rel="noopener noreferrer">
3
- <img
4
- src="https://raw.githubusercontent.com/pompelmi/pompelmi/refs/heads/main/assets/logo.svg"
5
- alt="pompelmi"
6
- width="360"
7
- height="280"
8
- />
3
+ <img src="https://raw.githubusercontent.com/pompelmi/pompelmi/refs/heads/main/assets/logo.svg" alt="pompelmi logo" width="360" height="280" />
9
4
  </a>
10
5
  </p>
11
6
 
12
-
13
7
  <h1 align="center">pompelmi</h1>
14
8
 
15
- <p align="center">
16
- Lightweight file upload scanner with optional <strong>YARA</strong> rules.<br/>
17
- Works out‑of‑the‑box on <strong>Node.js</strong>; supports <strong>browser</strong> via a simple HTTP “remote engine”.
18
- </p>
9
+ <p align="center"><strong>Fast file‑upload malware scanning for Node.js</strong> — with optional <strong>YARA</strong>, ZIP deep‑inspection, and drop‑in adapters for <em>Express</em>, <em>Koa</em>, and <em>Next.js</em>. Private by design. Typed. Tiny.</p>
19
10
 
20
- <!--
21
11
  <p align="center">
22
- <img alt="Node.js" src="https://img.shields.io/badge/Node.js-339933?style=for-the-badge&logo=node.js&logoColor=white" />
23
- <img alt="TypeScript" src="https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=white" />
24
- <img alt="Express" src="https://img.shields.io/badge/Express-000000?style=for-the-badge&logo=express&logoColor=white" />
25
- <img alt="Koa" src="https://img.shields.io/badge/Koa-33333D?style=for-the-badge&logo=nodedotjs&logoColor=white" />
26
- <img alt="Next.js" src="https://img.shields.io/badge/Next.js-000000?style=for-the-badge&logo=nextdotjs&logoColor=white" />
27
- <img alt="Fastify (planned)" src="https://img.shields.io/badge/Fastify-000000?style=for-the-badge&logo=fastify&logoColor=white" />
28
- <img alt="NestJS (planned)" src="https://img.shields.io/badge/NestJS-E0234E?style=for-the-badge&logo=nestjs&logoColor=white" />
29
- <img alt="Remix (planned)" src="https://img.shields.io/badge/Remix-000000?style=for-the-badge&logo=remix&logoColor=white" />
30
- <img alt="SvelteKit (planned)" src="https://img.shields.io/badge/SvelteKit-FF3E00?style=for-the-badge&logo=svelte&logoColor=white" />
12
+ <a href="https://www.npmjs.com/package/pompelmi"><img alt="npm version" src="https://img.shields.io/npm/v/pompelmi?label=pompelmi&color=0a7ea4"></a>
13
+ <a href="https://www.npmjs.com/package/pompelmi"><img alt="npm downloads" src="https://img.shields.io/npm/dm/pompelmi?label=downloads&color=6E9F18"></a>
14
+ <img alt="node engines" src="https://img.shields.io/node/v/pompelmi?label=node">
15
+ <img alt="types" src="https://img.shields.io/badge/types-TypeScript-3178C6?logo=typescript&logoColor=white">
16
+ <a href="https://github.com/pompelmi/pompelmi/blob/main/LICENSE"><img alt="license" src="https://img.shields.io/npm/l/pompelmi"></a>
17
+ <a href="https://github.com/pompelmi/pompelmi/stargazers"><img alt="GitHub stars" src="https://img.shields.io/github/stars/pompelmi/pompelmi?style=social"></a>
18
+ <a href="https://github.com/pompelmi/pompelmi/actions/workflows/ci-release-publish.yml"><img alt="CI / Release / Publish" src="https://img.shields.io/github/actions/workflow/status/pompelmi/pompelmi/ci-release-publish.yml?branch=main&label=CI%20%2F%20Release%20%2F%20Publish"></a>
19
+ <a href="https://github.com/pompelmi/pompelmi/issues"><img alt="open issues" src="https://img.shields.io/github/issues/pompelmi/pompelmi"></a>
20
+ <img alt="PRs welcome" src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg">
31
21
  </p>
32
22
 
33
23
  <p align="center">
34
- <img alt="pnpm" src="https://img.shields.io/badge/pnpm-222222?style=for-the-badge&logo=pnpm&logoColor=white" />
35
- <img alt="npm" src="https://img.shields.io/badge/npm-CB3837?style=for-the-badge&logo=npm&logoColor=white" />
36
- <img alt="Vitest" src="https://img.shields.io/badge/Vitest-6E9F18?style=for-the-badge&logo=vitest&logoColor=white" />
37
- <img alt="ESLint" src="https://img.shields.io/badge/ESLint-4B32C3?style=for-the-badge&logo=eslint&logoColor=white" />
38
- <img alt="Prettier" src="https://img.shields.io/badge/Prettier-F7B93E?style=for-the-badge&logo=prettier&logoColor=white" />
24
+ <a href="https://pompelmi.github.io/pompelmi/">Documentation</a> ·
25
+ <a href="#installation">Install</a> ·
26
+ <a href="#quickstart">Quickstart</a> ·
27
+ <a href="#adapters">Adapters</a> ·
28
+ <a href="#diagrams">Diagrams</a> ·
29
+ <a href="#configuration">Config</a> ·
30
+ <a href="#quick-test-eicar">Quick test</a> ·
31
+ <a href="#security-notes">Security</a> ·
32
+ <a href="#packages">Packages</a> ·
33
+ <a href="#faq">FAQ</a>
39
34
  </p>
40
35
 
41
-
42
- -->
43
-
44
- <p align="center">
45
- <a href="https://github.com/pompelmi/pompelmi">
46
- <img alt="npm" src="https://img.shields.io/npm/v/pompelmi?label=pompelmi">
47
- </a>
48
- <a href="https://github.com/pompelmi/pompelmi">
49
- <img alt="downloads" src="https://img.shields.io/npm/d18m/pompelmi?label=downloads">
50
- </a>
51
- <a href="https://github.com/pompelmi/pompelmi/blob/main/LICENSE">
52
- <img alt="license" src="https://img.shields.io/npm/l/pompelmi">
53
- </a>
54
- <img alt="node" src="https://img.shields.io/node/v/pompelmi">
55
- <img alt="types" src="https://img.shields.io/badge/types-TypeScript-3178C6?logo=typescript&logoColor=white">
56
- <img alt="status" src="https://img.shields.io/badge/channel-alpha-orange">
57
- <img alt="coverage" src="https://img.shields.io/github/actions/workflow/status/pompelmi/pompelmi/ci.yml?branch=main&label=coverage&style=flat-square" />
58
-
59
- </p>
36
+ ---
60
37
 
61
38
  ## Installation
62
39
 
63
40
  ```bash
64
41
  # core library
65
42
  npm i pompelmi
66
-
67
- # typical dev deps used in examples (optional)
68
- npm i -D tsx express multer cors
43
+ # or
44
+ pnpm add pompelmi
69
45
  ```
70
46
 
71
- <p align="center">
72
- <a href="#why-pompelmi">Why</a> •
73
- <a href="#installation">Installation</a> •
74
- <a href="#technologies--tools">Technologies & Tools</a> •
75
- <a href="#features">Features</a> •
76
- <a href="#packages">Packages</a> •
77
- <a href="#quickstart">Quickstart</a> •
78
- <a href="#framework-adapters">Framework Adapters</a> •
79
- <a href="#architecture--uml">Architecture & UML</a> •
80
- <a href="#api-overview">API</a> •
81
- <a href="#security--disclaimer">Security</a> •
82
- <a href="#license">License</a>
83
- </p>
84
-
85
- ---
86
-
87
- ## Technologies & Tools
88
-
89
- | Technology | Badge | Link | Description |
90
- | --- | --- | --- | --- |
91
- | Node.js | <img alt="Node.js" src="https://img.shields.io/badge/Node.js-339933?style=for-the-badge&logo=node.js&logoColor=white" /> | [nodejs.org](https://nodejs.org/) | Runtime used by all adapters and the core engine. |
92
- | TypeScript | <img alt="TypeScript" src="https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=white" /> | [typescriptlang.org](https://www.typescriptlang.org/) | Typed development and bundled type definitions. |
93
- | Express | <img alt="Express" src="https://img.shields.io/badge/Express-000000?style=for-the-badge&logo=express&logoColor=white" /> | [expressjs.com](https://expressjs.com/) | Middleware adapter `@pompelmi/express-middleware`. |
94
- | Koa | <img alt="Koa" src="https://img.shields.io/badge/Koa-33333D?style=for-the-badge&logo=nodedotjs&logoColor=white" /> | [koajs.com](https://koajs.com/) | Middleware adapter `@pompelmi/koa-middleware`. |
95
- | Next.js | <img alt="Next.js" src="https://img.shields.io/badge/Next.js-000000?style=for-the-badge&logo=nextdotjs&logoColor=white" /> | [nextjs.org](https://nextjs.org/) | App Router upload handler `@pompelmi/next-upload`. |
96
- | Fastify *(planned)* | <img alt="Fastify" src="https://img.shields.io/badge/Fastify-000000?style=for-the-badge&logo=fastify&logoColor=white" /> | [fastify.dev](https://fastify.dev/) | Planned plugin with identical policies and ZIP handling. |
97
- | NestJS *(planned)* | <img alt="NestJS" src="https://img.shields.io/badge/NestJS-E0234E?style=for-the-badge&logo=nestjs&logoColor=white" /> | [nestjs.com](https://nestjs.com/) | Planned interceptor/guard for file uploads. |
98
- | Remix *(planned)* | <img alt="Remix" src="https://img.shields.io/badge/Remix-000000?style=for-the-badge&logo=remix&logoColor=white" /> | [remix.run](https://remix.run/) | Planned helpers to scan `FormData` in actions/loaders. |
99
- | SvelteKit *(planned)* | <img alt="SvelteKit" src="https://img.shields.io/badge/SvelteKit-FF3E00?style=for-the-badge&logo=svelte&logoColor=white" /> | [kit.svelte.dev](https://kit.svelte.dev/) | Planned utilities for `+server.ts` and actions. |
100
- | pnpm | <img alt="pnpm" src="https://img.shields.io/badge/pnpm-222222?style=for-the-badge&logo=pnpm&logoColor=white" /> | [pnpm.io](https://pnpm.io/) | Monorepo/workspace package manager. |
101
- | npm | <img alt="npm" src="https://img.shields.io/badge/npm-CB3837?style=for-the-badge&logo=npm&logoColor=white" /> | [npmjs.com](https://www.npmjs.com/) | Registry and install scripts. |
102
- | Vitest | <img alt="Vitest" src="https://img.shields.io/badge/Vitest-6E9F18?style=for-the-badge&logo=vitest&logoColor=white" /> | [vitest.dev](https://vitest.dev/) | Test runner for future E2E and unit tests. |
103
- | ESLint | <img alt="ESLint" src="https://img.shields.io/badge/ESLint-4B32C3?style=for-the-badge&logo=eslint&logoColor=white" /> | [eslint.org](https://eslint.org/) | Linting. |
104
- | Prettier | <img alt="Prettier" src="https://img.shields.io/badge/Prettier-F7B93E?style=for-the-badge&logo=prettier&logoColor=white" /> | [prettier.io](https://prettier.io/) | Code formatting. |
105
- | YARA | <img alt="YARA" src="https://img.shields.io/badge/YARA-2F855A?style=for-the-badge" /> | [virustotal.github.io/yara](https://virustotal.github.io/yara/) | Optional rule engine for advanced detections. |
106
- | file-type | <img alt="file-type" src="https://img.shields.io/badge/file--type-24292E?style=for-the-badge" /> | [sindresorhus/file-type](https://github.com/sindresorhus/file-type) | MIME sniffing (magic bytes) on buffers. |
107
- | unzipper | <img alt="unzipper" src="https://img.shields.io/badge/unzipper-24292E?style=for-the-badge" /> | [ZJONSSON/node-unzipper](https://github.com/ZJONSSON/node-unzipper) | ZIP processing with anti‑bomb limits and nested scan. |
108
- | Multer | <img alt="Multer" src="https://img.shields.io/badge/Multer-000000?style=for-the-badge" /> | [expressjs/multer](https://github.com/expressjs/multer) | In‑memory file buffers for Express/Koa demos. |
109
-
110
- ---
111
-
112
47
  ## Why pompelmi?
113
48
 
114
- - **Stop risky uploads**: quickly tells you if a file looks **clean**, **suspicious**, or **malicious**—and blocks it when needed.
115
- - **Easy to adopt**: drop‑in middlewares/handlers for popular frameworks (Express, Koa, Next.js, more coming).
116
- - **YARA when you need it**: plug your YARA rules for advanced detections, or start with a simple matcher.
117
- - **Real file checks**: extension whitelist, **MIME sniffing with fallback**, file size caps, and ZIP inspection with anti‑bomb limits.
118
- - **Local & private**: scans run in your app process. No cloud calls required.
119
- - **Typed and tiny**: TypeScript types included, ESM & CJS builds.
49
+ - **Block risky uploads at the edge** mark files as <em>clean</em>, <em>suspicious</em>, or <em>malicious</em> and stop them early.
50
+ - **YARA when you need it** plug in your rules; start simple and iterate.
51
+ - **Real checks** extension allow‑list, MIME sniffing (magic bytes), file size caps, and **ZIP** traversal with anti‑bomb limits.
52
+ - **No cloud required** scans run in‑process. Keep bytes private.
53
+ - **DX first** TypeScript types, ESM/CJS builds, minimal API.
120
54
 
121
- ---
122
-
123
- ## Features
124
-
125
- - **Node-first scanning** with optional **YARA** engine (native binaries are auto‑pulled by platform packages; no brew/apt for consumers).
126
- - **ZIP aware**: inspects archive contents with limits on entries, per‑entry size, total uncompressed size, and nesting depth.
127
- - **Policy filters**:
128
- - allowed extensions
129
- - allowed MIME types (with extension fallback)
130
- - max file size per upload
131
- - **Clear responses**:
132
- - success (200) with scan results
133
- - 4xx for policy violations (415/413)
134
- - 422 when verdict is suspicious/malicious
135
- - 503 on fail‑closed errors
136
- - **Observability**: structured `onScanEvent` callbacks (start/end/blocked/errors/archive_*).
137
- - **Browser support** via a **Remote Engine** (HTTP endpoint) that compiles rules and runs scans for you.
55
+ > Keywords: file upload security, malware scanning, YARA, Node.js, Express, Koa, Next.js, ZIP scanning
138
56
 
139
57
  ---
140
58
 
141
- ## Packages
142
59
 
143
- This is a monorepo. The following packages are included:
144
60
 
145
- | Package | NPM | Description |
146
- | --- | --- | --- |
147
- | **`pompelmi`** | <a href="https://github.com/pompelmi/pompelmi"><img src="https://img.shields.io/npm/v/pompelmi?label=pompelmi" alt="npm"/></a> | Core scanning library (Node + Remote Engine for browsers). |
148
- | **`@pompelmi/express-middleware`** | *(alpha)* | Express middleware that scans uploads and enforces policies. |
149
- | **`@pompelmi/koa-middleware`** | *(alpha)* | Koa middleware compatible with `@koa/multer`/`koa-body`. |
150
- | **`@pompelmi/next-upload`** | *(alpha)* | Next.js (App Router) `POST` handler factory for `/api/upload`. |
151
- | **(Planned)** `@pompelmi/fastify-plugin` | — | Fastify plugin with the same policies and ZIP support. |
152
- | **(Planned)** `@pompelmi/nestjs` | — | NestJS Guard/Interceptor module for uploads. |
153
- | **(Planned)** `@pompelmi/remix` | — | Remix helpers to scan `FormData` in actions/loaders. |
154
- | **(Planned)** `@pompelmi/hapi-plugin` | — | Hapi plugin with `onPreHandler`. |
155
- | **(Planned)** `@pompelmi/sveltekit` | — | SvelteKit utilities for `+server.ts` and actions. |
61
+ Optional dev deps used in examples:
156
62
 
157
- > Status: **alpha** — expect minor API refinements before a stable `0.2.0`.
63
+ ```bash
64
+ npm i -D tsx express multer @koa/router @koa/multer koa next
65
+ ```
158
66
 
159
67
  ---
160
68
 
161
69
  ## Quickstart
162
70
 
163
- ### Express (middleware)
71
+ ### Express
164
72
 
165
73
  ```ts
166
74
  import express from 'express';
@@ -170,7 +78,6 @@ import { createUploadGuard } from '@pompelmi/express-middleware';
170
78
  const app = express();
171
79
  const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 20 * 1024 * 1024 } });
172
80
 
173
- // Simple demo scanner (replace with YARA rules in production)
174
81
  const SimpleEicarScanner = {
175
82
  async scan(bytes: Uint8Array) {
176
83
  const text = Buffer.from(bytes).toString('utf8');
@@ -192,15 +99,13 @@ app.post(
192
99
  failClosed: true,
193
100
  onScanEvent: (ev) => console.log('[scan]', ev)
194
101
  }),
195
- (req, res) => {
196
- res.json({ ok: true, scan: (req as any).pompelmi ?? null });
197
- }
102
+ (req, res) => res.json({ ok: true, scan: (req as any).pompelmi ?? null })
198
103
  );
199
104
 
200
- app.listen(3000, () => console.log('demo on http://localhost:3000'));
105
+ app.listen(3000, () => console.log('http://localhost:3000'));
201
106
  ```
202
107
 
203
- ### Koa (middleware)
108
+ ### Koa
204
109
 
205
110
  ```ts
206
111
  import Koa from 'koa';
@@ -212,7 +117,9 @@ const app = new Koa();
212
117
  const router = new Router();
213
118
  const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 20 * 1024 * 1024 } });
214
119
 
215
- const SimpleEicarScanner = { /* same as above */ };
120
+ const SimpleEicarScanner = { async scan(b: Uint8Array){
121
+ return Buffer.from(b).toString('utf8').includes('EICAR') ? [{ rule: 'eicar_test' }] : [];
122
+ }};
216
123
 
217
124
  router.post(
218
125
  '/upload',
@@ -231,7 +138,7 @@ router.post(
231
138
  );
232
139
 
233
140
  app.use(router.routes()).use(router.allowedMethods());
234
- app.listen(3003, () => console.log('demo on http://localhost:3003'));
141
+ app.listen(3003, () => console.log('http://localhost:3003'));
235
142
  ```
236
143
 
237
144
  ### Next.js (App Router)
@@ -240,10 +147,12 @@ app.listen(3003, () => console.log('demo on http://localhost:3003'));
240
147
  // app/api/upload/route.ts
241
148
  import { createNextUploadHandler } from '@pompelmi/next-upload';
242
149
 
243
- export const runtime = 'nodejs'; // Next: Node runtime (not Edge)
244
- export const dynamic = 'force-dynamic'; // optional: avoid route cache
150
+ export const runtime = 'nodejs';
151
+ export const dynamic = 'force-dynamic';
245
152
 
246
- const SimpleEicarScanner = { /* same as above */ };
153
+ const SimpleEicarScanner = { async scan(b: Uint8Array){
154
+ return Buffer.from(b).toString('utf8').includes('EICAR') ? [{ rule: 'eicar_test' }] : [];
155
+ }};
247
156
 
248
157
  export const POST = createNextUploadHandler({
249
158
  scanner: SimpleEicarScanner,
@@ -259,33 +168,24 @@ export const POST = createNextUploadHandler({
259
168
 
260
169
  ---
261
170
 
262
- ## Framework Adapters
171
+ ## Adapters
263
172
 
264
- The adapters share the same behavior and defaults:
173
+ Use the adapter that matches your web framework. All adapters share the same policy options and scanning contract.
265
174
 
266
- - **Extension whitelist**
267
- - **MIME sniffing with extension fallback**
268
- - **Max file size**
269
- - **ZIP scanning** (entry count / per‑entry size / total uncompressed / depth)
270
- - **Timeout & concurrency** controls
271
- - **Fail‑closed** and **report‑only** modes
272
- - **Structured events** via `onScanEvent`
273
-
274
- **HTTP status codes**
275
-
276
- - `200` — accepted, includes `{ scan: { results: [...] } }`
277
- - `415` — `extension_not_allowed`, `mime_mismatch`, or `mime_not_allowed`
278
- - `413` — `file_too_large`
279
- - `422` — `blocked` with `verdict: suspicious|malicious`
280
- - `503` — `scanner_init_error` / `scan_error` (when `failClosed: true`)
175
+ | Framework | Package | Status |
176
+ | --- | --- | --- |
177
+ | Express | `@pompelmi/express-middleware` | alpha |
178
+ | Koa | `@pompelmi/koa-middleware` | alpha |
179
+ | Next.js (App Router) | `@pompelmi/next-upload` | alpha |
180
+ | Fastify | fastify plugin — planned |
181
+ | NestJS | nestjs — planned |
182
+ | Remix | remix — planned |
183
+ | hapi | hapi plugin — planned |
184
+ | SvelteKit | sveltekit — planned |
281
185
 
282
186
  ---
283
187
 
284
- ## Architecture & UML
285
-
286
- > **Note:** Diagrams are embedded as images via mermaid.ink so they render on GitHub, npm, and other Markdown viewers. The Mermaid source is included below each image.
287
- > **Tip:** To avoid parser issues across renderers, labels use quotes inside node shapes (e.g., `A["text"]`, `C{"text"}`) when they include parentheses, slashes, or other symbols.
288
-
188
+ ## Diagrams
289
189
 
290
190
  ### Upload scanning flow
291
191
  <p align="center">
@@ -369,102 +269,115 @@ flowchart LR
369
269
  ```
370
270
  </details>
371
271
 
272
+ ## Packages
273
+
274
+ | Package | NPM | Description |
275
+ | --- | --- | --- |
276
+ | **`pompelmi`** | <a href="https://www.npmjs.com/package/pompelmi"><img src="https://img.shields.io/npm/v/pompelmi?label=pompelmi" alt="npm"/></a> | Core scanner (Node + Remote Engine for browsers). |
277
+ | **`@pompelmi/express-middleware`** | *(alpha)* | Express middleware to scan uploads & enforce policies. |
278
+ | **`@pompelmi/koa-middleware`** | *(alpha)* | Koa middleware compatible with `@koa/multer`/`koa-body`. |
279
+ | **`@pompelmi/next-upload`** | *(alpha)* | Next.js App Router `POST` handler factory. |
280
+
281
+ > Status: **alpha** — small API refinements may happen before a stable milestone.
282
+
372
283
  ---
373
284
 
374
- ## API Overview
285
+ ## Configuration
286
+
287
+ All adapters accept a common set of options:
375
288
 
376
- ### Core (Node)
289
+ | Option | Type (TS) | Purpose |
290
+ | --- | --- | --- |
291
+ | `scanner` | `{ scan(bytes: Uint8Array): Promise<Match[]> }` | Your scanning engine. Return `[]` when clean; non‑empty to flag. |
292
+ | `includeExtensions` | `string[]` | Allow‑list of file extensions. Evaluated case‑insensitively. |
293
+ | `allowedMimeTypes` | `string[]` | Allow‑list of MIME types after magic‑byte sniffing. |
294
+ | `maxFileSizeBytes` | `number` | Per‑file size cap. Oversize files are rejected early. |
295
+ | `timeoutMs` | `number` | Per‑file scan timeout; guards against stuck scanners. |
296
+ | `concurrency` | `number` | How many files to scan in parallel. |
297
+ | `failClosed` | `boolean` | If `true`, errors/timeouts block the upload. |
298
+ | `onScanEvent` | `(event: unknown) => void` | Optional telemetry hook for logging/metrics. |
299
+
300
+ **Common recipes**
301
+
302
+ Allow only images up to 5 MB:
377
303
 
378
304
  ```ts
379
- import { scanDir } from 'pompelmi';
380
- import { resolve } from 'node:path';
381
-
382
- const opts = {
383
- enableYara: true,
384
- yaraRulesPath: resolve(process.cwd(), 'rules/demo.yar'),
385
- includeExtensions: ['.txt', '.bin'],
386
- maxFileSizeBytes: 10 * 1024 * 1024,
387
- yaraAsync: true,
388
- };
305
+ includeExtensions: ['png','jpg','jpeg','webp'],
306
+ allowedMimeTypes: ['image/png','image/jpeg','image/webp'],
307
+ maxFileSizeBytes: 5 * 1024 * 1024,
308
+ failClosed: true,
309
+ ```
310
+
311
+ ---
312
+
313
+ ## Quick test (EICAR)
314
+
315
+ Use the Express/Koa/Next examples above, then send the standard EICAR test file to verify that blocking works end‑to‑end.
389
316
 
390
- for await (const entry of scanDir('./some-folder', opts)) {
391
- console.log(entry.path, entry.yara?.verdict);
392
- }
317
+ **1) Generate the EICAR file (safe test string)**
318
+
319
+ Linux:
320
+
321
+ ```bash
322
+ echo 'WDVPIVAlQEFQWzRcUFpYNTQoUF4pN0NDKTd9JEVJQ0FSLVNUQU5EQVJELUFOVElWSVJVUy1URVNULUZJTEUhJEgrSCo=' | base64 -d > eicar.txt
393
323
  ```
394
324
 
395
- **NodeScanOptions**
325
+ macOS:
396
326
 
397
- ```ts
398
- type NodeScanOptions = {
399
- enableYara?: boolean;
400
- yaraRules?: string;
401
- yaraRulesPath?: string;
402
- includeExtensions?: string[];
403
- maxFileSizeBytes?: number;
404
- yaraAsync?: boolean;
405
- yaraPreferBuffer?: boolean;
406
- yaraSampleBytes?: number;
407
- };
327
+ ```bash
328
+ echo 'WDVPIVAlQEFQWzRcUFpYNTQoUF4pN0NDKTd9JEVJQ0FSLVNUQU5EQVJELUFOVElWSVJVUy1URVNULUZJTEUhJEgrSCo=' | base64 -D > eicar.txt
408
329
  ```
409
330
 
410
- ### Browser (Remote Engine)
331
+ **2) Send it to your endpoint**
411
332
 
412
- ```ts
413
- import { createRemoteEngine } from 'pompelmi';
414
-
415
- const RULES = `
416
- rule demo_contains_virus_literal {
417
- strings: $a = "virus" ascii nocase
418
- condition: $a
419
- }`;
420
-
421
- async function scanFileInBrowser(file: File) {
422
- const engine = await createRemoteEngine({
423
- endpoint: 'http://localhost:8787/api/yara/scan',
424
- mode: 'json-base64',
425
- rulesAsBase64: true,
426
- });
427
- const compiled = await engine.compile(RULES);
428
- const bytes = new Uint8Array(await file.arrayBuffer());
429
- const matches = await compiled.scan(bytes);
430
- console.log('REMOTE MATCHES:', matches);
431
- }
333
+ Express (default from the Quickstart):
334
+
335
+ ```bash
336
+ curl -F "file=@eicar.txt;type=text/plain" http://localhost:3000/upload -i
432
337
  ```
433
338
 
339
+ You should see an HTTP **422 Unprocessable Entity** (blocked by policy). Clean files return **200 OK**. Pre‑filter failures (size/ext/MIME) should return a **4xx**. Adapt these conventions to your app as needed.
340
+
434
341
  ---
435
342
 
436
- ## Security & Disclaimer
343
+ ## Security notes
437
344
 
438
- - The library **reads** bytes; it does not execute files.
439
- - YARA detections depend on the **rules you supply**. Expect false positives/negatives.
440
- - Always run scanning in a controlled environment with appropriate security controls.
441
- - ZIP scanning enforces limits (entries, per‑entry size, total uncompressed, nesting) to reduce archivebomb risk.
345
+ - The library **reads** bytes; it never executes files.
346
+ - YARA detections depend on the **rules you provide**; expect some false positives/negatives.
347
+ - ZIP scanning applies limits (entries, per‑entry size, total uncompressed, nesting) to reduce archive‑bomb risk.
348
+ - Prefer running scans in a **dedicated process/container** for defensein‑depth.
442
349
 
443
350
  ---
444
351
 
445
- ## Contributing
352
+ ## Star history
353
+
354
+ [![Star History Chart](https://api.star-history.com/svg?repos=pompelmi/pompelmi&type=Date)](https://star-history.com/#pompelmi/pompelmi&Date)
355
+
356
+ ---
446
357
 
447
- PRs and issues are welcome!
358
+ ## FAQ
448
359
 
449
- - Run build & smoke tests:
450
- ```bash
451
- npm run build
452
- npm run yara:int:smoke
453
- ```
454
- - Keep commits focused and well described.
455
- - For new features, please add or adjust tests.
360
+ **Do I need YARA?**
361
+ No. `scanner` is pluggable. The examples use a minimal scanner for clarity; you can call out to a YARA engine or any other detector you prefer.
362
+
363
+ **Where do the results live?**
364
+ In the examples, the guard attaches scan data to the request context (e.g. `req.pompelmi` in Express, `ctx.pompelmi` in Koa). In Next.js, include the results in your JSON response as you see fit.
365
+
366
+ **Why 422 for blocked files?**
367
+ Using **422** to signal a policy violation keeps it distinct from transport errors; it’s a common pattern. Use the codes that best match your API guidelines.
368
+
369
+ **Are ZIP bombs handled?**
370
+ Archives are traversed with limits to reduce archive‑bomb risk. Keep your size limits conservative and prefer `failClosed: true` in production.
456
371
 
457
372
  ---
458
373
 
459
- ## Versioning
374
+ ## Contributing
460
375
 
461
- Channel: **`0.5.0`**
462
- Expect minor API changes before a stable `0.5.0`.
376
+ PRs and issues welcome! Start with:
463
377
 
464
- Suggested publish:
465
378
  ```bash
466
- npm version 0.5.0
467
- npm publish --tag next
379
+ pnpm -r build
380
+ pnpm -r lint
468
381
  ```
469
382
 
470
383
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pompelmi",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Prototipo di scanner di file lato cliente",
5
5
  "main": "dist/pompelmi.cjs.js",
6
6
  "module": "dist/pompelmi.esm.js",