pompelmi 0.34.10 → 0.35.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/README.md +26 -15
- package/dist/pompelmi.audit.cjs +13 -15
- package/dist/pompelmi.audit.cjs.map +1 -1
- package/dist/pompelmi.audit.esm.js +13 -15
- package/dist/pompelmi.audit.esm.js.map +1 -1
- package/dist/pompelmi.browser.cjs +585 -534
- package/dist/pompelmi.browser.cjs.map +1 -1
- package/dist/pompelmi.browser.esm.js +585 -534
- package/dist/pompelmi.browser.esm.js.map +1 -1
- package/dist/pompelmi.cjs +2066 -2016
- package/dist/pompelmi.cjs.map +1 -1
- package/dist/pompelmi.esm.js +2066 -2016
- package/dist/pompelmi.esm.js.map +1 -1
- package/dist/pompelmi.hooks.cjs +2 -2
- package/dist/pompelmi.hooks.cjs.map +1 -1
- package/dist/pompelmi.hooks.esm.js +2 -2
- package/dist/pompelmi.hooks.esm.js.map +1 -1
- package/dist/pompelmi.policy-packs.cjs +74 -73
- package/dist/pompelmi.policy-packs.cjs.map +1 -1
- package/dist/pompelmi.policy-packs.esm.js +74 -73
- package/dist/pompelmi.policy-packs.esm.js.map +1 -1
- package/dist/pompelmi.quarantine.cjs +135 -133
- package/dist/pompelmi.quarantine.cjs.map +1 -1
- package/dist/pompelmi.quarantine.esm.js +135 -133
- package/dist/pompelmi.quarantine.esm.js.map +1 -1
- package/dist/pompelmi.react.cjs +585 -534
- package/dist/pompelmi.react.cjs.map +1 -1
- package/dist/pompelmi.react.esm.js +585 -534
- package/dist/pompelmi.react.esm.js.map +1 -1
- package/dist/types/audit.d.ts +12 -12
- package/dist/types/browser-index.d.ts +12 -12
- package/dist/types/config.d.ts +4 -4
- package/dist/types/engines/dynamic-taint.d.ts +1 -1
- package/dist/types/engines/hybrid-orchestrator.d.ts +1 -1
- package/dist/types/engines/hybrid-taint-integration.d.ts +6 -6
- package/dist/types/engines/taint-policies.d.ts +4 -4
- package/dist/types/hipaa-compliance.d.ts +2 -2
- package/dist/types/hooks.d.ts +2 -2
- package/dist/types/index.d.ts +20 -20
- package/dist/types/node/scanDir.d.ts +5 -5
- package/dist/types/policy-packs.d.ts +2 -2
- package/dist/types/presets.d.ts +3 -3
- package/dist/types/quarantine/index.d.ts +3 -3
- package/dist/types/quarantine/storage.d.ts +1 -1
- package/dist/types/quarantine/types.d.ts +3 -3
- package/dist/types/quarantine/workflow.d.ts +4 -4
- package/dist/types/react-index.d.ts +2 -2
- package/dist/types/risk.d.ts +1 -1
- package/dist/types/scan/remote.d.ts +2 -2
- package/dist/types/scan.d.ts +5 -5
- package/dist/types/scanners/common-heuristics.d.ts +1 -1
- package/dist/types/scanners/zip-bomb-guard.d.ts +1 -1
- package/dist/types/src/audit.d.ts +84 -0
- package/dist/types/src/browser-index.d.ts +29 -0
- package/dist/types/src/config.d.ts +143 -0
- package/dist/types/src/engines/dynamic-taint.d.ts +102 -0
- package/dist/types/src/engines/hybrid-orchestrator.d.ts +65 -0
- package/dist/types/src/engines/hybrid-taint-integration.d.ts +129 -0
- package/dist/types/src/engines/taint-policies.d.ts +84 -0
- package/dist/types/src/hipaa-compliance.d.ts +110 -0
- package/dist/types/src/hooks.d.ts +89 -0
- package/dist/types/src/index.d.ts +29 -0
- package/dist/types/src/magic.d.ts +7 -0
- package/dist/types/src/node/scanDir.d.ts +30 -0
- package/dist/types/src/policy-packs.d.ts +98 -0
- package/dist/types/src/policy.d.ts +12 -0
- package/dist/types/src/presets.d.ts +72 -0
- package/dist/types/src/quarantine/index.d.ts +18 -0
- package/dist/types/src/quarantine/storage.d.ts +77 -0
- package/dist/types/src/quarantine/types.d.ts +78 -0
- package/dist/types/src/quarantine/workflow.d.ts +97 -0
- package/dist/types/src/react-index.d.ts +13 -0
- package/dist/types/src/risk.d.ts +18 -0
- package/dist/types/src/scan/remote.d.ts +12 -0
- package/dist/types/src/scan.d.ts +17 -0
- package/dist/types/src/scanners/common-heuristics.d.ts +14 -0
- package/dist/types/src/scanners/zip-bomb-guard.d.ts +9 -0
- package/dist/types/src/scanners/zipTraversalGuard.d.ts +19 -0
- package/dist/types/src/stream.d.ts +10 -0
- package/dist/types/src/types/decompilation.d.ts +96 -0
- package/dist/types/src/types/taint-tracking.d.ts +495 -0
- package/dist/types/src/types.d.ts +48 -0
- package/dist/types/src/useFileScanner.d.ts +15 -0
- package/dist/types/src/utils/advanced-detection.d.ts +21 -0
- package/dist/types/src/utils/batch-scanner.d.ts +62 -0
- package/dist/types/src/utils/cache-manager.d.ts +95 -0
- package/dist/types/src/utils/export.d.ts +51 -0
- package/dist/types/src/utils/performance-metrics.d.ts +68 -0
- package/dist/types/src/utils/threat-intelligence.d.ts +96 -0
- package/dist/types/src/validate.d.ts +7 -0
- package/dist/types/src/verdict.d.ts +2 -0
- package/dist/types/src/yara/browser.d.ts +7 -0
- package/dist/types/src/yara/index.d.ts +17 -0
- package/dist/types/src/yara/node.d.ts +2 -0
- package/dist/types/src/yara/remote.d.ts +10 -0
- package/dist/types/src/yara-bridge.d.ts +3 -0
- package/dist/types/src/zip.d.ts +13 -0
- package/dist/types/types/decompilation.d.ts +4 -4
- package/dist/types/types/taint-tracking.d.ts +19 -19
- package/dist/types/types.d.ts +3 -3
- package/dist/types/useFileScanner.d.ts +1 -1
- package/dist/types/utils/advanced-detection.d.ts +1 -1
- package/dist/types/utils/batch-scanner.d.ts +3 -3
- package/dist/types/utils/cache-manager.d.ts +1 -1
- package/dist/types/utils/export.d.ts +2 -2
- package/dist/types/utils/threat-intelligence.d.ts +4 -4
- package/dist/types/verdict.d.ts +1 -1
- package/dist/types/yara/browser.d.ts +1 -1
- package/dist/types/yara/index.d.ts +1 -1
- package/dist/types/yara/node.d.ts +1 -1
- package/dist/types/yara/remote.d.ts +2 -2
- package/package.json +6 -6
package/dist/pompelmi.hooks.cjs
CHANGED
|
@@ -46,7 +46,7 @@ function createScanHooks(hooks) {
|
|
|
46
46
|
*/
|
|
47
47
|
function withHooks(scanFn, hooks) {
|
|
48
48
|
return async (bytes, opts = {}) => {
|
|
49
|
-
const scanId = typeof crypto !==
|
|
49
|
+
const scanId = typeof crypto !== "undefined" && "randomUUID" in crypto
|
|
50
50
|
? crypto.randomUUID()
|
|
51
51
|
: undefined;
|
|
52
52
|
const startedAt = Date.now();
|
|
@@ -63,7 +63,7 @@ function withHooks(scanFn, hooks) {
|
|
|
63
63
|
const durationMs = Date.now() - startedAt;
|
|
64
64
|
const completeCtx = { ...ctx, durationMs };
|
|
65
65
|
void hooks.onScanComplete?.(completeCtx, report);
|
|
66
|
-
if (report.verdict ===
|
|
66
|
+
if (report.verdict === "suspicious" || report.verdict === "malicious") {
|
|
67
67
|
void hooks.onThreatDetected?.(completeCtx, report);
|
|
68
68
|
}
|
|
69
69
|
return report;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pompelmi.hooks.cjs","sources":["
|
|
1
|
+
{"version":3,"file":"pompelmi.hooks.cjs","sources":["../../src/hooks.ts"],"sourcesContent":["/**\n * Scan lifecycle hooks for Pompelmi.\n *\n * Hooks let you observe and react to scan events without modifying the scan\n * pipeline itself. They are the recommended integration point for:\n * - logging / metrics collection\n * - alerting on threats\n * - triggering quarantine automatically\n * - OpenTelemetry span creation\n *\n * Usage:\n * ```ts\n * import { scanBytes } from 'pompelmi';\n * import { createScanHooks, withHooks } from 'pompelmi/hooks';\n *\n * const hooks = createScanHooks({\n * onScanComplete(ctx, report) {\n * console.log(ctx.filename, report.verdict, report.durationMs + 'ms');\n * },\n * onThreatDetected(ctx, report) {\n * alertTeam({ file: ctx.filename, verdict: report.verdict });\n * },\n * });\n *\n * const scan = withHooks(scanBytes, hooks);\n * const report = await scan(bytes, { ctx: { filename: 'upload.zip' } });\n * ```\n *\n * @module hooks\n */\n\nimport type { QuarantineEntry } from \"./quarantine/types\";\nimport type { ScanContext, ScanReport } from \"./types\";\n\n// ── Event payloads ────────────────────────────────────────────────────────────\n\nexport interface ScanStartContext extends ScanContext {\n /** Unique identifier for this scan invocation (useful for correlating logs). */\n scanId?: string;\n /** Timestamp when the scan started (ms since epoch). */\n startedAt: number;\n}\n\nexport interface ScanCompleteContext extends ScanStartContext {\n /** Duration of the scan in milliseconds. */\n durationMs: number;\n}\n\n// ── Hook interface ────────────────────────────────────────────────────────────\n\n/**\n * Callbacks for the scan lifecycle. All hooks are optional.\n *\n * Hooks MUST NOT throw — wrap logic in try/catch if it can fail.\n * Async hooks are fire-and-forget; they do not block the scan result.\n */\nexport interface ScanHooks {\n /**\n * Called immediately before a scan begins.\n */\n onScanStart?: (ctx: ScanStartContext) => void | Promise<void>;\n\n /**\n * Called when a scan completes successfully (any verdict, including clean).\n */\n onScanComplete?: (ctx: ScanCompleteContext, report: ScanReport) => void | Promise<void>;\n\n /**\n * Called when the scan verdict is 'suspicious' or 'malicious'.\n * Fired in addition to `onScanComplete`.\n */\n onThreatDetected?: (ctx: ScanCompleteContext, report: ScanReport) => void | Promise<void>;\n\n /**\n * Called when a file has been quarantined.\n * Requires wiring with a `QuarantineManager`; not fired automatically by `scanBytes`.\n */\n onQuarantine?: (entry: QuarantineEntry) => void | Promise<void>;\n\n /**\n * Called when a scan throws an unexpected error.\n */\n onScanError?: (ctx: ScanStartContext, error: unknown) => void | Promise<void>;\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * Create a `ScanHooks` object with optional defaults.\n * This is a thin factory — the value of using it is the inline TS types.\n */\nexport function createScanHooks(hooks: ScanHooks): ScanHooks {\n return hooks;\n}\n\n// ── withHooks wrapper ─────────────────────────────────────────────────────────\n\ntype ScanFn = (\n bytes: Uint8Array,\n opts?: { ctx?: ScanContext; [k: string]: unknown },\n) => Promise<ScanReport>;\n\n/**\n * Wrap a scan function with lifecycle hooks.\n *\n * Returns a new function with the same signature that fires the hooks\n * around each scan call.\n */\nexport function withHooks(scanFn: ScanFn, hooks: ScanHooks): ScanFn {\n return async (bytes, opts = {}) => {\n const scanId =\n typeof crypto !== \"undefined\" && \"randomUUID\" in crypto\n ? (crypto as { randomUUID(): string }).randomUUID()\n : undefined;\n\n const startedAt = Date.now();\n const ctx: ScanStartContext = { ...opts.ctx, scanId, startedAt };\n\n void hooks.onScanStart?.(ctx);\n\n let report: ScanReport;\n try {\n report = await scanFn(bytes, opts);\n } catch (err) {\n void hooks.onScanError?.({ ...ctx }, err);\n throw err;\n }\n\n const durationMs = Date.now() - startedAt;\n const completeCtx: ScanCompleteContext = { ...ctx, durationMs };\n\n void hooks.onScanComplete?.(completeCtx, report);\n\n if (report.verdict === \"suspicious\" || report.verdict === \"malicious\") {\n void hooks.onThreatDetected?.(completeCtx, report);\n }\n\n return report;\n };\n}\n"],"names":[],"mappings":";;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BG;AAwDH;AAEA;;;AAGG;AACG,SAAU,eAAe,CAAC,KAAgB,EAAA;AAC9C,IAAA,OAAO,KAAK;AACd;AASA;;;;;AAKG;AACG,SAAU,SAAS,CAAC,MAAc,EAAE,KAAgB,EAAA;IACxD,OAAO,OAAO,KAAK,EAAE,IAAI,GAAG,EAAE,KAAI;QAChC,MAAM,MAAM,GACV,OAAO,MAAM,KAAK,WAAW,IAAI,YAAY,IAAI;AAC/C,cAAG,MAAmC,CAAC,UAAU;cAC/C,SAAS;AAEf,QAAA,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE;AAC5B,QAAA,MAAM,GAAG,GAAqB,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE;AAEhE,QAAA,KAAK,KAAK,CAAC,WAAW,GAAG,GAAG,CAAC;AAE7B,QAAA,IAAI,MAAkB;AACtB,QAAA,IAAI;YACF,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC;QACpC;QAAE,OAAO,GAAG,EAAE;AACZ,YAAA,KAAK,KAAK,CAAC,WAAW,GAAG,EAAE,GAAG,GAAG,EAAE,EAAE,GAAG,CAAC;AACzC,YAAA,MAAM,GAAG;QACX;QAEA,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;QACzC,MAAM,WAAW,GAAwB,EAAE,GAAG,GAAG,EAAE,UAAU,EAAE;QAE/D,KAAK,KAAK,CAAC,cAAc,GAAG,WAAW,EAAE,MAAM,CAAC;AAEhD,QAAA,IAAI,MAAM,CAAC,OAAO,KAAK,YAAY,IAAI,MAAM,CAAC,OAAO,KAAK,WAAW,EAAE;YACrE,KAAK,KAAK,CAAC,gBAAgB,GAAG,WAAW,EAAE,MAAM,CAAC;QACpD;AAEA,QAAA,OAAO,MAAM;AACf,IAAA,CAAC;AACH;;;;;"}
|
|
@@ -44,7 +44,7 @@ function createScanHooks(hooks) {
|
|
|
44
44
|
*/
|
|
45
45
|
function withHooks(scanFn, hooks) {
|
|
46
46
|
return async (bytes, opts = {}) => {
|
|
47
|
-
const scanId = typeof crypto !==
|
|
47
|
+
const scanId = typeof crypto !== "undefined" && "randomUUID" in crypto
|
|
48
48
|
? crypto.randomUUID()
|
|
49
49
|
: undefined;
|
|
50
50
|
const startedAt = Date.now();
|
|
@@ -61,7 +61,7 @@ function withHooks(scanFn, hooks) {
|
|
|
61
61
|
const durationMs = Date.now() - startedAt;
|
|
62
62
|
const completeCtx = { ...ctx, durationMs };
|
|
63
63
|
void hooks.onScanComplete?.(completeCtx, report);
|
|
64
|
-
if (report.verdict ===
|
|
64
|
+
if (report.verdict === "suspicious" || report.verdict === "malicious") {
|
|
65
65
|
void hooks.onThreatDetected?.(completeCtx, report);
|
|
66
66
|
}
|
|
67
67
|
return report;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pompelmi.hooks.esm.js","sources":["
|
|
1
|
+
{"version":3,"file":"pompelmi.hooks.esm.js","sources":["../../src/hooks.ts"],"sourcesContent":["/**\n * Scan lifecycle hooks for Pompelmi.\n *\n * Hooks let you observe and react to scan events without modifying the scan\n * pipeline itself. They are the recommended integration point for:\n * - logging / metrics collection\n * - alerting on threats\n * - triggering quarantine automatically\n * - OpenTelemetry span creation\n *\n * Usage:\n * ```ts\n * import { scanBytes } from 'pompelmi';\n * import { createScanHooks, withHooks } from 'pompelmi/hooks';\n *\n * const hooks = createScanHooks({\n * onScanComplete(ctx, report) {\n * console.log(ctx.filename, report.verdict, report.durationMs + 'ms');\n * },\n * onThreatDetected(ctx, report) {\n * alertTeam({ file: ctx.filename, verdict: report.verdict });\n * },\n * });\n *\n * const scan = withHooks(scanBytes, hooks);\n * const report = await scan(bytes, { ctx: { filename: 'upload.zip' } });\n * ```\n *\n * @module hooks\n */\n\nimport type { QuarantineEntry } from \"./quarantine/types\";\nimport type { ScanContext, ScanReport } from \"./types\";\n\n// ── Event payloads ────────────────────────────────────────────────────────────\n\nexport interface ScanStartContext extends ScanContext {\n /** Unique identifier for this scan invocation (useful for correlating logs). */\n scanId?: string;\n /** Timestamp when the scan started (ms since epoch). */\n startedAt: number;\n}\n\nexport interface ScanCompleteContext extends ScanStartContext {\n /** Duration of the scan in milliseconds. */\n durationMs: number;\n}\n\n// ── Hook interface ────────────────────────────────────────────────────────────\n\n/**\n * Callbacks for the scan lifecycle. All hooks are optional.\n *\n * Hooks MUST NOT throw — wrap logic in try/catch if it can fail.\n * Async hooks are fire-and-forget; they do not block the scan result.\n */\nexport interface ScanHooks {\n /**\n * Called immediately before a scan begins.\n */\n onScanStart?: (ctx: ScanStartContext) => void | Promise<void>;\n\n /**\n * Called when a scan completes successfully (any verdict, including clean).\n */\n onScanComplete?: (ctx: ScanCompleteContext, report: ScanReport) => void | Promise<void>;\n\n /**\n * Called when the scan verdict is 'suspicious' or 'malicious'.\n * Fired in addition to `onScanComplete`.\n */\n onThreatDetected?: (ctx: ScanCompleteContext, report: ScanReport) => void | Promise<void>;\n\n /**\n * Called when a file has been quarantined.\n * Requires wiring with a `QuarantineManager`; not fired automatically by `scanBytes`.\n */\n onQuarantine?: (entry: QuarantineEntry) => void | Promise<void>;\n\n /**\n * Called when a scan throws an unexpected error.\n */\n onScanError?: (ctx: ScanStartContext, error: unknown) => void | Promise<void>;\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * Create a `ScanHooks` object with optional defaults.\n * This is a thin factory — the value of using it is the inline TS types.\n */\nexport function createScanHooks(hooks: ScanHooks): ScanHooks {\n return hooks;\n}\n\n// ── withHooks wrapper ─────────────────────────────────────────────────────────\n\ntype ScanFn = (\n bytes: Uint8Array,\n opts?: { ctx?: ScanContext; [k: string]: unknown },\n) => Promise<ScanReport>;\n\n/**\n * Wrap a scan function with lifecycle hooks.\n *\n * Returns a new function with the same signature that fires the hooks\n * around each scan call.\n */\nexport function withHooks(scanFn: ScanFn, hooks: ScanHooks): ScanFn {\n return async (bytes, opts = {}) => {\n const scanId =\n typeof crypto !== \"undefined\" && \"randomUUID\" in crypto\n ? (crypto as { randomUUID(): string }).randomUUID()\n : undefined;\n\n const startedAt = Date.now();\n const ctx: ScanStartContext = { ...opts.ctx, scanId, startedAt };\n\n void hooks.onScanStart?.(ctx);\n\n let report: ScanReport;\n try {\n report = await scanFn(bytes, opts);\n } catch (err) {\n void hooks.onScanError?.({ ...ctx }, err);\n throw err;\n }\n\n const durationMs = Date.now() - startedAt;\n const completeCtx: ScanCompleteContext = { ...ctx, durationMs };\n\n void hooks.onScanComplete?.(completeCtx, report);\n\n if (report.verdict === \"suspicious\" || report.verdict === \"malicious\") {\n void hooks.onThreatDetected?.(completeCtx, report);\n }\n\n return report;\n };\n}\n"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BG;AAwDH;AAEA;;;AAGG;AACG,SAAU,eAAe,CAAC,KAAgB,EAAA;AAC9C,IAAA,OAAO,KAAK;AACd;AASA;;;;;AAKG;AACG,SAAU,SAAS,CAAC,MAAc,EAAE,KAAgB,EAAA;IACxD,OAAO,OAAO,KAAK,EAAE,IAAI,GAAG,EAAE,KAAI;QAChC,MAAM,MAAM,GACV,OAAO,MAAM,KAAK,WAAW,IAAI,YAAY,IAAI;AAC/C,cAAG,MAAmC,CAAC,UAAU;cAC/C,SAAS;AAEf,QAAA,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE;AAC5B,QAAA,MAAM,GAAG,GAAqB,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE;AAEhE,QAAA,KAAK,KAAK,CAAC,WAAW,GAAG,GAAG,CAAC;AAE7B,QAAA,IAAI,MAAkB;AACtB,QAAA,IAAI;YACF,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC;QACpC;QAAE,OAAO,GAAG,EAAE;AACZ,YAAA,KAAK,KAAK,CAAC,WAAW,GAAG,EAAE,GAAG,GAAG,EAAE,EAAE,GAAG,CAAC;AACzC,YAAA,MAAM,GAAG;QACX;QAEA,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;QACzC,MAAM,WAAW,GAAwB,EAAE,GAAG,GAAG,EAAE,UAAU,EAAE;QAE/D,KAAK,KAAK,CAAC,cAAc,GAAG,WAAW,EAAE,MAAM,CAAC;AAEhD,QAAA,IAAI,MAAM,CAAC,OAAO,KAAK,YAAY,IAAI,MAAM,CAAC,OAAO,KAAK,WAAW,EAAE;YACrE,KAAK,KAAK,CAAC,gBAAgB,GAAG,WAAW,EAAE,MAAM,CAAC;QACpD;AAEA,QAAA,OAAO,MAAM;AACf,IAAA,CAAC;AACH;;;;"}
|
|
@@ -2,25 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
const MB$1 = 1024 * 1024;
|
|
4
4
|
const DEFAULT_POLICY = {
|
|
5
|
-
includeExtensions: [
|
|
6
|
-
allowedMimeTypes: [
|
|
5
|
+
includeExtensions: ["zip", "png", "jpg", "jpeg", "pdf"],
|
|
6
|
+
allowedMimeTypes: ["application/zip", "image/png", "image/jpeg", "application/pdf", "text/plain"],
|
|
7
7
|
maxFileSizeBytes: 20 * MB$1,
|
|
8
8
|
timeoutMs: 5000,
|
|
9
9
|
concurrency: 4,
|
|
10
|
-
failClosed: true
|
|
10
|
+
failClosed: true,
|
|
11
11
|
};
|
|
12
12
|
function definePolicy(input = {}) {
|
|
13
13
|
const p = { ...DEFAULT_POLICY, ...input };
|
|
14
14
|
if (!Array.isArray(p.includeExtensions))
|
|
15
|
-
throw new TypeError(
|
|
15
|
+
throw new TypeError("includeExtensions must be string[]");
|
|
16
16
|
if (!Array.isArray(p.allowedMimeTypes))
|
|
17
|
-
throw new TypeError(
|
|
17
|
+
throw new TypeError("allowedMimeTypes must be string[]");
|
|
18
18
|
if (!(Number.isFinite(p.maxFileSizeBytes) && p.maxFileSizeBytes > 0))
|
|
19
|
-
throw new TypeError(
|
|
19
|
+
throw new TypeError("maxFileSizeBytes must be > 0");
|
|
20
20
|
if (!(Number.isFinite(p.timeoutMs) && p.timeoutMs > 0))
|
|
21
|
-
throw new TypeError(
|
|
21
|
+
throw new TypeError("timeoutMs must be > 0");
|
|
22
22
|
if (!(Number.isInteger(p.concurrency) && p.concurrency > 0))
|
|
23
|
-
throw new TypeError(
|
|
23
|
+
throw new TypeError("concurrency must be > 0");
|
|
24
24
|
return p;
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -64,33 +64,39 @@ const MB = 1024 * KB;
|
|
|
64
64
|
*/
|
|
65
65
|
const DOCUMENTS_ONLY = definePolicy({
|
|
66
66
|
includeExtensions: [
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
67
|
+
"pdf",
|
|
68
|
+
"doc",
|
|
69
|
+
"docx",
|
|
70
|
+
"xls",
|
|
71
|
+
"xlsx",
|
|
72
|
+
"ppt",
|
|
73
|
+
"pptx",
|
|
74
|
+
"odt",
|
|
75
|
+
"ods",
|
|
76
|
+
"odp",
|
|
77
|
+
"csv",
|
|
78
|
+
"txt",
|
|
79
|
+
"json",
|
|
80
|
+
"yaml",
|
|
81
|
+
"yml",
|
|
82
|
+
"md",
|
|
77
83
|
],
|
|
78
84
|
allowedMimeTypes: [
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
85
|
+
"application/pdf",
|
|
86
|
+
"application/msword",
|
|
87
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
88
|
+
"application/vnd.ms-excel",
|
|
89
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
90
|
+
"application/vnd.ms-powerpoint",
|
|
91
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
92
|
+
"application/vnd.oasis.opendocument.text",
|
|
93
|
+
"application/vnd.oasis.opendocument.spreadsheet",
|
|
94
|
+
"application/vnd.oasis.opendocument.presentation",
|
|
95
|
+
"text/csv",
|
|
96
|
+
"text/plain",
|
|
97
|
+
"application/json",
|
|
98
|
+
"text/yaml",
|
|
99
|
+
"text/markdown",
|
|
94
100
|
],
|
|
95
101
|
maxFileSizeBytes: 25 * MB,
|
|
96
102
|
timeoutMs: 10000,
|
|
@@ -108,17 +114,17 @@ const DOCUMENTS_ONLY = definePolicy({
|
|
|
108
114
|
* Note: SVG is intentionally excluded — inline SVGs can contain scripts.
|
|
109
115
|
*/
|
|
110
116
|
const IMAGES_ONLY = definePolicy({
|
|
111
|
-
includeExtensions: [
|
|
117
|
+
includeExtensions: ["jpg", "jpeg", "png", "gif", "webp", "avif", "tiff", "tif", "bmp", "ico"],
|
|
112
118
|
allowedMimeTypes: [
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
"image/jpeg",
|
|
120
|
+
"image/png",
|
|
121
|
+
"image/gif",
|
|
122
|
+
"image/webp",
|
|
123
|
+
"image/avif",
|
|
124
|
+
"image/tiff",
|
|
125
|
+
"image/bmp",
|
|
126
|
+
"image/x-icon",
|
|
127
|
+
"image/vnd.microsoft.icon",
|
|
122
128
|
],
|
|
123
129
|
maxFileSizeBytes: 10 * MB,
|
|
124
130
|
timeoutMs: 5000,
|
|
@@ -135,13 +141,8 @@ const IMAGES_ONLY = definePolicy({
|
|
|
135
141
|
* allowlist. Only allows plain images and PDF.
|
|
136
142
|
*/
|
|
137
143
|
const STRICT_PUBLIC_UPLOAD = definePolicy({
|
|
138
|
-
includeExtensions: [
|
|
139
|
-
allowedMimeTypes: [
|
|
140
|
-
'image/jpeg',
|
|
141
|
-
'image/png',
|
|
142
|
-
'image/webp',
|
|
143
|
-
'application/pdf',
|
|
144
|
-
],
|
|
144
|
+
includeExtensions: ["jpg", "jpeg", "png", "webp", "pdf"],
|
|
145
|
+
allowedMimeTypes: ["image/jpeg", "image/png", "image/webp", "application/pdf"],
|
|
145
146
|
maxFileSizeBytes: 5 * MB,
|
|
146
147
|
timeoutMs: 4000,
|
|
147
148
|
concurrency: 2,
|
|
@@ -155,16 +156,16 @@ const STRICT_PUBLIC_UPLOAD = definePolicy({
|
|
|
155
156
|
* shorter timeout than the permissive default.
|
|
156
157
|
*/
|
|
157
158
|
const CONSERVATIVE_DEFAULT = definePolicy({
|
|
158
|
-
includeExtensions: [
|
|
159
|
+
includeExtensions: ["zip", "png", "jpg", "jpeg", "pdf", "txt", "csv", "docx", "xlsx"],
|
|
159
160
|
allowedMimeTypes: [
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
161
|
+
"application/zip",
|
|
162
|
+
"image/png",
|
|
163
|
+
"image/jpeg",
|
|
164
|
+
"application/pdf",
|
|
165
|
+
"text/plain",
|
|
166
|
+
"text/csv",
|
|
167
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
168
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
168
169
|
],
|
|
169
170
|
maxFileSizeBytes: 10 * MB,
|
|
170
171
|
timeoutMs: 8000,
|
|
@@ -188,15 +189,15 @@ const CONSERVATIVE_DEFAULT = definePolicy({
|
|
|
188
189
|
* ```
|
|
189
190
|
*/
|
|
190
191
|
const ARCHIVES = definePolicy({
|
|
191
|
-
includeExtensions: [
|
|
192
|
+
includeExtensions: ["zip", "tar", "gz", "tgz", "bz2", "xz", "7z", "rar"],
|
|
192
193
|
allowedMimeTypes: [
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
194
|
+
"application/zip",
|
|
195
|
+
"application/x-tar",
|
|
196
|
+
"application/gzip",
|
|
197
|
+
"application/x-bzip2",
|
|
198
|
+
"application/x-xz",
|
|
199
|
+
"application/x-7z-compressed",
|
|
200
|
+
"application/x-rar-compressed",
|
|
200
201
|
],
|
|
201
202
|
maxFileSizeBytes: 100 * MB,
|
|
202
203
|
timeoutMs: 30000,
|
|
@@ -212,11 +213,11 @@ const ARCHIVES = definePolicy({
|
|
|
212
213
|
* ```
|
|
213
214
|
*/
|
|
214
215
|
const POLICY_PACKS = {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
216
|
+
"documents-only": DOCUMENTS_ONLY,
|
|
217
|
+
"images-only": IMAGES_ONLY,
|
|
218
|
+
"strict-public-upload": STRICT_PUBLIC_UPLOAD,
|
|
219
|
+
"conservative-default": CONSERVATIVE_DEFAULT,
|
|
220
|
+
archives: ARCHIVES,
|
|
220
221
|
};
|
|
221
222
|
/**
|
|
222
223
|
* Look up a policy pack by name.
|
|
@@ -225,7 +226,7 @@ const POLICY_PACKS = {
|
|
|
225
226
|
function getPolicyPack(name) {
|
|
226
227
|
const policy = POLICY_PACKS[name];
|
|
227
228
|
if (!policy)
|
|
228
|
-
throw new Error(`Unknown policy pack: '${name}'. Valid names: ${Object.keys(POLICY_PACKS).join(
|
|
229
|
+
throw new Error(`Unknown policy pack: '${name}'. Valid names: ${Object.keys(POLICY_PACKS).join(", ")}`);
|
|
229
230
|
return policy;
|
|
230
231
|
}
|
|
231
232
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pompelmi.policy-packs.cjs","sources":["../src/policy.ts","../src/policy-packs.ts"],"sourcesContent":["export interface Policy {\n includeExtensions: string[];\n allowedMimeTypes: string[];\n maxFileSizeBytes: number;\n timeoutMs: number;\n concurrency: number;\n failClosed: boolean;\n onScanEvent?: (ev: unknown) => void;\n}\nexport type PolicyInput = Partial<Policy>;\n\nconst MB = 1024 * 1024;\n\nexport const DEFAULT_POLICY: Policy = {\n includeExtensions: ['zip','png','jpg','jpeg','pdf'],\n allowedMimeTypes: ['application/zip','image/png','image/jpeg','application/pdf','text/plain'],\n maxFileSizeBytes: 20 * MB,\n timeoutMs: 5000,\n concurrency: 4,\n failClosed: true\n};\n\nexport function definePolicy(input: PolicyInput = {}): Policy {\n const p: Policy = { ...DEFAULT_POLICY, ...input };\n if (!Array.isArray(p.includeExtensions)) throw new TypeError('includeExtensions must be string[]');\n if (!Array.isArray(p.allowedMimeTypes)) throw new TypeError('allowedMimeTypes must be string[]');\n if (!(Number.isFinite(p.maxFileSizeBytes) && p.maxFileSizeBytes > 0)) throw new TypeError('maxFileSizeBytes must be > 0');\n if (!(Number.isFinite(p.timeoutMs) && p.timeoutMs > 0)) throw new TypeError('timeoutMs must be > 0');\n if (!(Number.isInteger(p.concurrency) && p.concurrency > 0)) throw new TypeError('concurrency must be > 0');\n return p;\n}\n","/**\n * Policy packs for Pompelmi.\n *\n * Pre-configured, named policies for common upload scenarios. Each pack\n * defines the file type allowlist, size limits, and timeout appropriate for\n * its use case.\n *\n * All packs are built on `definePolicy` and are fully overridable:\n *\n * ```ts\n * import { POLICY_PACKS } from 'pompelmi/policy-packs';\n *\n * // Use a pack as-is:\n * const policy = POLICY_PACKS['images-only'];\n *\n * // Or override individual fields:\n * import { definePolicy } from 'pompelmi';\n * const custom = definePolicy({ ...POLICY_PACKS['documents-only'], maxFileSizeBytes: 5 * 1024 * 1024 });\n * ```\n *\n * These packs are *deterministic* and *descriptor-based* — they do not\n * depend on any external threat intelligence feed.\n *\n * @module policy-packs\n */\n\nimport { definePolicy, type Policy } from './policy';\n\nconst KB = 1024;\nconst MB = 1024 * KB;\n\n// ── Policy packs ──────────────────────────────────────────────────────────────\n\n/**\n * Documents-only policy.\n *\n * Appropriate for: document management APIs, PDF/Office file upload endpoints,\n * data import pipelines.\n *\n * Allowed: PDF, Word (.docx/.doc), Excel (.xlsx/.xls), PowerPoint (.pptx/.ppt),\n * CSV, plain text, JSON, YAML, ODT/ODS/ODP (OpenDocument).\n * Max size: 25 MB.\n */\nexport const DOCUMENTS_ONLY: Policy = definePolicy({\n includeExtensions: [\n 'pdf',\n 'doc', 'docx',\n 'xls', 'xlsx',\n 'ppt', 'pptx',\n 'odt', 'ods', 'odp',\n 'csv',\n 'txt',\n 'json',\n 'yaml', 'yml',\n 'md',\n ],\n allowedMimeTypes: [\n 'application/pdf',\n 'application/msword',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n 'application/vnd.ms-excel',\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n 'application/vnd.ms-powerpoint',\n 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n 'application/vnd.oasis.opendocument.text',\n 'application/vnd.oasis.opendocument.spreadsheet',\n 'application/vnd.oasis.opendocument.presentation',\n 'text/csv',\n 'text/plain',\n 'application/json',\n 'text/yaml',\n 'text/markdown',\n ],\n maxFileSizeBytes: 25 * MB,\n timeoutMs: 10_000,\n concurrency: 4,\n failClosed: true,\n});\n\n/**\n * Images-only policy.\n *\n * Appropriate for: avatar uploads, product image APIs, content platforms with\n * user-generated imagery.\n *\n * Allowed: JPEG, PNG, GIF, WebP, AVIF, TIFF, BMP, ICO.\n * Max size: 10 MB.\n * Note: SVG is intentionally excluded — inline SVGs can contain scripts.\n */\nexport const IMAGES_ONLY: Policy = definePolicy({\n includeExtensions: ['jpg', 'jpeg', 'png', 'gif', 'webp', 'avif', 'tiff', 'tif', 'bmp', 'ico'],\n allowedMimeTypes: [\n 'image/jpeg',\n 'image/png',\n 'image/gif',\n 'image/webp',\n 'image/avif',\n 'image/tiff',\n 'image/bmp',\n 'image/x-icon',\n 'image/vnd.microsoft.icon',\n ],\n maxFileSizeBytes: 10 * MB,\n timeoutMs: 5_000,\n concurrency: 8,\n failClosed: true,\n});\n\n/**\n * Strict public-upload policy.\n *\n * Appropriate for: anonymous or low-trust upload endpoints, public APIs,\n * any surface exposed to untrusted users.\n *\n * Aggressive size limit (5 MB), short timeout, fail-closed, narrow MIME\n * allowlist. Only allows plain images and PDF.\n */\nexport const STRICT_PUBLIC_UPLOAD: Policy = definePolicy({\n includeExtensions: ['jpg', 'jpeg', 'png', 'webp', 'pdf'],\n allowedMimeTypes: [\n 'image/jpeg',\n 'image/png',\n 'image/webp',\n 'application/pdf',\n ],\n maxFileSizeBytes: 5 * MB,\n timeoutMs: 4_000,\n concurrency: 2,\n failClosed: true,\n});\n\n/**\n * Conservative default policy.\n *\n * A hardened version of the built-in `DEFAULT_POLICY` suitable for\n * production without further customisation. Stricter size limit and\n * shorter timeout than the permissive default.\n */\nexport const CONSERVATIVE_DEFAULT: Policy = definePolicy({\n includeExtensions: ['zip', 'png', 'jpg', 'jpeg', 'pdf', 'txt', 'csv', 'docx', 'xlsx'],\n allowedMimeTypes: [\n 'application/zip',\n 'image/png',\n 'image/jpeg',\n 'application/pdf',\n 'text/plain',\n 'text/csv',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n ],\n maxFileSizeBytes: 10 * MB,\n timeoutMs: 8_000,\n concurrency: 4,\n failClosed: true,\n});\n\n/**\n * Archives policy.\n *\n * Appropriate for: endpoints that accept ZIP, tar, or compressed archives.\n * Combines a generous size allowance with a longer timeout for deep inspection.\n *\n * NOTE: Pair this policy with `createZipBombGuard()` to defend against\n * decompression-bomb attacks:\n *\n * ```ts\n * import { composeScanners, createZipBombGuard, CommonHeuristicsScanner } from 'pompelmi';\n * const scanner = composeScanners(\n * [['zipGuard', createZipBombGuard()], ['heuristics', CommonHeuristicsScanner]]\n * );\n * ```\n */\nexport const ARCHIVES: Policy = definePolicy({\n includeExtensions: ['zip', 'tar', 'gz', 'tgz', 'bz2', 'xz', '7z', 'rar'],\n allowedMimeTypes: [\n 'application/zip',\n 'application/x-tar',\n 'application/gzip',\n 'application/x-bzip2',\n 'application/x-xz',\n 'application/x-7z-compressed',\n 'application/x-rar-compressed',\n ],\n maxFileSizeBytes: 100 * MB,\n timeoutMs: 30_000,\n concurrency: 2,\n failClosed: true,\n});\n\n// ── Named map ────────────────────────────────────────────────────────────────\n\nexport type PolicyPackName =\n | 'documents-only'\n | 'images-only'\n | 'strict-public-upload'\n | 'conservative-default'\n | 'archives';\n\n/**\n * Named map of all built-in policy packs.\n *\n * ```ts\n * import { POLICY_PACKS } from 'pompelmi/policy-packs';\n * const policy = POLICY_PACKS['strict-public-upload'];\n * ```\n */\nexport const POLICY_PACKS: Record<PolicyPackName, Policy> = {\n 'documents-only': DOCUMENTS_ONLY,\n 'images-only': IMAGES_ONLY,\n 'strict-public-upload': STRICT_PUBLIC_UPLOAD,\n 'conservative-default': CONSERVATIVE_DEFAULT,\n 'archives': ARCHIVES,\n};\n\n/**\n * Look up a policy pack by name.\n * Throws if the name is not recognised.\n */\nexport function getPolicyPack(name: PolicyPackName): Policy {\n const policy = POLICY_PACKS[name];\n if (!policy) throw new Error(`Unknown policy pack: '${name}'. Valid names: ${Object.keys(POLICY_PACKS).join(', ')}`);\n return policy;\n}\n"],"names":["MB"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pompelmi.policy-packs.cjs","sources":["../../src/policy.ts","../../src/policy-packs.ts"],"sourcesContent":["export interface Policy {\n includeExtensions: string[];\n allowedMimeTypes: string[];\n maxFileSizeBytes: number;\n timeoutMs: number;\n concurrency: number;\n failClosed: boolean;\n onScanEvent?: (ev: unknown) => void;\n}\nexport type PolicyInput = Partial<Policy>;\n\nconst MB = 1024 * 1024;\n\nexport const DEFAULT_POLICY: Policy = {\n includeExtensions: [\"zip\", \"png\", \"jpg\", \"jpeg\", \"pdf\"],\n allowedMimeTypes: [\"application/zip\", \"image/png\", \"image/jpeg\", \"application/pdf\", \"text/plain\"],\n maxFileSizeBytes: 20 * MB,\n timeoutMs: 5000,\n concurrency: 4,\n failClosed: true,\n};\n\nexport function definePolicy(input: PolicyInput = {}): Policy {\n const p: Policy = { ...DEFAULT_POLICY, ...input };\n if (!Array.isArray(p.includeExtensions))\n throw new TypeError(\"includeExtensions must be string[]\");\n if (!Array.isArray(p.allowedMimeTypes)) throw new TypeError(\"allowedMimeTypes must be string[]\");\n if (!(Number.isFinite(p.maxFileSizeBytes) && p.maxFileSizeBytes > 0))\n throw new TypeError(\"maxFileSizeBytes must be > 0\");\n if (!(Number.isFinite(p.timeoutMs) && p.timeoutMs > 0))\n throw new TypeError(\"timeoutMs must be > 0\");\n if (!(Number.isInteger(p.concurrency) && p.concurrency > 0))\n throw new TypeError(\"concurrency must be > 0\");\n return p;\n}\n","/**\n * Policy packs for Pompelmi.\n *\n * Pre-configured, named policies for common upload scenarios. Each pack\n * defines the file type allowlist, size limits, and timeout appropriate for\n * its use case.\n *\n * All packs are built on `definePolicy` and are fully overridable:\n *\n * ```ts\n * import { POLICY_PACKS } from 'pompelmi/policy-packs';\n *\n * // Use a pack as-is:\n * const policy = POLICY_PACKS['images-only'];\n *\n * // Or override individual fields:\n * import { definePolicy } from 'pompelmi';\n * const custom = definePolicy({ ...POLICY_PACKS['documents-only'], maxFileSizeBytes: 5 * 1024 * 1024 });\n * ```\n *\n * These packs are *deterministic* and *descriptor-based* — they do not\n * depend on any external threat intelligence feed.\n *\n * @module policy-packs\n */\n\nimport { definePolicy, type Policy } from \"./policy\";\n\nconst KB = 1024;\nconst MB = 1024 * KB;\n\n// ── Policy packs ──────────────────────────────────────────────────────────────\n\n/**\n * Documents-only policy.\n *\n * Appropriate for: document management APIs, PDF/Office file upload endpoints,\n * data import pipelines.\n *\n * Allowed: PDF, Word (.docx/.doc), Excel (.xlsx/.xls), PowerPoint (.pptx/.ppt),\n * CSV, plain text, JSON, YAML, ODT/ODS/ODP (OpenDocument).\n * Max size: 25 MB.\n */\nexport const DOCUMENTS_ONLY: Policy = definePolicy({\n includeExtensions: [\n \"pdf\",\n \"doc\",\n \"docx\",\n \"xls\",\n \"xlsx\",\n \"ppt\",\n \"pptx\",\n \"odt\",\n \"ods\",\n \"odp\",\n \"csv\",\n \"txt\",\n \"json\",\n \"yaml\",\n \"yml\",\n \"md\",\n ],\n allowedMimeTypes: [\n \"application/pdf\",\n \"application/msword\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n \"application/vnd.ms-excel\",\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n \"application/vnd.ms-powerpoint\",\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n \"application/vnd.oasis.opendocument.text\",\n \"application/vnd.oasis.opendocument.spreadsheet\",\n \"application/vnd.oasis.opendocument.presentation\",\n \"text/csv\",\n \"text/plain\",\n \"application/json\",\n \"text/yaml\",\n \"text/markdown\",\n ],\n maxFileSizeBytes: 25 * MB,\n timeoutMs: 10_000,\n concurrency: 4,\n failClosed: true,\n});\n\n/**\n * Images-only policy.\n *\n * Appropriate for: avatar uploads, product image APIs, content platforms with\n * user-generated imagery.\n *\n * Allowed: JPEG, PNG, GIF, WebP, AVIF, TIFF, BMP, ICO.\n * Max size: 10 MB.\n * Note: SVG is intentionally excluded — inline SVGs can contain scripts.\n */\nexport const IMAGES_ONLY: Policy = definePolicy({\n includeExtensions: [\"jpg\", \"jpeg\", \"png\", \"gif\", \"webp\", \"avif\", \"tiff\", \"tif\", \"bmp\", \"ico\"],\n allowedMimeTypes: [\n \"image/jpeg\",\n \"image/png\",\n \"image/gif\",\n \"image/webp\",\n \"image/avif\",\n \"image/tiff\",\n \"image/bmp\",\n \"image/x-icon\",\n \"image/vnd.microsoft.icon\",\n ],\n maxFileSizeBytes: 10 * MB,\n timeoutMs: 5_000,\n concurrency: 8,\n failClosed: true,\n});\n\n/**\n * Strict public-upload policy.\n *\n * Appropriate for: anonymous or low-trust upload endpoints, public APIs,\n * any surface exposed to untrusted users.\n *\n * Aggressive size limit (5 MB), short timeout, fail-closed, narrow MIME\n * allowlist. Only allows plain images and PDF.\n */\nexport const STRICT_PUBLIC_UPLOAD: Policy = definePolicy({\n includeExtensions: [\"jpg\", \"jpeg\", \"png\", \"webp\", \"pdf\"],\n allowedMimeTypes: [\"image/jpeg\", \"image/png\", \"image/webp\", \"application/pdf\"],\n maxFileSizeBytes: 5 * MB,\n timeoutMs: 4_000,\n concurrency: 2,\n failClosed: true,\n});\n\n/**\n * Conservative default policy.\n *\n * A hardened version of the built-in `DEFAULT_POLICY` suitable for\n * production without further customisation. Stricter size limit and\n * shorter timeout than the permissive default.\n */\nexport const CONSERVATIVE_DEFAULT: Policy = definePolicy({\n includeExtensions: [\"zip\", \"png\", \"jpg\", \"jpeg\", \"pdf\", \"txt\", \"csv\", \"docx\", \"xlsx\"],\n allowedMimeTypes: [\n \"application/zip\",\n \"image/png\",\n \"image/jpeg\",\n \"application/pdf\",\n \"text/plain\",\n \"text/csv\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n ],\n maxFileSizeBytes: 10 * MB,\n timeoutMs: 8_000,\n concurrency: 4,\n failClosed: true,\n});\n\n/**\n * Archives policy.\n *\n * Appropriate for: endpoints that accept ZIP, tar, or compressed archives.\n * Combines a generous size allowance with a longer timeout for deep inspection.\n *\n * NOTE: Pair this policy with `createZipBombGuard()` to defend against\n * decompression-bomb attacks:\n *\n * ```ts\n * import { composeScanners, createZipBombGuard, CommonHeuristicsScanner } from 'pompelmi';\n * const scanner = composeScanners(\n * [['zipGuard', createZipBombGuard()], ['heuristics', CommonHeuristicsScanner]]\n * );\n * ```\n */\nexport const ARCHIVES: Policy = definePolicy({\n includeExtensions: [\"zip\", \"tar\", \"gz\", \"tgz\", \"bz2\", \"xz\", \"7z\", \"rar\"],\n allowedMimeTypes: [\n \"application/zip\",\n \"application/x-tar\",\n \"application/gzip\",\n \"application/x-bzip2\",\n \"application/x-xz\",\n \"application/x-7z-compressed\",\n \"application/x-rar-compressed\",\n ],\n maxFileSizeBytes: 100 * MB,\n timeoutMs: 30_000,\n concurrency: 2,\n failClosed: true,\n});\n\n// ── Named map ────────────────────────────────────────────────────────────────\n\nexport type PolicyPackName =\n | \"documents-only\"\n | \"images-only\"\n | \"strict-public-upload\"\n | \"conservative-default\"\n | \"archives\";\n\n/**\n * Named map of all built-in policy packs.\n *\n * ```ts\n * import { POLICY_PACKS } from 'pompelmi/policy-packs';\n * const policy = POLICY_PACKS['strict-public-upload'];\n * ```\n */\nexport const POLICY_PACKS: Record<PolicyPackName, Policy> = {\n \"documents-only\": DOCUMENTS_ONLY,\n \"images-only\": IMAGES_ONLY,\n \"strict-public-upload\": STRICT_PUBLIC_UPLOAD,\n \"conservative-default\": CONSERVATIVE_DEFAULT,\n archives: ARCHIVES,\n};\n\n/**\n * Look up a policy pack by name.\n * Throws if the name is not recognised.\n */\nexport function getPolicyPack(name: PolicyPackName): Policy {\n const policy = POLICY_PACKS[name];\n if (!policy)\n throw new Error(\n `Unknown policy pack: '${name}'. Valid names: ${Object.keys(POLICY_PACKS).join(\", \")}`,\n );\n return policy;\n}\n"],"names":["MB"],"mappings":"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"}
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
const MB$1 = 1024 * 1024;
|
|
2
2
|
const DEFAULT_POLICY = {
|
|
3
|
-
includeExtensions: [
|
|
4
|
-
allowedMimeTypes: [
|
|
3
|
+
includeExtensions: ["zip", "png", "jpg", "jpeg", "pdf"],
|
|
4
|
+
allowedMimeTypes: ["application/zip", "image/png", "image/jpeg", "application/pdf", "text/plain"],
|
|
5
5
|
maxFileSizeBytes: 20 * MB$1,
|
|
6
6
|
timeoutMs: 5000,
|
|
7
7
|
concurrency: 4,
|
|
8
|
-
failClosed: true
|
|
8
|
+
failClosed: true,
|
|
9
9
|
};
|
|
10
10
|
function definePolicy(input = {}) {
|
|
11
11
|
const p = { ...DEFAULT_POLICY, ...input };
|
|
12
12
|
if (!Array.isArray(p.includeExtensions))
|
|
13
|
-
throw new TypeError(
|
|
13
|
+
throw new TypeError("includeExtensions must be string[]");
|
|
14
14
|
if (!Array.isArray(p.allowedMimeTypes))
|
|
15
|
-
throw new TypeError(
|
|
15
|
+
throw new TypeError("allowedMimeTypes must be string[]");
|
|
16
16
|
if (!(Number.isFinite(p.maxFileSizeBytes) && p.maxFileSizeBytes > 0))
|
|
17
|
-
throw new TypeError(
|
|
17
|
+
throw new TypeError("maxFileSizeBytes must be > 0");
|
|
18
18
|
if (!(Number.isFinite(p.timeoutMs) && p.timeoutMs > 0))
|
|
19
|
-
throw new TypeError(
|
|
19
|
+
throw new TypeError("timeoutMs must be > 0");
|
|
20
20
|
if (!(Number.isInteger(p.concurrency) && p.concurrency > 0))
|
|
21
|
-
throw new TypeError(
|
|
21
|
+
throw new TypeError("concurrency must be > 0");
|
|
22
22
|
return p;
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -62,33 +62,39 @@ const MB = 1024 * KB;
|
|
|
62
62
|
*/
|
|
63
63
|
const DOCUMENTS_ONLY = definePolicy({
|
|
64
64
|
includeExtensions: [
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
65
|
+
"pdf",
|
|
66
|
+
"doc",
|
|
67
|
+
"docx",
|
|
68
|
+
"xls",
|
|
69
|
+
"xlsx",
|
|
70
|
+
"ppt",
|
|
71
|
+
"pptx",
|
|
72
|
+
"odt",
|
|
73
|
+
"ods",
|
|
74
|
+
"odp",
|
|
75
|
+
"csv",
|
|
76
|
+
"txt",
|
|
77
|
+
"json",
|
|
78
|
+
"yaml",
|
|
79
|
+
"yml",
|
|
80
|
+
"md",
|
|
75
81
|
],
|
|
76
82
|
allowedMimeTypes: [
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
83
|
+
"application/pdf",
|
|
84
|
+
"application/msword",
|
|
85
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
86
|
+
"application/vnd.ms-excel",
|
|
87
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
88
|
+
"application/vnd.ms-powerpoint",
|
|
89
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
90
|
+
"application/vnd.oasis.opendocument.text",
|
|
91
|
+
"application/vnd.oasis.opendocument.spreadsheet",
|
|
92
|
+
"application/vnd.oasis.opendocument.presentation",
|
|
93
|
+
"text/csv",
|
|
94
|
+
"text/plain",
|
|
95
|
+
"application/json",
|
|
96
|
+
"text/yaml",
|
|
97
|
+
"text/markdown",
|
|
92
98
|
],
|
|
93
99
|
maxFileSizeBytes: 25 * MB,
|
|
94
100
|
timeoutMs: 10000,
|
|
@@ -106,17 +112,17 @@ const DOCUMENTS_ONLY = definePolicy({
|
|
|
106
112
|
* Note: SVG is intentionally excluded — inline SVGs can contain scripts.
|
|
107
113
|
*/
|
|
108
114
|
const IMAGES_ONLY = definePolicy({
|
|
109
|
-
includeExtensions: [
|
|
115
|
+
includeExtensions: ["jpg", "jpeg", "png", "gif", "webp", "avif", "tiff", "tif", "bmp", "ico"],
|
|
110
116
|
allowedMimeTypes: [
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
"image/jpeg",
|
|
118
|
+
"image/png",
|
|
119
|
+
"image/gif",
|
|
120
|
+
"image/webp",
|
|
121
|
+
"image/avif",
|
|
122
|
+
"image/tiff",
|
|
123
|
+
"image/bmp",
|
|
124
|
+
"image/x-icon",
|
|
125
|
+
"image/vnd.microsoft.icon",
|
|
120
126
|
],
|
|
121
127
|
maxFileSizeBytes: 10 * MB,
|
|
122
128
|
timeoutMs: 5000,
|
|
@@ -133,13 +139,8 @@ const IMAGES_ONLY = definePolicy({
|
|
|
133
139
|
* allowlist. Only allows plain images and PDF.
|
|
134
140
|
*/
|
|
135
141
|
const STRICT_PUBLIC_UPLOAD = definePolicy({
|
|
136
|
-
includeExtensions: [
|
|
137
|
-
allowedMimeTypes: [
|
|
138
|
-
'image/jpeg',
|
|
139
|
-
'image/png',
|
|
140
|
-
'image/webp',
|
|
141
|
-
'application/pdf',
|
|
142
|
-
],
|
|
142
|
+
includeExtensions: ["jpg", "jpeg", "png", "webp", "pdf"],
|
|
143
|
+
allowedMimeTypes: ["image/jpeg", "image/png", "image/webp", "application/pdf"],
|
|
143
144
|
maxFileSizeBytes: 5 * MB,
|
|
144
145
|
timeoutMs: 4000,
|
|
145
146
|
concurrency: 2,
|
|
@@ -153,16 +154,16 @@ const STRICT_PUBLIC_UPLOAD = definePolicy({
|
|
|
153
154
|
* shorter timeout than the permissive default.
|
|
154
155
|
*/
|
|
155
156
|
const CONSERVATIVE_DEFAULT = definePolicy({
|
|
156
|
-
includeExtensions: [
|
|
157
|
+
includeExtensions: ["zip", "png", "jpg", "jpeg", "pdf", "txt", "csv", "docx", "xlsx"],
|
|
157
158
|
allowedMimeTypes: [
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
159
|
+
"application/zip",
|
|
160
|
+
"image/png",
|
|
161
|
+
"image/jpeg",
|
|
162
|
+
"application/pdf",
|
|
163
|
+
"text/plain",
|
|
164
|
+
"text/csv",
|
|
165
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
166
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
166
167
|
],
|
|
167
168
|
maxFileSizeBytes: 10 * MB,
|
|
168
169
|
timeoutMs: 8000,
|
|
@@ -186,15 +187,15 @@ const CONSERVATIVE_DEFAULT = definePolicy({
|
|
|
186
187
|
* ```
|
|
187
188
|
*/
|
|
188
189
|
const ARCHIVES = definePolicy({
|
|
189
|
-
includeExtensions: [
|
|
190
|
+
includeExtensions: ["zip", "tar", "gz", "tgz", "bz2", "xz", "7z", "rar"],
|
|
190
191
|
allowedMimeTypes: [
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
192
|
+
"application/zip",
|
|
193
|
+
"application/x-tar",
|
|
194
|
+
"application/gzip",
|
|
195
|
+
"application/x-bzip2",
|
|
196
|
+
"application/x-xz",
|
|
197
|
+
"application/x-7z-compressed",
|
|
198
|
+
"application/x-rar-compressed",
|
|
198
199
|
],
|
|
199
200
|
maxFileSizeBytes: 100 * MB,
|
|
200
201
|
timeoutMs: 30000,
|
|
@@ -210,11 +211,11 @@ const ARCHIVES = definePolicy({
|
|
|
210
211
|
* ```
|
|
211
212
|
*/
|
|
212
213
|
const POLICY_PACKS = {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
214
|
+
"documents-only": DOCUMENTS_ONLY,
|
|
215
|
+
"images-only": IMAGES_ONLY,
|
|
216
|
+
"strict-public-upload": STRICT_PUBLIC_UPLOAD,
|
|
217
|
+
"conservative-default": CONSERVATIVE_DEFAULT,
|
|
218
|
+
archives: ARCHIVES,
|
|
218
219
|
};
|
|
219
220
|
/**
|
|
220
221
|
* Look up a policy pack by name.
|
|
@@ -223,7 +224,7 @@ const POLICY_PACKS = {
|
|
|
223
224
|
function getPolicyPack(name) {
|
|
224
225
|
const policy = POLICY_PACKS[name];
|
|
225
226
|
if (!policy)
|
|
226
|
-
throw new Error(`Unknown policy pack: '${name}'. Valid names: ${Object.keys(POLICY_PACKS).join(
|
|
227
|
+
throw new Error(`Unknown policy pack: '${name}'. Valid names: ${Object.keys(POLICY_PACKS).join(", ")}`);
|
|
227
228
|
return policy;
|
|
228
229
|
}
|
|
229
230
|
|