pompelmi 0.4.0 → 0.5.1

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 -227
  2. package/package.json +2 -1
package/README.md CHANGED
@@ -1,165 +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
7
  <h1 align="center">pompelmi</h1>
13
8
 
14
- <p align="center">
15
- Lightweight file upload scanner with optional <strong>YARA</strong> rules.<br/>
16
- Works out‑of‑the‑box on <strong>Node.js</strong>; supports <strong>browser</strong> via a simple HTTP “remote engine”.
17
- </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>
18
10
 
19
- <!--
20
11
  <p align="center">
21
- <img alt="Node.js" src="https://img.shields.io/badge/Node.js-339933?style=for-the-badge&logo=node.js&logoColor=white" />
22
- <img alt="TypeScript" src="https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=white" />
23
- <img alt="Express" src="https://img.shields.io/badge/Express-000000?style=for-the-badge&logo=express&logoColor=white" />
24
- <img alt="Koa" src="https://img.shields.io/badge/Koa-33333D?style=for-the-badge&logo=nodedotjs&logoColor=white" />
25
- <img alt="Next.js" src="https://img.shields.io/badge/Next.js-000000?style=for-the-badge&logo=nextdotjs&logoColor=white" />
26
- <img alt="Fastify (planned)" src="https://img.shields.io/badge/Fastify-000000?style=for-the-badge&logo=fastify&logoColor=white" />
27
- <img alt="NestJS (planned)" src="https://img.shields.io/badge/NestJS-E0234E?style=for-the-badge&logo=nestjs&logoColor=white" />
28
- <img alt="Remix (planned)" src="https://img.shields.io/badge/Remix-000000?style=for-the-badge&logo=remix&logoColor=white" />
29
- <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">
30
21
  </p>
31
22
 
32
23
  <p align="center">
33
- <img alt="pnpm" src="https://img.shields.io/badge/pnpm-222222?style=for-the-badge&logo=pnpm&logoColor=white" />
34
- <img alt="npm" src="https://img.shields.io/badge/npm-CB3837?style=for-the-badge&logo=npm&logoColor=white" />
35
- <img alt="Vitest" src="https://img.shields.io/badge/Vitest-6E9F18?style=for-the-badge&logo=vitest&logoColor=white" />
36
- <img alt="ESLint" src="https://img.shields.io/badge/ESLint-4B32C3?style=for-the-badge&logo=eslint&logoColor=white" />
37
- <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>
38
34
  </p>
39
35
 
40
-
41
- -->
42
-
43
- <p align="center">
44
- <a href="https://www.npmjs.com/package/pompelmi">
45
- <img alt="npm" src="https://img.shields.io/npm/v/pompelmi?label=pompelmi">
46
- </a>
47
- <a href="https://www.npmjs.com/package/pompelmi">
48
- <img alt="downloads" src="https://img.shields.io/npm/d18m/pompelmi?label=downloads">
49
- </a>
50
- <a href="https://github.com/pompelmi/pompelmi/blob/main/LICENSE">
51
- <img alt="license" src="https://img.shields.io/npm/l/pompelmi">
52
- </a>
53
- <img alt="node" src="https://img.shields.io/node/v/pompelmi">
54
- <img alt="types" src="https://img.shields.io/badge/types-TypeScript-3178C6?logo=typescript&logoColor=white">
55
- <img alt="status" src="https://img.shields.io/badge/channel-alpha-orange">
56
- <img alt="coverage" src="https://img.shields.io/github/actions/workflow/status/pompelmi/pompelmi/ci.yml?branch=main&label=coverage&style=flat-square" />
57
-
58
- </p>
36
+ ---
59
37
 
60
38
  ## Installation
61
39
 
62
40
  ```bash
63
41
  # core library
64
42
  npm i pompelmi
65
-
66
- # typical dev deps used in examples (optional)
67
- npm i -D tsx express multer cors
43
+ # or
44
+ pnpm add pompelmi
68
45
  ```
69
46
 
