pompelmi 0.33.0 → 0.34.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 (42) hide show
  1. package/README.md +351 -987
  2. package/dist/pompelmi.audit.cjs +130 -0
  3. package/dist/pompelmi.audit.cjs.map +1 -0
  4. package/dist/pompelmi.audit.esm.js +109 -0
  5. package/dist/pompelmi.audit.esm.js.map +1 -0
  6. package/dist/pompelmi.browser.cjs +1455 -0
  7. package/dist/pompelmi.browser.cjs.map +1 -0
  8. package/dist/pompelmi.browser.esm.js +1429 -0
  9. package/dist/pompelmi.browser.esm.js.map +1 -0
  10. package/dist/pompelmi.cjs +1333 -3044
  11. package/dist/pompelmi.cjs.map +1 -1
  12. package/dist/pompelmi.esm.js +1327 -3042
  13. package/dist/pompelmi.esm.js.map +1 -1
  14. package/dist/pompelmi.hooks.cjs +75 -0
  15. package/dist/pompelmi.hooks.cjs.map +1 -0
  16. package/dist/pompelmi.hooks.esm.js +72 -0
  17. package/dist/pompelmi.hooks.esm.js.map +1 -0
  18. package/dist/pompelmi.policy-packs.cjs +239 -0
  19. package/dist/pompelmi.policy-packs.cjs.map +1 -0
  20. package/dist/pompelmi.policy-packs.esm.js +231 -0
  21. package/dist/pompelmi.policy-packs.esm.js.map +1 -0
  22. package/dist/pompelmi.quarantine.cjs +315 -0
  23. package/dist/pompelmi.quarantine.cjs.map +1 -0
  24. package/dist/pompelmi.quarantine.esm.js +291 -0
  25. package/dist/pompelmi.quarantine.esm.js.map +1 -0
  26. package/dist/pompelmi.react.cjs +1486 -0
  27. package/dist/pompelmi.react.cjs.map +1 -0
  28. package/dist/pompelmi.react.esm.js +1459 -0
  29. package/dist/pompelmi.react.esm.js.map +1 -0
  30. package/dist/types/audit.d.ts +84 -0
  31. package/dist/types/browser-index.d.ts +28 -2
  32. package/dist/types/config.d.ts +3 -2
  33. package/dist/types/hooks.d.ts +89 -0
  34. package/dist/types/index.d.ts +17 -9
  35. package/dist/types/policy-packs.d.ts +98 -0
  36. package/dist/types/quarantine/index.d.ts +18 -0
  37. package/dist/types/quarantine/storage.d.ts +77 -0
  38. package/dist/types/quarantine/types.d.ts +78 -0
  39. package/dist/types/quarantine/workflow.d.ts +97 -0
  40. package/dist/types/react-index.d.ts +13 -0
  41. package/dist/types/types.d.ts +0 -1
  42. package/package.json +54 -3
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Pompelmi Quarantine Module
3
+ *
4
+ * Provides a first-class quarantine workflow for secure upload pipelines:
5
+ * scan → flag → quarantine → review → promote | delete
6
+ *
7
+ * Entry points:
8
+ * import { QuarantineManager, FilesystemQuarantineStorage } from 'pompelmi/quarantine';
9
+ *
10
+ * The storage layer is pluggable via the `QuarantineStorage` interface.
11
+ * `FilesystemQuarantineStorage` is the reference implementation for local/on-premise use.
12
+ *
13
+ * This module is Node.js-only (uses fs/crypto/path).
14
+ * It is NOT included in the 'pompelmi/browser' or 'pompelmi/react' bundles.
15
+ */
16
+ export { QuarantineManager, type QuarantineManagerOptions } from './workflow';
17
+ export { FilesystemQuarantineStorage, type QuarantineStorage, type FilesystemQuarantineStorageOptions, } from './storage';
18
+ export type { QuarantineEntry, QuarantinedFileInfo, QuarantineStatus, QuarantineDecision, QuarantineReview, QuarantineReport, QuarantineFilter, } from './types';
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Quarantine storage adapter interface and filesystem reference implementation.
3
+ *
4
+ * The `QuarantineStorage` interface decouples the quarantine workflow from any
5
+ * specific persistence layer. You can implement it for S3, GCS, a database,
6
+ * or any other backend.
7
+ *
8
+ * The built-in `FilesystemQuarantineStorage` stores files and metadata as JSON
9
+ * in a local directory — suitable for development, self-hosted, and on-premise
10
+ * deployments where data must not leave the machine.
11
+ *
12
+ * @module quarantine/storage
13
+ */
14
+ import type { QuarantineEntry, QuarantineFilter } from './types';
15
+ /**
16
+ * Storage adapter for the quarantine workflow.
17
+ * Implement this interface to support any backend (S3, GCS, DB, etc.).
18
+ */
19
+ export interface QuarantineStorage {
20
+ /**
21
+ * Persist the raw bytes of a quarantined file.
22
+ * Returns a `storageKey` that can later be used to retrieve or delete the bytes.
23
+ */
24
+ saveFile(id: string, bytes: Uint8Array): Promise<string>;
25
+ /**
26
+ * Retrieve the raw bytes of a quarantined file.
27
+ * Returns `null` if the file is not found.
28
+ */
29
+ getFile(storageKey: string): Promise<Uint8Array | null>;
30
+ /**
31
+ * Permanently remove the raw bytes of a quarantined file.
32
+ * No-op if already removed.
33
+ */
34
+ deleteFile(storageKey: string): Promise<void>;
35
+ /** Persist a quarantine entry (metadata + scan report). */
36
+ saveEntry(entry: QuarantineEntry): Promise<void>;
37
+ /** Load a quarantine entry by id. Returns `null` if not found. */
38
+ getEntry(id: string): Promise<QuarantineEntry | null>;
39
+ /** Update an existing quarantine entry (partial update). */
40
+ updateEntry(id: string, patch: Partial<QuarantineEntry>): Promise<QuarantineEntry>;
41
+ /** List quarantine entries matching the given filter. */
42
+ listEntries(filter?: QuarantineFilter): Promise<QuarantineEntry[]>;
43
+ /** Return the total count of quarantine entries matching the filter. */
44
+ countEntries(filter?: QuarantineFilter): Promise<number>;
45
+ }
46
+ export interface FilesystemQuarantineStorageOptions {
47
+ /**
48
+ * Root directory for quarantine storage.
49
+ * Two subdirectories are created: `files/` (raw bytes) and `meta/` (JSON).
50
+ */
51
+ dir: string;
52
+ /** Create the directory if it does not exist (default: true). */
53
+ createIfMissing?: boolean;
54
+ }
55
+ /**
56
+ * Reference implementation of `QuarantineStorage` backed by the local filesystem.
57
+ *
58
+ * File layout:
59
+ * <dir>/files/<storageKey> — raw file bytes
60
+ * <dir>/meta/<id>.json — QuarantineEntry JSON
61
+ *
62
+ * Suitable for single-process servers. For multi-process or distributed
63
+ * deployments, implement `QuarantineStorage` against a shared backend.
64
+ */
65
+ export declare class FilesystemQuarantineStorage implements QuarantineStorage {
66
+ private readonly filesDir;
67
+ private readonly metaDir;
68
+ constructor(options: FilesystemQuarantineStorageOptions);
69
+ saveFile(id: string, bytes: Uint8Array): Promise<string>;
70
+ getFile(storageKey: string): Promise<Uint8Array | null>;
71
+ deleteFile(storageKey: string): Promise<void>;
72
+ saveEntry(entry: QuarantineEntry): Promise<void>;
73
+ getEntry(id: string): Promise<QuarantineEntry | null>;
74
+ updateEntry(id: string, patch: Partial<QuarantineEntry>): Promise<QuarantineEntry>;
75
+ listEntries(filter?: QuarantineFilter): Promise<QuarantineEntry[]>;
76
+ countEntries(filter?: QuarantineFilter): Promise<number>;
77
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Quarantine system types for Pompelmi.
3
+ *
4
+ * A quarantine entry represents a file that was flagged during scanning and is
5
+ * held for manual review before being accepted or permanently removed.
6
+ *
7
+ * The lifecycle of a quarantined entry:
8
+ *
9
+ * scan → flagged → quarantined → reviewed → promoted | deleted
10
+ *
11
+ * @module quarantine/types
12
+ */
13
+ import type { ScanReport } from '../types';
14
+ /** The review status of a quarantined file. */
15
+ export type QuarantineStatus = 'pending' | 'reviewing' | 'promoted' | 'deleted';
16
+ /** Immutable metadata about the file at upload time. */
17
+ export interface QuarantinedFileInfo {
18
+ /** Original filename supplied by the uploader. */
19
+ originalName: string;
20
+ /** Detected MIME type (from magic bytes, not the Content-Type header). */
21
+ mimeType?: string;
22
+ /** File size in bytes. */
23
+ sizeBytes: number;
24
+ /** SHA-256 hex digest of the file content. */
25
+ sha256: string;
26
+ /** Uploader identity — opaque string (user id, session id, IP, etc.). */
27
+ uploadedBy?: string;
28
+ }
29
+ /** A quarantine entry created when a file is flagged. */
30
+ export interface QuarantineEntry {
31
+ /** Stable identifier for this quarantine entry (UUID). */
32
+ id: string;
33
+ /** Filename used to locate the quarantined bytes in storage. */
34
+ storageKey: string;
35
+ /** Metadata captured at upload time. */
36
+ file: QuarantinedFileInfo;
37
+ /** The scan report that triggered quarantine. */
38
+ scanReport: ScanReport;
39
+ /** ISO-8601 timestamp when the file was quarantined. */
40
+ quarantinedAt: string;
41
+ /** Current review status. */
42
+ status: QuarantineStatus;
43
+ /** ISO-8601 timestamp of the last status change. */
44
+ updatedAt: string;
45
+ /** Identity of the reviewer (operator id, etc.). Populated at review time. */
46
+ reviewedBy?: string;
47
+ /** Free-text review note from the operator. */
48
+ reviewNote?: string;
49
+ /** ISO-8601 timestamp when the final decision (promote/delete) was made. */
50
+ resolvedAt?: string;
51
+ /** Optional application-specific tags or labels. */
52
+ tags?: string[];
53
+ }
54
+ /** The outcome of a manual review. */
55
+ export type QuarantineDecision = 'promote' | 'delete';
56
+ /** Input required to resolve a quarantine entry. */
57
+ export interface QuarantineReview {
58
+ decision: QuarantineDecision;
59
+ reviewedBy?: string;
60
+ reviewNote?: string;
61
+ }
62
+ /** A structured JSON report of all quarantined entries (for audit/export). */
63
+ export interface QuarantineReport {
64
+ generatedAt: string;
65
+ totalEntries: number;
66
+ byStatus: Record<QuarantineStatus, number>;
67
+ entries: QuarantineEntry[];
68
+ }
69
+ /** Filter parameters for listing quarantine entries. */
70
+ export interface QuarantineFilter {
71
+ status?: QuarantineStatus | QuarantineStatus[];
72
+ /** Return only entries quarantined after this ISO-8601 timestamp. */
73
+ after?: string;
74
+ /** Return only entries quarantined before this ISO-8601 timestamp. */
75
+ before?: string;
76
+ /** Maximum number of results to return. */
77
+ limit?: number;
78
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Quarantine workflow — core API for the quarantine/review/resolve lifecycle.
3
+ *
4
+ * Usage (Node.js):
5
+ *
6
+ * ```ts
7
+ * import { scanBytes } from 'pompelmi';
8
+ * import { QuarantineManager, FilesystemQuarantineStorage } from 'pompelmi/quarantine';
9
+ *
10
+ * const quarantine = new QuarantineManager({
11
+ * storage: new FilesystemQuarantineStorage({ dir: './quarantine' }),
12
+ * });
13
+ *
14
+ * const report = await scanBytes(fileBytes, { ctx: { filename: file.name } });
15
+ *
16
+ * if (report.verdict !== 'clean') {
17
+ * const entry = await quarantine.quarantine(fileBytes, report, {
18
+ * originalName: file.name,
19
+ * sizeBytes: fileBytes.length,
20
+ * uploadedBy: req.user?.id,
21
+ * });
22
+ * console.log('Quarantined:', entry.id);
23
+ * }
24
+ * ```
25
+ *
26
+ * @module quarantine/workflow
27
+ */
28
+ import type { ScanReport } from '../types';
29
+ import type { QuarantineEntry, QuarantineFilter, QuarantineReport, QuarantineReview, QuarantinedFileInfo } from './types';
30
+ import type { QuarantineStorage } from './storage';
31
+ export interface QuarantineManagerOptions {
32
+ /** Storage adapter — use `FilesystemQuarantineStorage` for local deployments. */
33
+ storage: QuarantineStorage;
34
+ /**
35
+ * If true, files with a 'suspicious' verdict are also quarantined.
36
+ * Default: true.
37
+ */
38
+ quarantineSuspicious?: boolean;
39
+ /**
40
+ * If true, files with a 'malicious' verdict are also quarantined.
41
+ * Default: true.
42
+ */
43
+ quarantineMalicious?: boolean;
44
+ }
45
+ /**
46
+ * Manages the full lifecycle of quarantined files:
47
+ * scan → quarantine → review → promote | delete
48
+ */
49
+ export declare class QuarantineManager {
50
+ private readonly storage;
51
+ private readonly quarantineSuspicious;
52
+ private readonly quarantineMalicious;
53
+ constructor(options: QuarantineManagerOptions);
54
+ /**
55
+ * Determine whether a scan report should trigger quarantine per the
56
+ * configured policy.
57
+ */
58
+ shouldQuarantine(report: ScanReport): boolean;
59
+ /**
60
+ * Quarantine a file: save the bytes in storage, create the metadata entry,
61
+ * and return the entry.
62
+ *
63
+ * @param bytes Raw file bytes.
64
+ * @param report The scan report that triggered quarantine.
65
+ * @param fileInfo Partial metadata; `sha256` is derived from `bytes` if omitted.
66
+ */
67
+ quarantine(bytes: Uint8Array, report: ScanReport, fileInfo: Omit<QuarantinedFileInfo, 'sha256'> & {
68
+ sha256?: string;
69
+ }): Promise<QuarantineEntry>;
70
+ /**
71
+ * Mark an entry as being actively reviewed.
72
+ */
73
+ startReview(id: string, reviewedBy?: string): Promise<QuarantineEntry>;
74
+ /**
75
+ * Resolve a quarantine entry with a final decision.
76
+ *
77
+ * - `promote`: the file is cleared — bytes remain in storage for the caller
78
+ * to move to its final destination.
79
+ * - `delete`: the bytes are permanently removed from quarantine storage.
80
+ */
81
+ resolve(id: string, review: QuarantineReview): Promise<QuarantineEntry>;
82
+ /**
83
+ * Retrieve the raw bytes of a promoted file so the caller can move it to
84
+ * permanent storage. Returns `null` if the entry is not found or has been
85
+ * deleted.
86
+ */
87
+ getFile(id: string): Promise<Uint8Array | null>;
88
+ getEntry(id: string): Promise<QuarantineEntry | null>;
89
+ listEntries(filter?: QuarantineFilter): Promise<QuarantineEntry[]>;
90
+ listPending(): Promise<QuarantineEntry[]>;
91
+ countEntries(filter?: QuarantineFilter): Promise<number>;
92
+ /**
93
+ * Generate a structured JSON report of all quarantine entries matching the
94
+ * filter — suitable for audit logs and dashboards.
95
+ */
96
+ report(filter?: QuarantineFilter): Promise<QuarantineReport>;
97
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * src/react-index.ts — React entry point for Pompelmi.
3
+ *
4
+ * Re-exports the full browser-safe Pompelmi API plus the React hook.
5
+ * Import from 'pompelmi/react'.
6
+ *
7
+ * Peer dependency: react ^18 || ^19
8
+ *
9
+ * @example
10
+ * import { useFileScanner } from 'pompelmi/react';
11
+ */
12
+ export * from './browser-index';
13
+ export { useFileScanner } from './useFileScanner';
@@ -7,7 +7,6 @@ export interface YaraMatch {
7
7
  meta?: Record<string, unknown>;
8
8
  }
9
9
  export * from './types/decompilation';
10
- export * from './hipaa-compliance';
11
10
  export interface Match {
12
11
  rule: string;
13
12
  severity?: 'info' | 'low' | 'medium' | 'high' | 'critical' | 'suspicious' | 'malicious';
package/package.json CHANGED
@@ -1,13 +1,18 @@
1
1
  {
2
2
  "name": "pompelmi",
3
- "version": "0.33.0",
4
- "description": "Fast, private malware scanner for Node.js file uploads. TypeScript-first library with Express, Koa, Fastify, Next.js & Nuxt/Nitro adapters. Features deep ZIP inspection, YARA integration, ZIP bomb protection, and real-time threat detection. Zero cloud dependencies - scan files in-process before they hit disk. Perfect for GDPR/HIPAA compliance.",
3
+ "version": "0.34.0",
4
+ "description": "Fast, private malware scanner for Node.js file uploads. TypeScript-first library with Express, Koa, Fastify, Next.js & Nuxt/Nitro adapters. Features deep ZIP inspection, YARA integration, ZIP bomb protection, and real-time threat detection. Zero cloud dependencies - scan files in-process before they hit disk. Works well for privacy-sensitive and regulated environments.",
5
5
  "main": "./dist/pompelmi.cjs",
6
6
  "module": "./dist/pompelmi.esm.js",
7
7
  "type": "module",
8
8
  "browser": {
9
9
  "yara": false,
10
- "util": false
10
+ "util": false,
11
+ "crypto": false,
12
+ "os": false,
13
+ "path": false,
14
+ "unzipper": false,
15
+ "child_process": false
11
16
  },
12
17
  "repository": {
13
18
  "type": "git",
@@ -140,6 +145,10 @@
140
145
  "react": "^18.0.0 || ^19.0.0",
141
146
  "react-dom": "^18.0.0 || ^19.0.0"
142
147
  },
148
+ "peerDependenciesMeta": {
149
+ "react": { "optional": true },
150
+ "react-dom": { "optional": true }
151
+ },
143
152
  "optionalDependencies": {
144
153
  "@litko/yara-x": "^0.2.1"
145
154
  },
@@ -150,6 +159,48 @@
150
159
  "require": "./dist/pompelmi.cjs",
151
160
  "default": "./dist/pompelmi.esm.js"
152
161
  },
162
+ "./node": {
163
+ "types": "./dist/types/index.d.ts",
164
+ "import": "./dist/pompelmi.esm.js",
165
+ "require": "./dist/pompelmi.cjs",
166
+ "default": "./dist/pompelmi.esm.js"
167
+ },
168
+ "./browser": {
169
+ "types": "./dist/types/browser-index.d.ts",
170
+ "import": "./dist/pompelmi.browser.esm.js",
171
+ "require": "./dist/pompelmi.browser.cjs",
172
+ "default": "./dist/pompelmi.browser.esm.js"
173
+ },
174
+ "./react": {
175
+ "types": "./dist/types/react-index.d.ts",
176
+ "import": "./dist/pompelmi.react.esm.js",
177
+ "require": "./dist/pompelmi.react.cjs",
178
+ "default": "./dist/pompelmi.react.esm.js"
179
+ },
180
+ "./quarantine": {
181
+ "types": "./dist/types/quarantine/index.d.ts",
182
+ "import": "./dist/pompelmi.quarantine.esm.js",
183
+ "require": "./dist/pompelmi.quarantine.cjs",
184
+ "default": "./dist/pompelmi.quarantine.esm.js"
185
+ },
186
+ "./hooks": {
187
+ "types": "./dist/types/hooks.d.ts",
188
+ "import": "./dist/pompelmi.hooks.esm.js",
189
+ "require": "./dist/pompelmi.hooks.cjs",
190
+ "default": "./dist/pompelmi.hooks.esm.js"
191
+ },
192
+ "./audit": {
193
+ "types": "./dist/types/audit.d.ts",
194
+ "import": "./dist/pompelmi.audit.esm.js",
195
+ "require": "./dist/pompelmi.audit.cjs",
196
+ "default": "./dist/pompelmi.audit.esm.js"
197
+ },
198
+ "./policy-packs": {
199
+ "types": "./dist/types/policy-packs.d.ts",
200
+ "import": "./dist/pompelmi.policy-packs.esm.js",
201
+ "require": "./dist/pompelmi.policy-packs.cjs",
202
+ "default": "./dist/pompelmi.policy-packs.esm.js"
203
+ },
153
204
  "./package.json": "./package.json"
154
205
  },
155
206
  "files": [