pompelmi 0.35.4 → 1.0.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/.claude/settings.local.json +40 -0
- package/LICENSE +12 -18
- package/README.md +160 -143
- package/eslint.config.mjs +8 -0
- package/package.json +26 -251
- package/src/ClamAVDatabaseUpdater.js +48 -0
- package/src/ClamAVInstaller.js +49 -0
- package/src/ClamAVScanner.js +31 -0
- package/src/InstallerCommand.js +11 -0
- package/src/config.js +22 -0
- package/src/constants.js +3 -0
- package/src/favicon.ico +0 -0
- package/src/grapefruit.png +0 -0
- package/src/index.js +5 -0
- package/CHANGELOG.md +0 -71
- package/dist/pompelmi.audit.cjs +0 -128
- package/dist/pompelmi.audit.cjs.map +0 -1
- package/dist/pompelmi.audit.esm.js +0 -107
- package/dist/pompelmi.audit.esm.js.map +0 -1
- package/dist/pompelmi.browser.cjs +0 -1493
- package/dist/pompelmi.browser.cjs.map +0 -1
- package/dist/pompelmi.browser.esm.js +0 -1467
- package/dist/pompelmi.browser.esm.js.map +0 -1
- package/dist/pompelmi.cjs +0 -2535
- package/dist/pompelmi.cjs.map +0 -1
- package/dist/pompelmi.esm.js +0 -2469
- package/dist/pompelmi.esm.js.map +0 -1
- package/dist/pompelmi.hooks.cjs +0 -75
- package/dist/pompelmi.hooks.cjs.map +0 -1
- package/dist/pompelmi.hooks.esm.js +0 -72
- package/dist/pompelmi.hooks.esm.js.map +0 -1
- package/dist/pompelmi.policy-packs.cjs +0 -240
- package/dist/pompelmi.policy-packs.cjs.map +0 -1
- package/dist/pompelmi.policy-packs.esm.js +0 -232
- package/dist/pompelmi.policy-packs.esm.js.map +0 -1
- package/dist/pompelmi.quarantine.cjs +0 -317
- package/dist/pompelmi.quarantine.cjs.map +0 -1
- package/dist/pompelmi.quarantine.esm.js +0 -293
- package/dist/pompelmi.quarantine.esm.js.map +0 -1
- package/dist/pompelmi.react.cjs +0 -1524
- package/dist/pompelmi.react.cjs.map +0 -1
- package/dist/pompelmi.react.esm.js +0 -1497
- package/dist/pompelmi.react.esm.js.map +0 -1
- package/dist/types/audit.d.ts +0 -84
- package/dist/types/browser-index.d.ts +0 -29
- package/dist/types/config.d.ts +0 -143
- package/dist/types/engines/dynamic-taint.d.ts +0 -102
- package/dist/types/engines/hybrid-orchestrator.d.ts +0 -65
- package/dist/types/engines/hybrid-taint-integration.d.ts +0 -129
- package/dist/types/engines/taint-policies.d.ts +0 -84
- package/dist/types/hipaa-compliance.d.ts +0 -110
- package/dist/types/hooks.d.ts +0 -89
- package/dist/types/index.d.ts +0 -29
- package/dist/types/magic.d.ts +0 -7
- package/dist/types/node/scanDir.d.ts +0 -30
- package/dist/types/policy-packs.d.ts +0 -98
- package/dist/types/policy.d.ts +0 -12
- package/dist/types/presets.d.ts +0 -72
- package/dist/types/quarantine/index.d.ts +0 -18
- package/dist/types/quarantine/storage.d.ts +0 -77
- package/dist/types/quarantine/types.d.ts +0 -78
- package/dist/types/quarantine/workflow.d.ts +0 -97
- package/dist/types/react-index.d.ts +0 -13
- package/dist/types/risk.d.ts +0 -18
- package/dist/types/scan/remote.d.ts +0 -12
- package/dist/types/scan.d.ts +0 -17
- package/dist/types/scanners/common-heuristics.d.ts +0 -14
- package/dist/types/scanners/zip-bomb-guard.d.ts +0 -9
- package/dist/types/scanners/zipTraversalGuard.d.ts +0 -19
- package/dist/types/src/audit.d.ts +0 -84
- package/dist/types/src/browser-index.d.ts +0 -29
- package/dist/types/src/config.d.ts +0 -143
- package/dist/types/src/engines/dynamic-taint.d.ts +0 -102
- package/dist/types/src/engines/hybrid-orchestrator.d.ts +0 -65
- package/dist/types/src/engines/hybrid-taint-integration.d.ts +0 -129
- package/dist/types/src/engines/taint-policies.d.ts +0 -84
- package/dist/types/src/hipaa-compliance.d.ts +0 -110
- package/dist/types/src/hooks.d.ts +0 -89
- package/dist/types/src/index.d.ts +0 -29
- package/dist/types/src/magic.d.ts +0 -7
- package/dist/types/src/node/scanDir.d.ts +0 -30
- package/dist/types/src/policy-packs.d.ts +0 -98
- package/dist/types/src/policy.d.ts +0 -12
- package/dist/types/src/presets.d.ts +0 -72
- package/dist/types/src/quarantine/index.d.ts +0 -18
- package/dist/types/src/quarantine/storage.d.ts +0 -77
- package/dist/types/src/quarantine/types.d.ts +0 -78
- package/dist/types/src/quarantine/workflow.d.ts +0 -97
- package/dist/types/src/react-index.d.ts +0 -13
- package/dist/types/src/risk.d.ts +0 -18
- package/dist/types/src/scan/remote.d.ts +0 -12
- package/dist/types/src/scan.d.ts +0 -17
- package/dist/types/src/scanners/common-heuristics.d.ts +0 -14
- package/dist/types/src/scanners/zip-bomb-guard.d.ts +0 -9
- package/dist/types/src/scanners/zipTraversalGuard.d.ts +0 -19
- package/dist/types/src/stream.d.ts +0 -10
- package/dist/types/src/types/decompilation.d.ts +0 -96
- package/dist/types/src/types/taint-tracking.d.ts +0 -495
- package/dist/types/src/types.d.ts +0 -48
- package/dist/types/src/useFileScanner.d.ts +0 -15
- package/dist/types/src/utils/advanced-detection.d.ts +0 -21
- package/dist/types/src/utils/batch-scanner.d.ts +0 -62
- package/dist/types/src/utils/cache-manager.d.ts +0 -95
- package/dist/types/src/utils/export.d.ts +0 -51
- package/dist/types/src/utils/performance-metrics.d.ts +0 -68
- package/dist/types/src/utils/threat-intelligence.d.ts +0 -96
- package/dist/types/src/validate.d.ts +0 -7
- package/dist/types/src/verdict.d.ts +0 -2
- package/dist/types/src/yara/browser.d.ts +0 -7
- package/dist/types/src/yara/index.d.ts +0 -17
- package/dist/types/src/yara/node.d.ts +0 -2
- package/dist/types/src/yara/remote.d.ts +0 -10
- package/dist/types/src/yara-bridge.d.ts +0 -3
- package/dist/types/src/zip.d.ts +0 -13
- package/dist/types/stream.d.ts +0 -10
- package/dist/types/types/decompilation.d.ts +0 -96
- package/dist/types/types/taint-tracking.d.ts +0 -495
- package/dist/types/types.d.ts +0 -48
- package/dist/types/useFileScanner.d.ts +0 -15
- package/dist/types/utils/advanced-detection.d.ts +0 -21
- package/dist/types/utils/batch-scanner.d.ts +0 -62
- package/dist/types/utils/cache-manager.d.ts +0 -95
- package/dist/types/utils/export.d.ts +0 -51
- package/dist/types/utils/performance-metrics.d.ts +0 -68
- package/dist/types/utils/threat-intelligence.d.ts +0 -96
- package/dist/types/validate.d.ts +0 -7
- package/dist/types/verdict.d.ts +0 -2
- package/dist/types/yara/browser.d.ts +0 -7
- package/dist/types/yara/index.d.ts +0 -17
- package/dist/types/yara/node.d.ts +0 -2
- package/dist/types/yara/remote.d.ts +0 -10
- package/dist/types/yara-bridge.d.ts +0 -3
- package/dist/types/zip.d.ts +0 -13
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(node:*)",
|
|
5
|
+
"Bash(echo \"EXIT:$?\")",
|
|
6
|
+
"Bash(echo \"EXIT_CODE:$?\")",
|
|
7
|
+
"Bash(tee /tmp/pompelmi_test_out.txt)",
|
|
8
|
+
"Bash(echo \"done: $?\")",
|
|
9
|
+
"Bash(ls /Users/tommy/pompelmi/pompelmi/*.md)",
|
|
10
|
+
"Bash(ls /Users/tommy/pompelmi/pompelmi/LICENSE*)",
|
|
11
|
+
"WebSearch",
|
|
12
|
+
"WebFetch(domain:pompelmi.app)",
|
|
13
|
+
"WebFetch(domain:news.ycombinator.com)",
|
|
14
|
+
"WebFetch(domain:dev.to)",
|
|
15
|
+
"WebFetch(domain:socket.dev)",
|
|
16
|
+
"WebFetch(domain:helpnetsecurity.com)",
|
|
17
|
+
"WebFetch(domain:nodeweekly.com)",
|
|
18
|
+
"WebFetch(domain:bytes.dev)",
|
|
19
|
+
"WebFetch(domain:www.helpnetsecurity.com)",
|
|
20
|
+
"WebFetch(domain:www.enveil.com)",
|
|
21
|
+
"WebFetch(domain:img.helpnetsecurity.com)",
|
|
22
|
+
"WebFetch(domain:logo.clearbit.com)",
|
|
23
|
+
"WebFetch(domain:cdn.brandfetch.io)",
|
|
24
|
+
"WebFetch(domain:cooperpress.com)",
|
|
25
|
+
"WebFetch(domain:wirexsystems.com)",
|
|
26
|
+
"WebFetch(domain:www.zlti.com)",
|
|
27
|
+
"Bash(gh run:*)",
|
|
28
|
+
"Bash(gh api:*)",
|
|
29
|
+
"WebFetch(domain:api.github.com)",
|
|
30
|
+
"WebFetch(domain:raw.githubusercontent.com)",
|
|
31
|
+
"Bash(git -C /Users/tommy/pompelmi/pompelmi status)",
|
|
32
|
+
"Bash(git -C /Users/tommy/pompelmi/pompelmi log --oneline -5)",
|
|
33
|
+
"Bash(git pull:*)",
|
|
34
|
+
"Bash(git add:*)",
|
|
35
|
+
"Bash(git commit -m ':*)",
|
|
36
|
+
"Bash(git push:*)",
|
|
37
|
+
"Bash(grep -E '\\\\.\\(png|svg|ico|webp\\)$')"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
}
|
package/LICENSE
CHANGED
|
@@ -1,21 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
ISC License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) 2024 pompelmi contributors
|
|
4
4
|
|
|
5
|
-
Permission
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
11
8
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,187 +1,204 @@
|
|
|
1
|
-
<div align="center">
|
|
2
|
-
<img src="./assets/logo.svg" alt="Pompelmi logo" width="120" />
|
|
3
|
-
|
|
4
|
-
<h1>Pompelmi</h1>
|
|
5
|
-
|
|
6
|
-
<p><strong>Secure file uploads in Node.js before storage.</strong></p>
|
|
7
|
-
|
|
8
|
-
<p>
|
|
9
|
-
Open-source route-level upload security for Node.js teams that need to
|
|
10
|
-
inspect untrusted files before disk, object storage, previews, or
|
|
11
|
-
downstream parsers.
|
|
12
|
-
</p>
|
|
13
|
-
|
|
14
|
-
<p><code>clean</code> · <code>suspicious</code> · <code>malicious</code></p>
|
|
15
|
-
|
|
16
|
-
<p>
|
|
17
|
-
MIME spoofing · risky archives · document and binary signals · optional
|
|
18
|
-
YARA
|
|
19
|
-
</p>
|
|
20
|
-
|
|
21
|
-
<p>
|
|
22
|
-
<sub>Express · Next.js · NestJS · Fastify · Koa · Nuxt/Nitro · S3 quarantine flows · CI/CD</sub>
|
|
23
|
-
</p>
|
|
24
|
-
|
|
25
|
-
<p>
|
|
26
|
-
<a href="https://www.npmjs.com/package/pompelmi"><img alt="npm version" src="https://img.shields.io/npm/v/pompelmi" /></a>
|
|
27
|
-
<a href="https://github.com/pompelmi/pompelmi/actions/workflows/ci.yml"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/pompelmi/pompelmi/ci.yml?label=ci" /></a>
|
|
28
|
-
<a href="https://codecov.io/gh/pompelmi/pompelmi"><img alt="codecov" src="https://codecov.io/gh/pompelmi/pompelmi/graph/badge.svg" /></a>
|
|
29
|
-
<a href="https://github.com/pompelmi/pompelmi/stargazers"><img alt="GitHub stars" src="https://img.shields.io/github/stars/pompelmi/pompelmi?style=social" /></a>
|
|
30
|
-
</p>
|
|
31
|
-
|
|
32
|
-
<p>
|
|
33
|
-
<a href="https://pompelmi.app/"><strong>Docs</strong></a>
|
|
34
|
-
·
|
|
35
|
-
<a href="https://pompelmi.app/getting-started/"><strong>Getting started</strong></a>
|
|
36
|
-
·
|
|
37
|
-
<a href="https://pompelmi.app/#browser-preview"><strong>Browser preview</strong></a>
|
|
38
|
-
·
|
|
39
|
-
<a href="./examples/demo"><strong>Express demo</strong></a>
|
|
40
|
-
·
|
|
41
|
-
<a href="./examples/README.md"><strong>Examples</strong></a>
|
|
42
|
-
</p>
|
|
43
|
-
|
|
44
|
-
<p><sub>Node.js 18+ · MIT</sub></p>
|
|
45
|
-
</div>
|
|
46
|
-
|
|
47
1
|
<p align="center">
|
|
48
|
-
<
|
|
49
|
-
<a href="https://nodeweekly.com/issues/594">Node Weekly</a>,
|
|
50
|
-
<a href="https://stackoverflow.blog/2026/02/23/defense-against-uploads-oss-file-scanner-pompelmi/">Stack Overflow</a>,
|
|
51
|
-
<a href="https://www.helpnetsecurity.com/2026/02/02/pompelmi-open-source-secure-file-upload-scanning-node-js/">Help Net Security</a>,
|
|
52
|
-
<a href="https://github.com/sorrycc/awesome-javascript">Awesome JavaScript</a>,
|
|
53
|
-
and
|
|
54
|
-
<a href="https://github.com/dzharii/awesome-typescript">Awesome TypeScript</a>
|
|
2
|
+
<img src="./src/grapefruit.png" width="96" alt="pompelmi logo">
|
|
55
3
|
</p>
|
|
56
4
|
|
|
5
|
+
<h1 align="center">pompelmi</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center"><strong>ClamAV for humans</strong></p>
|
|
8
|
+
|
|
57
9
|
<p align="center">
|
|
58
|
-
<
|
|
10
|
+
<a href="https://www.npmjs.com/package/pompelmi"><img src="https://img.shields.io/npm/v/pompelmi.svg" alt="npm version"></a>
|
|
11
|
+
<img src="https://img.shields.io/badge/license-ISC-blue.svg" alt="license">
|
|
12
|
+
<img src="https://img.shields.io/badge/platform-macOS%20%7C%20Linux%20%7C%20Windows-lightgrey.svg" alt="platform">
|
|
13
|
+
<a href="https://www.npmjs.com/package/pompelmi?activeTab=dependencies"><img src="https://img.shields.io/badge/dependencies-0-brightgreen" alt="zero dependencies"></a>
|
|
59
14
|
</p>
|
|
60
15
|
|
|
61
|
-
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
A minimal Node.js wrapper around [ClamAV](https://www.clamav.net/) that scans any file and returns a plain string: `"Clean"`, `"Malicious"`, or `"ScanError"`. No daemons. No cloud. No native bindings.
|
|
62
19
|
|
|
63
|
-
##
|
|
20
|
+
## Table of contents
|
|
64
21
|
|
|
65
|
-
|
|
22
|
+
- [Quickstart](#quickstart)
|
|
23
|
+
- [How it works](#how-it-works)
|
|
24
|
+
- [API](#api)
|
|
25
|
+
- [pompelmi.scan()](#pompelmiscanfilepath)
|
|
26
|
+
- [Internal utilities](#internal-utilities)
|
|
27
|
+
- [ClamAVInstaller()](#clamavinstaller)
|
|
28
|
+
- [updateClamAVDatabase()](#updateclamavdatabase)
|
|
29
|
+
- [Supported platforms](#supported-platforms)
|
|
30
|
+
- [Installing ClamAV manually](#installing-clamav-manually)
|
|
31
|
+
- [Testing](#testing)
|
|
32
|
+
- [Contributing](#contributing)
|
|
33
|
+
- [Security](#security)
|
|
34
|
+
- [License](#license)
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Quickstart
|
|
66
39
|
|
|
67
40
|
```bash
|
|
68
41
|
npm install pompelmi
|
|
69
42
|
```
|
|
70
43
|
|
|
71
|
-
|
|
44
|
+
```js
|
|
45
|
+
const pompelmi = require('pompelmi');
|
|
72
46
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
mimeType: req.file.mimetype,
|
|
79
|
-
policy: STRICT_PUBLIC_UPLOAD,
|
|
80
|
-
failClosed: true,
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
if (report.verdict !== 'clean') {
|
|
84
|
-
return res.status(422).json({
|
|
85
|
-
error: 'Upload blocked',
|
|
86
|
-
verdict: report.verdict,
|
|
87
|
-
reasons: report.reasons,
|
|
88
|
-
});
|
|
47
|
+
const result = await pompelmi.scan('/path/to/file.zip');
|
|
48
|
+
// "Clean" | "Malicious" | "ScanError"
|
|
49
|
+
|
|
50
|
+
if (result === 'Malicious') {
|
|
51
|
+
throw new Error('File rejected: malware detected');
|
|
89
52
|
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## How it works
|
|
56
|
+
|
|
57
|
+
1. **Validate** — pompelmi checks that the argument is a string and that the file exists before spawning anything.
|
|
58
|
+
2. **Scan** — pompelmi spawns `clamscan --no-summary <filePath>` as a child process and reads the exit code.
|
|
59
|
+
3. **Map** — the exit code is mapped to a result string. Unknown codes and spawn errors reject the Promise.
|
|
60
|
+
|
|
61
|
+
No stdout parsing. No regex. No surprises.
|
|
90
62
|
|
|
91
|
-
|
|
63
|
+
## API
|
|
64
|
+
|
|
65
|
+
### `pompelmi.scan(filePath)`
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
pompelmi.scan(filePath: string): Promise<"Clean" | "Malicious" | "ScanError">
|
|
92
69
|
```
|
|
93
70
|
|
|
94
|
-
|
|
71
|
+
| Parameter | Type | Description |
|
|
72
|
+
|------------|----------|-----------------------------------------|
|
|
73
|
+
| `filePath` | `string` | Absolute or relative path to the file. |
|
|
74
|
+
|
|
75
|
+
**Resolves** to one of:
|
|
76
|
+
|
|
77
|
+
| Result | ClamAV exit code | Meaning |
|
|
78
|
+
|---------------|:---:|------------------------------------------------------------------------------------------------------|
|
|
79
|
+
| `"Clean"` | 0 | No threats found. |
|
|
80
|
+
| `"Malicious"` | 1 | A known virus or malware signature was matched. |
|
|
81
|
+
| `"ScanError"` | 2 | The scan itself failed (I/O error, encrypted archive, permission denied). File status is unknown — treat as untrusted. |
|
|
82
|
+
|
|
83
|
+
**Rejects** with an `Error` in these cases:
|
|
84
|
+
|
|
85
|
+
| Condition | Error message |
|
|
86
|
+
|-----------|---------------|
|
|
87
|
+
| `filePath` is not a string | `filePath must be a string` |
|
|
88
|
+
| File does not exist | `File not found: <path>` |
|
|
89
|
+
| `clamscan` is not in PATH | `ENOENT` (from the OS) |
|
|
90
|
+
| ClamAV returns an unknown exit code | `Unexpected exit code: N` |
|
|
91
|
+
| `clamscan` process is killed by a signal | `Process killed by signal: <SIGNAL>` |
|
|
92
|
+
|
|
93
|
+
**Example — full error handling:**
|
|
94
|
+
|
|
95
|
+
```js
|
|
96
|
+
const pompelmi = require('pompelmi');
|
|
97
|
+
const path = require('path');
|
|
98
|
+
|
|
99
|
+
async function safeScan(filePath) {
|
|
100
|
+
try {
|
|
101
|
+
const result = await pompelmi.scan(path.resolve(filePath));
|
|
102
|
+
|
|
103
|
+
if (result === 'ScanError') {
|
|
104
|
+
// The scan could not complete — treat the file as untrusted.
|
|
105
|
+
console.warn('Scan incomplete, rejecting file as precaution.');
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return result; // "Clean" or "Malicious"
|
|
110
|
+
} catch (err) {
|
|
111
|
+
console.error('Scan failed:', err.message);
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Internal utilities
|
|
118
|
+
|
|
119
|
+
These modules are not part of the public npm API but are used internally to set up the ClamAV environment on a fresh machine.
|
|
95
120
|
|
|
96
|
-
|
|
121
|
+
### `ClamAVInstaller()`
|
|
122
|
+
|
|
123
|
+
Installs ClamAV using the platform's native package manager. Skips silently if ClamAV is already installed.
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
ClamAVInstaller(): Promise<string>
|
|
127
|
+
```
|
|
97
128
|
|
|
98
|
-
-
|
|
99
|
-
-
|
|
100
|
-
- It gives Node.js teams a practical way to build secure file uploads with route-level decisions instead of bolting checks on after persistence.
|
|
129
|
+
- Resolves with a status message string on success or skip.
|
|
130
|
+
- Rejects if the install process exits with a non-zero code or if spawning the package manager fails.
|
|
101
131
|
|
|
102
|
-
|
|
132
|
+
| Platform | Package manager | Command |
|
|
133
|
+
|----------|----------------|---------|
|
|
134
|
+
| macOS | Homebrew | `brew install clamav` |
|
|
135
|
+
| Linux | apt-get | `sudo apt-get install -y clamav clamav-daemon` |
|
|
136
|
+
| Windows | Chocolatey | `choco install clamav -y` |
|
|
103
137
|
|
|
104
|
-
|
|
105
|
-
- Risky archives such as ZIP bombs, traversal attempts, deep nesting, and entry-count abuse
|
|
106
|
-
- Risky document and binary signals such as PDF actions, Office macro hints, PE headers, and polyglot files
|
|
107
|
-
- Optional YARA-based matches when you want malware scanning uploads as part of the flow
|
|
108
|
-
- Verdicts and reasons you can use for fail-closed routes, quarantine flows, and auditability
|
|
138
|
+
### `updateClamAVDatabase()`
|
|
109
139
|
|
|
110
|
-
|
|
140
|
+
Downloads or updates the ClamAV virus definition database by running `freshclam`. Skips if `main.cvd` is already present on disk.
|
|
111
141
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
4. Persist, quarantine, or reject based on the route's rules.
|
|
142
|
+
```ts
|
|
143
|
+
updateClamAVDatabase(): Promise<string>
|
|
144
|
+
```
|
|
116
145
|
|
|
117
|
-
|
|
146
|
+
- Resolves with a status message string on success or skip.
|
|
147
|
+
- Rejects if `freshclam` exits with a non-zero code or if spawning fails.
|
|
118
148
|
|
|
119
|
-
|
|
149
|
+
| Platform | Database path |
|
|
150
|
+
|----------|--------------|
|
|
151
|
+
| macOS | `/usr/local/share/clamav/main.cvd` |
|
|
152
|
+
| Linux | `/var/lib/clamav/main.cvd` |
|
|
153
|
+
| Windows | `C:\ProgramData\ClamAV\main.cvd` |
|
|
120
154
|
|
|
121
|
-
|
|
122
|
-
| --- | --- | --- |
|
|
123
|
-
| Browser MIME and extension checks | Fast client-side hints and UX feedback | Client MIME and filenames are easy to spoof |
|
|
124
|
-
| File-type or magic-byte validation only | Confirming a file looks like the claimed type | Archive abuse, risky internal structure, and route policy decisions |
|
|
125
|
-
| Antivirus or YARA only | Known malicious matches and signature-style detection | Route context, spoofing checks, and before-storage handling |
|
|
126
|
-
| Pompelmi at the upload route | Node.js file upload security, scan files before storage, and verdict-driven workflow decisions | It is not a full antivirus replacement on its own |
|
|
155
|
+
## Supported platforms
|
|
127
156
|
|
|
128
|
-
|
|
157
|
+
| OS | ClamAV install | DB path checked |
|
|
158
|
+
|---------|---------------------------|------------------------------------|
|
|
159
|
+
| macOS | `brew install clamav` | `/usr/local/share/clamav/main.cvd` |
|
|
160
|
+
| Linux | `apt-get install clamav` | `/var/lib/clamav/main.cvd` |
|
|
161
|
+
| Windows | `choco install clamav -y` | `C:\ProgramData\ClamAV\main.cvd` |
|
|
129
162
|
|
|
130
|
-
|
|
131
|
-
| --- | --- |
|
|
132
|
-
| Express | [Docs](https://pompelmi.app/how-to/express/) · [Minimal example](./examples/express-minimal) · [Demo](./examples/demo) |
|
|
133
|
-
| Next.js | [Docs](https://pompelmi.app/how-to/nextjs/) · [Example](./examples/next-app-router) · [Package](./packages/next-upload) |
|
|
134
|
-
| NestJS | [Docs](https://pompelmi.app/how-to/nestjs/) · [Package](./packages/nestjs) · [Example app](./examples/nestjs-app) |
|
|
135
|
-
| Fastify | [Docs](https://pompelmi.app/how-to/fastify/) · [Package](./packages/fastify-plugin) |
|
|
136
|
-
| Koa | [Docs](https://pompelmi.app/how-to/koa/) · [Package](./packages/koa-middleware) |
|
|
137
|
-
| Nuxt/Nitro | [Docs](https://pompelmi.app/how-to/nuxt-nitro/) · [Example](./examples/nuxt-nitro) |
|
|
138
|
-
| S3 / object storage | [Tutorial](https://pompelmi.app/tutorials/secure-s3-presigned-uploads-with-malware-scanning/) · [Use case](https://pompelmi.app/use-cases/object-storage-promotion-workflows/) |
|
|
139
|
-
| CI/CD | [Use case](https://pompelmi.app/use-cases/cicd-artifact-scanning/) · [Blog](https://pompelmi.app/blog/cicd-scan-build-artifacts/) |
|
|
163
|
+
ClamAV must be installed on the host system. pompelmi does not bundle or download it.
|
|
140
164
|
|
|
141
|
-
##
|
|
165
|
+
## Installing ClamAV manually
|
|
142
166
|
|
|
143
|
-
|
|
167
|
+
```bash
|
|
168
|
+
# macOS
|
|
169
|
+
brew install clamav && freshclam
|
|
144
170
|
|
|
145
|
-
|
|
146
|
-
-
|
|
147
|
-
- [Examples index](./examples/README.md) for framework-specific and production-oriented patterns
|
|
148
|
-
- [Docs home](https://pompelmi.app/) for guides, comparisons, use cases, and tutorials
|
|
171
|
+
# Linux (Debian / Ubuntu)
|
|
172
|
+
sudo apt-get install -y clamav clamav-daemon && sudo freshclam
|
|
149
173
|
|
|
150
|
-
|
|
174
|
+
# Windows (Chocolatey)
|
|
175
|
+
choco install clamav -y
|
|
176
|
+
```
|
|
151
177
|
|
|
152
|
-
|
|
178
|
+
## Testing
|
|
153
179
|
|
|
154
|
-
|
|
180
|
+
```bash
|
|
181
|
+
npm test
|
|
182
|
+
```
|
|
155
183
|
|
|
156
|
-
|
|
184
|
+
The test suite has two parts:
|
|
157
185
|
|
|
158
|
-
-
|
|
159
|
-
-
|
|
160
|
-
- [Pompelmi: Open-source secure file upload scanning for Node.js](https://www.helpnetsecurity.com/2026/02/02/pompelmi-open-source-secure-file-upload-scanning-node-js/) — Help Net Security
|
|
161
|
-
- [Awesome JavaScript](https://github.com/sorrycc/awesome-javascript)
|
|
162
|
-
- [Awesome TypeScript](https://github.com/dzharii/awesome-typescript)
|
|
163
|
-
- [See all mentions](https://pompelmi.app/featured-in/)
|
|
186
|
+
- **Unit tests** (`test/unit.test.js`) — run with Node's built-in test runner. Mock `cross-spawn` and platform dependencies; no ClamAV installation required.
|
|
187
|
+
- **Integration tests** (`test/scan.test.js`) — spawn real `clamscan` processes against EICAR test files. Skipped automatically if `clamscan` is not found in PATH.
|
|
164
188
|
|
|
165
|
-
|
|
189
|
+
## Contributing
|
|
166
190
|
|
|
167
|
-
|
|
191
|
+
1. Fork the repository at [https://github.com/pompelmi/pompelmi](https://github.com/pompelmi/pompelmi).
|
|
192
|
+
2. Create a feature branch: `git checkout -b feat/your-change`.
|
|
193
|
+
3. Make your changes and run `npm test` to verify.
|
|
194
|
+
4. Open a pull request against `main`.
|
|
168
195
|
|
|
169
|
-
|
|
170
|
-
- [Use cases](https://pompelmi.app/use-cases/)
|
|
171
|
-
- [Comparisons](https://pompelmi.app/comparisons/)
|
|
172
|
-
- [Tutorials](https://pompelmi.app/tutorials/)
|
|
173
|
-
- [Browser preview](https://pompelmi.app/#browser-preview)
|
|
174
|
-
- [Featured in](https://pompelmi.app/featured-in/)
|
|
175
|
-
- [Translations](https://pompelmi.app/translations/)
|
|
196
|
+
Please read [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md) before contributing.
|
|
176
197
|
|
|
177
|
-
##
|
|
198
|
+
## Security
|
|
178
199
|
|
|
179
|
-
|
|
200
|
+
To report a vulnerability, see [SECURITY.md](./SECURITY.md).
|
|
180
201
|
|
|
181
|
-
##
|
|
202
|
+
## License
|
|
182
203
|
|
|
183
|
-
|
|
184
|
-
- [Security](./SECURITY.md)
|
|
185
|
-
- [Roadmap](./ROADMAP.md)
|
|
186
|
-
- [GitHub Discussions](https://github.com/pompelmi/pompelmi/discussions)
|
|
187
|
-
- [License](./LICENSE)
|
|
204
|
+
[ISC](./LICENSE) — © pompelmi contributors
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import js from "@eslint/js";
|
|
2
|
+
import globals from "globals";
|
|
3
|
+
import { defineConfig } from "eslint/config";
|
|
4
|
+
|
|
5
|
+
export default defineConfig([
|
|
6
|
+
{ files: ["**/*.{js,mjs,cjs}"], plugins: { js }, extends: ["js/recommended"], languageOptions: { globals: globals.node } },
|
|
7
|
+
{ files: ["**/*.js"], languageOptions: { sourceType: "commonjs" } },
|
|
8
|
+
]);
|