70
- <p align="center">
71
- <a href="#why-pompelmi">Why</a> •
72
- <a href="#installation">Installation</a> •
73
- <a href="#technologies--tools">Technologies & Tools</a> •
74
- <a href="#features">Features</a> •
75
- <a href="#packages">Packages</a> •
76
- <a href="#quickstart">Quickstart</a> •
77
- <a href="#framework-adapters">Framework Adapters</a> •
78
- <a href="#architecture--uml">Architecture & UML</a> •
79
- <a href="#api-overview">API</a> •
80
- <a href="#security--disclaimer">Security</a> •
81
- <a href="#license">License</a>
82
- </p>
83
-
84
- ---
85
-
86
- ## Technologies & Tools
87
-
88
- | Technology | Badge | Link | Description |
89
- | --- | --- | --- | --- |
90
- | 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. |
91
- | 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. |
92
- | 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`. |
93
- | 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`. |
94
- | 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`. |
95
- | 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. |
96
- | 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. |
97
- | 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. |
98
- | 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. |
99
- | 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. |
100
- | 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. |
101
- | 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. |
102
- | 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. |
103
- | 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. |
104
- | 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. |
105
- | 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. |
106
- | 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. |
107
- | 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. |
108
-
109
- ---
110
-
111
47
  ## Why pompelmi?
112
48
 
113
- - **Stop risky uploads**: quickly tells you if a file looks **clean**, **suspicious**, or **malicious**—and blocks it when needed.
114
- - **Easy to adopt**: drop‑in middlewares/handlers for popular frameworks (Express, Koa, Next.js, more coming).
115
- - **YARA when you need it**: plug your YARA rules for advanced detections, or start with a simple matcher.
116
- - **Real file checks**: extension whitelist, **MIME sniffing with fallback**, file size caps, and ZIP inspection with anti‑bomb limits.
117
- - **Local & private**: scans run in your app process. No cloud calls required.
118
- - **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.
119
54
 
120
- ---
121
-
122
- ## Features
123
-
124
- - **Node-first scanning** with optional **YARA** engine (native binaries are auto‑pulled by platform packages; no brew/apt for consumers).
125
- - **ZIP aware**: inspects archive contents with limits on entries, per‑entry size, total uncompressed size, and nesting depth.
126
- - **Policy filters**:
127
- - allowed extensions
128
- - allowed MIME types (with extension fallback)
129
- - max file size per upload
130
- - **Clear responses**:
131
- - success (200) with scan results
132
- - 4xx for policy violations (415/413)
133
- - 422 when verdict is suspicious/malicious
134
- - 503 on fail‑closed errors
135
- - **Observability**: structured `onScanEvent` callbacks (start/end/blocked/errors/archive_*).
136
- - **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
137
56
 
138
57
  ---
139
58
 
140
- ## Packages
141
59
 
142
- This is a monorepo. The following packages are included:
143
60
 
144
- | Package | NPM | Description |
145
- | --- | --- | --- |
146
- | **`pompelmi`** | <a href="https://www.npmjs.com/package/pompelmi"><img src="https://img.shields.io/npm/v/pompelmi?label=pompelmi" alt="npm"/></a> | Core scanning library (Node + Remote Engine for browsers). |
147
- | **`@pompelmi/express-middleware`** | *(alpha)* | Express middleware that scans uploads and enforces policies. |
148
- | **`@pompelmi/koa-middleware`** | *(alpha)* | Koa middleware compatible with `@koa/multer`/`koa-body`. |
149
- | **`@pompelmi/next-upload`** | *(alpha)* | Next.js (App Router) `POST` handler factory for `/api/upload`. |
150
- | **(Planned)** `@pompelmi/fastify-plugin` | — | Fastify plugin with the same policies and ZIP support. |
151
- | **(Planned)** `@pompelmi/nestjs` | — | NestJS Guard/Interceptor module for uploads. |
152
- | **(Planned)** `@pompelmi/remix` | — | Remix helpers to scan `FormData` in actions/loaders. |
153
- | **(Planned)** `@pompelmi/hapi-plugin` | — | Hapi plugin with `onPreHandler`. |
154
- | **(Planned)** `@pompelmi/sveltekit` | — | SvelteKit utilities for `+server.ts` and actions. |
61
+ Optional dev deps used in examples:
155
62
 
156
- > 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
+ ```
157
66
 
158
67
  ---
159
68
 
160
69
  ## Quickstart
161
70
 
162
- ### Express (middleware)
71
+ ### Express
163
72
 
