pompelmi 0.35.5 → 1.1.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 +45 -0
- package/LICENSE +12 -18
- package/README.md +174 -181
- 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 +37 -0
- package/src/ClamdScanner.js +81 -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/test_out.txt +74 -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 -1549
- package/dist/pompelmi.browser.cjs.map +0 -1
- package/dist/pompelmi.browser.esm.js +0 -1523
- package/dist/pompelmi.browser.esm.js.map +0 -1
- package/dist/pompelmi.cjs +0 -2591
- package/dist/pompelmi.cjs.map +0 -1
- package/dist/pompelmi.esm.js +0 -2525
- 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 -1580
- package/dist/pompelmi.react.cjs.map +0 -1
- package/dist/pompelmi.react.esm.js +0 -1553
- 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 -11
- 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,45 @@
|
|
|
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
|
+
"Bash(ls -d /Users/tommy/pompelmi/pompelmi/*/)",
|
|
39
|
+
"Bash(node --test test/unit.test.js)",
|
|
40
|
+
"Bash(echo \"exit:$?\")",
|
|
41
|
+
"Read(//tmp/**)",
|
|
42
|
+
"Bash(tee /Users/tommy/pompelmi/pompelmi/test_out.txt)"
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
}
|
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,228 +1,221 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
> [!CAUTION]
|
|
5
|
-
> **v0.x is an experimental prototype. Do NOT use it in production.**
|
|
6
|
-
>
|
|
7
|
-
> Pompelmi v0.x has **known Event Loop blocking issues** and makes overreaching
|
|
8
|
-
> "scanner" claims that it cannot keep. This version is in **soft maintenance
|
|
9
|
-
> only** — no new features, security patches only.
|
|
10
|
-
>
|
|
11
|
-
> ---
|
|
12
|
-
>
|
|
13
|
-
> **Pompelmi is pivoting for v1.0.**
|
|
14
|
-
>
|
|
15
|
-
> The new identity is a **dead-simple, one-line utility wrapper** for Node.js
|
|
16
|
-
> file uploads — not an "ultimate malware scanner". v1.0 will bundle standard,
|
|
17
|
-
> well-understood checks (size limits, magic bytes, basic heuristics) into a
|
|
18
|
-
> single convenient call that returns a traffic-light verdict:
|
|
19
|
-
>
|
|
20
|
-
> ```js
|
|
21
|
-
> const result = await pompelmi.scan(file);
|
|
22
|
-
> // returns 'green', 'suspicious', or 'malicious'
|
|
23
|
-
> ```
|
|
24
|
-
>
|
|
25
|
-
> No magic promises. No impossible guarantees. Just saved developer time.
|
|
26
|
-
>
|
|
27
|
-
> Follow the pivot → [GitHub Issues](https://github.com/pompelmi/pompelmi/issues) · [Discussions](https://github.com/pompelmi/pompelmi/discussions)
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="./src/grapefruit.png" width="96" alt="pompelmi logo">
|
|
3
|
+
</p>
|
|
28
4
|
|
|
29
|
-
|
|
5
|
+
<h1 align="center">pompelmi</h1>
|
|
30
6
|
|
|
31
|
-
<
|
|
32
|
-
<img src="./assets/logo.svg" alt="Pompelmi logo" width="120" />
|
|
33
|
-
|
|
34
|
-
<h1>Pompelmi</h1>
|
|
35
|
-
|
|
36
|
-
<p><strong>Secure file uploads in Node.js before storage.</strong></p>
|
|
37
|
-
|
|
38
|
-
<p>
|
|
39
|
-
Open-source route-level upload security for Node.js teams that need to
|
|
40
|
-
inspect untrusted files before disk, object storage, previews, or
|
|
41
|
-
downstream parsers.
|
|
42
|
-
</p>
|
|
43
|
-
|
|
44
|
-
<p><code>clean</code> · <code>suspicious</code> · <code>malicious</code></p>
|
|
45
|
-
|
|
46
|
-
<p>
|
|
47
|
-
MIME spoofing · risky archives · document and binary signals · optional
|
|
48
|
-
YARA
|
|
49
|
-
</p>
|
|
50
|
-
|
|
51
|
-
<p>
|
|
52
|
-
<sub>Express · Next.js · NestJS · Fastify · Koa · Nuxt/Nitro · S3 quarantine flows · CI/CD</sub>
|
|
53
|
-
</p>
|
|
54
|
-
|
|
55
|
-
<p>
|
|
56
|
-
<a href="https://www.npmjs.com/package/pompelmi"><img alt="npm version" src="https://img.shields.io/npm/v/pompelmi" /></a>
|
|
57
|
-
<a href="https://www.npmjs.com/package/pompelmi"><img alt="npm total downloads" src="https://img.shields.io/npm/dt/pompelmi" /></a>
|
|
58
|
-
<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>
|
|
59
|
-
<a href="https://codecov.io/gh/pompelmi/pompelmi"><img alt="codecov" src="https://codecov.io/gh/pompelmi/pompelmi/graph/badge.svg" /></a>
|
|
60
|
-
<a href="https://github.com/pompelmi/pompelmi/stargazers"><img alt="GitHub stars" src="https://img.shields.io/github/stars/pompelmi/pompelmi?style=social" /></a>
|
|
61
|
-
<a href="https://github.com/pompelmi/pompelmi/blob/main/LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-green.svg" /></a>
|
|
62
|
-
<a href="https://nodejs.org"><img alt="Node.js 18+" src="https://img.shields.io/badge/node-%3E%3D18-brightgreen" /></a>
|
|
63
|
-
</p>
|
|
64
|
-
|
|
65
|
-
> If Pompelmi fits how you think about upload security at the route
|
|
66
|
-
> level, starring the repo helps other Node.js teams find it.
|
|
67
|
-
> [★ Star on GitHub](https://github.com/pompelmi/pompelmi/stargazers)
|
|
68
|
-
|
|
69
|
-
<p>
|
|
70
|
-
<a href="https://pompelmi.app/"><strong>Docs</strong></a>
|
|
71
|
-
·
|
|
72
|
-
<a href="https://pompelmi.app/getting-started/"><strong>Getting started</strong></a>
|
|
73
|
-
·
|
|
74
|
-
<a href="https://pompelmi.app/#browser-preview"><strong>Browser preview</strong></a>
|
|
75
|
-
·
|
|
76
|
-
<a href="./examples/demo"><strong>Express demo</strong></a>
|
|
77
|
-
·
|
|
78
|
-
<a href="./examples/README.md"><strong>Examples</strong></a>
|
|
79
|
-
</p>
|
|
80
|
-
|
|
81
|
-
<p><sub>Node.js 18+ · MIT</sub></p>
|
|
82
|
-
</div>
|
|
7
|
+
<p align="center"><strong>ClamAV for humans</strong></p>
|
|
83
8
|
|
|
84
9
|
<p align="center">
|
|
85
|
-
<
|
|
86
|
-
<
|
|
87
|
-
<
|
|
88
|
-
<a href="https://www.
|
|
89
|
-
<a href="https://github.com/sorrycc/awesome-javascript">Awesome JavaScript</a>,
|
|
90
|
-
<a href="https://github.com/dzharii/awesome-typescript">Awesome TypeScript</a>,
|
|
91
|
-
<a href="https://bytes.dev/archives/429">Bytes</a>,
|
|
92
|
-
and
|
|
93
|
-
<a href="https://www.detectionengineering.net/p/det-eng-weekly-124">Detection Engineering Weekly</a>
|
|
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>
|
|
94
14
|
</p>
|
|
95
15
|
|
|
96
|
-
|
|
16
|
+
---
|
|
97
17
|
|
|
98
|
-
|
|
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.
|
|
19
|
+
|
|
20
|
+
## Table of contents
|
|
21
|
+
|
|
22
|
+
- [Quickstart](#quickstart)
|
|
23
|
+
- [How it works](#how-it-works)
|
|
24
|
+
- [API](#api)
|
|
25
|
+
- [pompelmi.scan()](#pompelmiscanfilepath-options)
|
|
26
|
+
- [Docker / remote scanning](#docker--remote-scanning)
|
|
27
|
+
- [Internal utilities](#internal-utilities)
|
|
28
|
+
- [ClamAVInstaller()](#clamavinstaller)
|
|
29
|
+
- [updateClamAVDatabase()](#updateclamavdatabase)
|
|
30
|
+
- [Supported platforms](#supported-platforms)
|
|
31
|
+
- [Installing ClamAV manually](#installing-clamav-manually)
|
|
32
|
+
- [Testing](#testing)
|
|
33
|
+
- [Contributing](#contributing)
|
|
34
|
+
- [Security](#security)
|
|
35
|
+
- [License](#license)
|
|
99
36
|
|
|
100
|
-
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Quickstart
|
|
101
40
|
|
|
102
41
|
```bash
|
|
103
42
|
npm install pompelmi
|
|
104
43
|
```
|
|
105
44
|
|
|
106
|
-
|
|
45
|
+
```js
|
|
46
|
+
const pompelmi = require('pompelmi');
|
|
107
47
|
|
|
108
|
-
|
|
109
|
-
|
|
48
|
+
const result = await pompelmi.scan('/path/to/file.zip');
|
|
49
|
+
// "Clean" | "Malicious" | "ScanError"
|
|
110
50
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
51
|
+
if (result === 'Malicious') {
|
|
52
|
+
throw new Error('File rejected: malware detected');
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## How it works
|
|
57
|
+
|
|
58
|
+
1. **Validate** — pompelmi checks that the argument is a string and that the file exists before spawning anything.
|
|
59
|
+
2. **Scan** — pompelmi spawns `clamscan --no-summary <filePath>` as a child process and reads the exit code.
|
|
60
|
+
3. **Map** — the exit code is mapped to a result string. Unknown codes and spawn errors reject the Promise.
|
|
61
|
+
|
|
62
|
+
No stdout parsing. No regex. No surprises.
|
|
63
|
+
|
|
64
|
+
## API
|
|
65
|
+
|
|
66
|
+
### `pompelmi.scan(filePath, [options])`
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
pompelmi.scan(filePath: string, options?: { host?: string; port?: number; timeout?: number }): Promise<"Clean" | "Malicious" | "ScanError">
|
|
70
|
+
```
|
|
117
71
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
72
|
+
| Parameter | Type | Description |
|
|
73
|
+
|------------|----------|-----------------------------------------|
|
|
74
|
+
| `filePath` | `string` | Absolute or relative path to the file. |
|
|
75
|
+
| `options` | `object` | Optional. Omit to use the local `clamscan` CLI. Pass `host` / `port` to scan via a clamd TCP socket instead. See [docs/api.md](./docs/api.md) for the full reference. |
|
|
76
|
+
|
|
77
|
+
**Resolves** to one of:
|
|
78
|
+
|
|
79
|
+
| Result | ClamAV exit code | Meaning |
|
|
80
|
+
|---------------|:---:|------------------------------------------------------------------------------------------------------|
|
|
81
|
+
| `"Clean"` | 0 | No threats found. |
|
|
82
|
+
| `"Malicious"` | 1 | A known virus or malware signature was matched. |
|
|
83
|
+
| `"ScanError"` | 2 | The scan itself failed (I/O error, encrypted archive, permission denied). File status is unknown — treat as untrusted. |
|
|
84
|
+
|
|
85
|
+
**Rejects** with an `Error` in these cases:
|
|
86
|
+
|
|
87
|
+
| Condition | Error message |
|
|
88
|
+
|-----------|---------------|
|
|
89
|
+
| `filePath` is not a string | `filePath must be a string` |
|
|
90
|
+
| File does not exist | `File not found: <path>` |
|
|
91
|
+
| `clamscan` is not in PATH | `ENOENT` (from the OS) |
|
|
92
|
+
| ClamAV returns an unknown exit code | `Unexpected exit code: N` |
|
|
93
|
+
| `clamscan` process is killed by a signal | `Process killed by signal: <SIGNAL>` |
|
|
94
|
+
|
|
95
|
+
**Example — full error handling:**
|
|
96
|
+
|
|
97
|
+
```js
|
|
98
|
+
const pompelmi = require('pompelmi');
|
|
99
|
+
const path = require('path');
|
|
100
|
+
|
|
101
|
+
async function safeScan(filePath) {
|
|
102
|
+
try {
|
|
103
|
+
const result = await pompelmi.scan(path.resolve(filePath));
|
|
104
|
+
|
|
105
|
+
if (result === 'ScanError') {
|
|
106
|
+
// The scan could not complete — treat the file as untrusted.
|
|
107
|
+
console.warn('Scan incomplete, rejecting file as precaution.');
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return result; // "Clean" or "Malicious"
|
|
112
|
+
} catch (err) {
|
|
113
|
+
console.error('Scan failed:', err.message);
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
124
116
|
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Docker / remote scanning
|
|
120
|
+
|
|
121
|
+
If ClamAV runs in a Docker container (or anywhere on the network), pass `host` and `port` — everything else stays the same.
|
|
125
122
|
|
|
126
|
-
|
|
123
|
+
```js
|
|
124
|
+
const result = await pompelmi.scan('/path/to/upload.zip', {
|
|
125
|
+
host: '127.0.0.1',
|
|
126
|
+
port: 3310,
|
|
127
|
+
});
|
|
127
128
|
```
|
|
128
129
|
|
|
129
|
-
|
|
130
|
+
See [docs/docker.md](./docs/docker.md) for the `docker-compose.yml` snippet and first-boot notes.
|
|
130
131
|
|
|
131
|
-
|
|
132
|
+
---
|
|
132
133
|
|
|
133
|
-
|
|
134
|
-
- Pompelmi keeps the first trust decision inside the application path, where the route still knows the file class, trust level, storage path, and failure mode.
|
|
135
|
-
- It gives Node.js teams a practical way to build secure file uploads with route-level decisions instead of bolting checks on after persistence.
|
|
134
|
+
## Internal utilities
|
|
136
135
|
|
|
137
|
-
|
|
136
|
+
These modules are not part of the public npm API but are used internally to set up the ClamAV environment on a fresh machine.
|
|
138
137
|
|
|
139
|
-
|
|
138
|
+
### `ClamAVInstaller()`
|
|
140
139
|
|
|
141
|
-
|
|
142
|
-
- Risky archives such as ZIP bombs, traversal attempts, deep nesting, and entry-count abuse
|
|
143
|
-
- Risky document and binary signals such as PDF actions, Office macro hints, PE headers, and polyglot files
|
|
144
|
-
- Optional YARA-based matches when you want malware scanning uploads as part of the flow
|
|
145
|
-
- Verdicts and reasons you can use for fail-closed routes, quarantine flows, and auditability
|
|
140
|
+
Installs ClamAV using the platform's native package manager. Skips silently if ClamAV is already installed.
|
|
146
141
|
|
|
147
|
-
|
|
142
|
+
```ts
|
|
143
|
+
ClamAVInstaller(): Promise<string>
|
|
144
|
+
```
|
|
148
145
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
3. Act on the verdict: `clean`, `suspicious`, or `malicious`.
|
|
152
|
-
4. Persist, quarantine, or reject based on the route's rules.
|
|
146
|
+
- Resolves with a status message string on success or skip.
|
|
147
|
+
- Rejects if the install process exits with a non-zero code or if spawning the package manager fails.
|
|
153
148
|
|
|
154
|
-
|
|
149
|
+
| Platform | Package manager | Command |
|
|
150
|
+
|----------|----------------|---------|
|
|
151
|
+
| macOS | Homebrew | `brew install clamav` |
|
|
152
|
+
| Linux | apt-get | `sudo apt-get install -y clamav clamav-daemon` |
|
|
153
|
+
| Windows | Chocolatey | `choco install clamav -y` |
|
|
155
154
|
|
|
156
|
-
|
|
155
|
+
### `updateClamAVDatabase()`
|
|
157
156
|
|
|
158
|
-
|
|
159
|
-
| --- | --- | --- |
|
|
160
|
-
| Browser MIME and extension checks | Fast client-side hints and UX feedback | Client MIME and filenames are easy to spoof |
|
|
161
|
-
| File-type or magic-byte validation only | Confirming a file looks like the claimed type | Archive abuse, risky internal structure, and route policy decisions |
|
|
162
|
-
| Antivirus or YARA only | Known malicious matches and signature-style detection | Route context, spoofing checks, and before-storage handling |
|
|
163
|
-
| 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 |
|
|
157
|
+
Downloads or updates the ClamAV virus definition database by running `freshclam`. Skips if `main.cvd` is already present on disk.
|
|
164
158
|
|
|
165
|
-
|
|
159
|
+
```ts
|
|
160
|
+
updateClamAVDatabase(): Promise<string>
|
|
161
|
+
```
|
|
166
162
|
|
|
167
|
-
|
|
163
|
+
- Resolves with a status message string on success or skip.
|
|
164
|
+
- Rejects if `freshclam` exits with a non-zero code or if spawning fails.
|
|
168
165
|
|
|
169
|
-
|
|
|
170
|
-
|
|
171
|
-
|
|
|
172
|
-
|
|
|
173
|
-
|
|
|
174
|
-
| Fastify | [Docs](https://pompelmi.app/how-to/fastify/) · [Package](./packages/fastify-plugin) |
|
|
175
|
-
| Koa | [Docs](https://pompelmi.app/how-to/koa/) · [Package](./packages/koa-middleware) |
|
|
176
|
-
| Nuxt/Nitro | [Docs](https://pompelmi.app/how-to/nuxt-nitro/) · [Example](./examples/nuxt-nitro) |
|
|
177
|
-
| 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/) |
|
|
178
|
-
| CI/CD | [Use case](https://pompelmi.app/use-cases/cicd-artifact-scanning/) · [Blog](https://pompelmi.app/blog/cicd-scan-build-artifacts/) |
|
|
166
|
+
| Platform | Database path |
|
|
167
|
+
|----------|--------------|
|
|
168
|
+
| macOS | `/usr/local/share/clamav/main.cvd` |
|
|
169
|
+
| Linux | `/var/lib/clamav/main.cvd` |
|
|
170
|
+
| Windows | `C:\ProgramData\ClamAV\main.cvd` |
|
|
179
171
|
|
|
180
|
-
##
|
|
172
|
+
## Supported platforms
|
|
181
173
|
|
|
182
|
-
|
|
174
|
+
| OS | ClamAV install | DB path checked |
|
|
175
|
+
|---------|---------------------------|------------------------------------|
|
|
176
|
+
| macOS | `brew install clamav` | `/usr/local/share/clamav/main.cvd` |
|
|
177
|
+
| Linux | `apt-get install clamav` | `/var/lib/clamav/main.cvd` |
|
|
178
|
+
| Windows | `choco install clamav -y` | `C:\ProgramData\ClamAV\main.cvd` |
|
|
183
179
|
|
|
184
|
-
|
|
185
|
-
- [Express demo](./examples/demo) for a tiny upload gate that returns `clean`, `suspicious`, or `malicious` before storage
|
|
186
|
-
- [Examples index](./examples/README.md) for framework-specific and production-oriented patterns
|
|
187
|
-
- [Docs home](https://pompelmi.app/) for guides, comparisons, use cases, and tutorials
|
|
180
|
+
ClamAV must be installed on the host system. pompelmi does not bundle or download it.
|
|
188
181
|
|
|
189
|
-
|
|
182
|
+
## Installing ClamAV manually
|
|
190
183
|
|
|
191
|
-
|
|
184
|
+
```bash
|
|
185
|
+
# macOS
|
|
186
|
+
brew install clamav && freshclam
|
|
187
|
+
|
|
188
|
+
# Linux (Debian / Ubuntu)
|
|
189
|
+
sudo apt-get install -y clamav clamav-daemon && sudo freshclam
|
|
190
|
+
|
|
191
|
+
# Windows (Chocolatey)
|
|
192
|
+
choco install clamav -y
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Testing
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
npm test
|
|
199
|
+
```
|
|
192
200
|
|
|
193
|
-
|
|
201
|
+
The test suite has two parts:
|
|
194
202
|
|
|
195
|
-
-
|
|
196
|
-
-
|
|
197
|
-
- [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
|
|
198
|
-
- [Awesome JavaScript](https://github.com/sorrycc/awesome-javascript)
|
|
199
|
-
- [Awesome TypeScript](https://github.com/dzharii/awesome-typescript)
|
|
200
|
-
- [Bytes #429](https://bytes.dev/archives/429) — bytes.dev (2025-10-03)
|
|
201
|
-
- [Det. Eng. Weekly #124](https://www.detectionengineering.net/p/det-eng-weekly-124) — detectionengineering.net (2025-08-13)
|
|
202
|
-
- [The Overflow #319](https://stackoverflow.blog/2026/03/04/the-overflow-319/) — stackoverflow.blog (2026-03-04)
|
|
203
|
-
- [Hottest OSS tools Feb 2026](https://www.helpnetsecurity.com/2026/02/26/hottest-oss-tools-feb-2026/) — helpnetsecurity.com (2026-02-26)
|
|
204
|
-
- [See all mentions](https://pompelmi.app/featured-in/)
|
|
203
|
+
- **Unit tests** (`test/unit.test.js`) — run with Node's built-in test runner. Mock `cross-spawn` and platform dependencies; no ClamAV installation required.
|
|
204
|
+
- **Integration tests** (`test/scan.test.js`) — spawn real `clamscan` processes against EICAR test files. Skipped automatically if `clamscan` is not found in PATH.
|
|
205
205
|
|
|
206
|
-
|
|
206
|
+
## Contributing
|
|
207
207
|
|
|
208
|
-
|
|
208
|
+
1. Fork the repository at [https://github.com/pompelmi/pompelmi](https://github.com/pompelmi/pompelmi).
|
|
209
|
+
2. Create a feature branch: `git checkout -b feat/your-change`.
|
|
210
|
+
3. Make your changes and run `npm test` to verify.
|
|
211
|
+
4. Open a pull request against `main`.
|
|
209
212
|
|
|
210
|
-
|
|
211
|
-
- [Use cases](https://pompelmi.app/use-cases/)
|
|
212
|
-
- [Comparisons](https://pompelmi.app/comparisons/)
|
|
213
|
-
- [Tutorials](https://pompelmi.app/tutorials/)
|
|
214
|
-
- [Browser preview](https://pompelmi.app/#browser-preview)
|
|
215
|
-
- [Featured in](https://pompelmi.app/featured-in/)
|
|
216
|
-
- [Translations](https://pompelmi.app/translations/)
|
|
213
|
+
Please read [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md) before contributing.
|
|
217
214
|
|
|
218
|
-
##
|
|
215
|
+
## Security
|
|
219
216
|
|
|
220
|
-
|
|
217
|
+
To report a vulnerability, see [SECURITY.md](./SECURITY.md).
|
|
221
218
|
|
|
222
|
-
##
|
|
219
|
+
## License
|
|
223
220
|
|
|
224
|
-
|
|
225
|
-
- [Security](./SECURITY.md)
|
|
226
|
-
- [Roadmap](./ROADMAP.md)
|
|
227
|
-
- [GitHub Discussions](https://github.com/pompelmi/pompelmi/discussions)
|
|
228
|
-
- [License](./LICENSE)
|
|
221
|
+
[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
|
+
]);
|