164
73
  ```ts
165
74
  import express from 'express';
@@ -169,7 +78,6 @@ import { createUploadGuard } from '@pompelmi/express-middleware';
169
78
  const app = express();
170
79
  const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 20 * 1024 * 1024 } });
171
80
 
172
- // Simple demo scanner (replace with YARA rules in production)
173
81
  const SimpleEicarScanner = {
174
82
  async scan(bytes: Uint8Array) {
175
83
  const text = Buffer.from(bytes).toString('utf8');
@@ -191,15 +99,13 @@ app.post(
191
99
  failClosed: true,
192
100
  onScanEvent: (ev) => console.log('[scan]', ev)
193
101
  }),
194
- (req, res) => {
195
- res.json({ ok: true, scan: (req as any).pompelmi ?? null });
196
- }
102
+ (req, res) => res.json({ ok: true, scan: (req as any).pompelmi ?? null })
197
103
  );
198
104
 
199
- app.listen(3000, () => console.log('demo on http://localhost:3000'));
105
+ app.listen(3000, () => console.log('http://localhost:3000'));
200
106
  ```
201
107
 
202
- ### Koa (middleware)
108
+ ### Koa
203
109
 
204
110
  ```ts
205
111
  import Koa from 'koa';
@@ -211,7 +117,9 @@ const app = new Koa();
211
117
  const router = new Router();
212
118
  const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 20 * 1024 * 1024 } });
213
119
 
214
- 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
+ }};
215
123
 
216
124
  router.post(
217
125
  '/upload',
@@ -230,7 +138,7 @@ router.post(
230
138
  );
231
139
 
232
140
  app.use(router.routes()).use(router.allowedMethods());
233
- app.listen(3003, () => console.log('demo on http://localhost:3003'));
141
+ app.listen(3003, () => console.log('http://localhost:3003'));
234
142
  ```
235
143
 
236
144
  ### Next.js (App Router)
@@ -239,10 +147,12 @@ app.listen(3003, () => console.log('demo on http://localhost:3003'));
239
147
  // app/api/upload/route.ts
240
148
  import { createNextUploadHandler } from '@pompelmi/next-upload';
241
149
 
242
- export const runtime = 'nodejs'; // Next: Node runtime (not Edge)
243
- export const dynamic = 'force-dynamic'; // optional: avoid route cache
150
+ export const runtime = 'nodejs';
151
+ export const dynamic = 'force-dynamic';
244
152
 
245
- 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
+ }};
246
156
 
247
157
  export const POST = createNextUploadHandler({
248
158
  scanner: SimpleEicarScanner,
@@ -258,33 +168,24 @@ export const POST = createNextUploadHandler({
258
168
 
259
169
  ---
260
170
 
261
- ## Framework Adapters
262
-
263
- The adapters share the same behavior and defaults:
171
+ ## Adapters
264
172
 
265
- - **Extension whitelist**
266
- - **MIME sniffing with extension fallback**
267
- - **Max file size**
268
- - **ZIP scanning** (entry count / per‑entry size / total uncompressed / depth)
269
- - **Timeout & concurrency** controls
270
- - **Fail‑closed** and **report‑only** modes
271
- - **Structured events** via `onScanEvent`
173
+ Use the adapter that matches your web framework. All adapters share the same policy options and scanning contract.
272
174
 
273
- **HTTP status codes**
274
-
275
- - `200` accepted, includes `{ scan: { results: [...] } }`
276
- - `415` `extension_not_allowed`, `mime_mismatch`, or `mime_not_allowed`
277
- - `413` `file_too_large`
278
- - `422` `blocked` with `verdict: suspicious|malicious`
279
- - `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 |
280
185
 
281
186
  ---
282
187
 
283
- ## Architecture & UML
284
-
285
- > **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.
286
- > **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.
287
-
188
+ ## Diagrams
288
189
 
289
190
  ### Upload scanning flow
290
191
  <p align="center">
@@ -368,102 +269,115 @@ flowchart LR
368
269
  ```
369
270
  </details>
370
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
+
371
283
  ---
372
284
 
373
- ## API Overview
285
+ ## Configuration
286
+
287
+ All adapters accept a common set of options:
288
+
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**
374
301
 
375
- ### Core (Node)
302
+ Allow only images up to 5 MB:
376
303
 
377
304
  ```ts
378
- import { scanDir } from 'pompelmi';
379
- import { resolve } from 'node:path';
380
-
381
- const opts = {
382
- enableYara: true,
383
- yaraRulesPath: resolve(process.cwd(), 'rules/demo.yar'),
384
- includeExtensions: ['.txt', '.bin'],
385
- maxFileSizeBytes: 10 * 1024 * 1024,
386
- yaraAsync: true,
387
- };
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.
316
+
317
+ **1) Generate the EICAR file (safe test string)**
388
318
 
389
- for await (const entry of scanDir('./some-folder', opts)) {
390
- console.log(entry.path, entry.yara?.verdict);
391
- }
319
+ Linux:
320
+
321
+ ```bash
322
+ echo 'WDVPIVAlQEFQWzRcUFpYNTQoUF4pN0NDKTd9JEVJQ0FSLVNUQU5EQVJELUFOVElWSVJVUy1URVNULUZJTEUhJEgrSCo=' | base64 -d > eicar.txt
392
323
  ```
393
324
 
394
- **NodeScanOptions**
325
+ macOS:
395
326
 
396
- ```ts
397
- type NodeScanOptions = {
398
- enableYara?: boolean;
399
- yaraRules?: string;
400
- yaraRulesPath?: string;
401
- includeExtensions?: string[];
402
- maxFileSizeBytes?: number;
403
- yaraAsync?: boolean;
404
- yaraPreferBuffer?: boolean;
405
- yaraSampleBytes?: number;
406
- };
327
+ ```bash
328
+ echo 'WDVPIVAlQEFQWzRcUFpYNTQoUF4pN0NDKTd9JEVJQ0FSLVNUQU5EQVJELUFOVElWSVJVUy1URVNULUZJTEUhJEgrSCo=' | base64 -D > eicar.txt
407
329
  ```
408
330
 
409
- ### Browser (Remote Engine)
331
+ **2) Send it to your endpoint**
410
332
 
411
- ```ts
412
- import { createRemoteEngine } from 'pompelmi';
413
-
414
- const RULES = `
415
- rule demo_contains_virus_literal {
416
- strings: $a = "virus" ascii nocase
417
- condition: $a
418
- }`;
419
-
420
- async function scanFileInBrowser(file: File) {
421
- const engine = await createRemoteEngine({
422
- endpoint: 'http://localhost:8787/api/yara/scan',
423
- mode: 'json-base64',
424
- rulesAsBase64: true,
425
- });
426
- const compiled = await engine.compile(RULES);
427
- const bytes = new Uint8Array(await file.arrayBuffer());
428
- const matches = await compiled.scan(bytes);
429
- console.log('REMOTE MATCHES:', matches);
430
- }
333
+ Express (default from the Quickstart):
334
+
335
+ ```bash
336
+ curl -F "file=@eicar.txt;type=text/plain" http://localhost:3000/upload -i
431
337
  ```
432
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
+
433
341
  ---
434
342
 
435
- ## Security & Disclaimer
343
+ ## Security notes
436
344
 
437
- - The library **reads** bytes; it does not execute files.
438
- - YARA detections depend on the **rules you supply**. Expect false positives/negatives.
439
- - Always run scanning in a controlled environment with appropriate security controls.
440
- - 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.
441
349
 
442
350
  ---
443
351
 
444
- ## 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
+ ---
445
357
 
446
- PRs and issues are welcome!
358
+ ## FAQ
447
359
 
448
- - Run build & smoke tests:
449
- ```bash
450
- npm run build
451
- npm run yara:int:smoke
452
- ```
453
- - Keep commits focused and well described.
454
- - 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.
455
371
 
456
372
  ---
457
373
 
458
- ## Versioning
374
+ ## Contributing
459
375
 
460
- Channel: **`0.4.0`**
461
- Expect minor API changes before a stable `0.4.0`.
376
+ PRs and issues welcome! Start with:
462
377
 
463
- Suggested publish:
464
378
  ```bash
465
- npm version 0.4.0
466
- npm publish --tag next
379
+ pnpm -r build
380
+ pnpm -r lint
467
381
  ```
468
382
 
469
383
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pompelmi",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "Prototipo di scanner di file lato cliente",
5
5
  "main": "dist/pompelmi.cjs.js",
6
6
  "module": "dist/pompelmi.esm.js",
@@ -105,6 +105,7 @@
105
105
  "workspaces": [
106
106
  "packages/*"
107
107
  ],
108
+ "packageManager": "pnpm@9.12.0",
108
109
  "repository": {
109
110
  "type": "git",
110
111
  "url": "https://github.com/pompelmi/pompelmi"