pompelmi 0.31.0 → 0.32.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -1
- package/dist/pompelmi.cjs +90 -1
- package/dist/pompelmi.cjs.map +1 -1
- package/dist/pompelmi.esm.js +90 -1
- package/dist/pompelmi.esm.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/presets.d.ts +55 -2
- package/dist/types/types.d.ts +1 -1
- package/package.json +1 -1
- package/CHANGELOG_v0.29.0.md +0 -151
package/dist/pompelmi.esm.js
CHANGED
|
@@ -6,7 +6,96 @@ import * as path from 'path';
|
|
|
6
6
|
function toScanFn(s) {
|
|
7
7
|
return (typeof s === "function" ? s : s.scan);
|
|
8
8
|
}
|
|
9
|
-
|
|
9
|
+
/** Map a Match's severity field to a Verdict for stopOn comparison. */
|
|
10
|
+
function matchToVerdict(m) {
|
|
11
|
+
const s = m.severity;
|
|
12
|
+
if (s === "critical" || s === "high" || s === "malicious")
|
|
13
|
+
return "malicious";
|
|
14
|
+
if (s === "medium" || s === "low" || s === "suspicious" || s === "info")
|
|
15
|
+
return "suspicious";
|
|
16
|
+
return "clean";
|
|
17
|
+
}
|
|
18
|
+
/** Highest verdict across all matches in the list. */
|
|
19
|
+
function highestSeverity(matches) {
|
|
20
|
+
if (matches.length === 0)
|
|
21
|
+
return null;
|
|
22
|
+
if (matches.some((m) => matchToVerdict(m) === "malicious"))
|
|
23
|
+
return "malicious";
|
|
24
|
+
if (matches.some((m) => matchToVerdict(m) === "suspicious"))
|
|
25
|
+
return "suspicious";
|
|
26
|
+
return "clean";
|
|
27
|
+
}
|
|
28
|
+
const SEVERITY_RANK = { malicious: 2, suspicious: 1, clean: 0 };
|
|
29
|
+
function shouldStop(matches, stopOn) {
|
|
30
|
+
if (!stopOn)
|
|
31
|
+
return false;
|
|
32
|
+
const highest = highestSeverity(matches);
|
|
33
|
+
if (!highest)
|
|
34
|
+
return false;
|
|
35
|
+
return SEVERITY_RANK[highest] >= SEVERITY_RANK[stopOn];
|
|
36
|
+
}
|
|
37
|
+
async function runWithTimeout(fn, timeoutMs) {
|
|
38
|
+
if (!timeoutMs)
|
|
39
|
+
return fn();
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
const timer = setTimeout(() => reject(new Error("scanner timeout")), timeoutMs);
|
|
42
|
+
fn().then((v) => { clearTimeout(timer); resolve(v); }, (e) => { clearTimeout(timer); reject(e); });
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
46
|
+
function composeScanners(...args) {
|
|
47
|
+
const first = args[0];
|
|
48
|
+
const rest = args.slice(1);
|
|
49
|
+
// ── Named-scanner array form ──────────────────────────────────────────────
|
|
50
|
+
if (Array.isArray(first) &&
|
|
51
|
+
(first.length === 0 || (Array.isArray(first[0]) && typeof first[0][0] === "string"))) {
|
|
52
|
+
const entries = first;
|
|
53
|
+
const opts = rest.length > 0 && !Array.isArray(rest[0]) && typeof rest[0] !== "function" &&
|
|
54
|
+
!(typeof rest[0] === "object" && rest[0] !== null && "scan" in rest[0])
|
|
55
|
+
? rest[0]
|
|
56
|
+
: {};
|
|
57
|
+
return async (input, ctx) => {
|
|
58
|
+
const all = [];
|
|
59
|
+
if (opts.parallel) {
|
|
60
|
+
// Parallel execution — collect all results then return
|
|
61
|
+
const results = await Promise.allSettled(entries.map(([name, scanner]) => runWithTimeout(() => toScanFn(scanner)(input, ctx), opts.timeoutMsPerScanner)));
|
|
62
|
+
for (let i = 0; i < results.length; i++) {
|
|
63
|
+
const result = results[i];
|
|
64
|
+
if (result.status === "fulfilled" && Array.isArray(result.value)) {
|
|
65
|
+
const matches = opts.tagSourceName
|
|
66
|
+
? result.value.map((m) => ({
|
|
67
|
+
...m,
|
|
68
|
+
meta: { ...m.meta, _sourceName: entries[i][0] },
|
|
69
|
+
}))
|
|
70
|
+
: result.value;
|
|
71
|
+
all.push(...matches);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
// Sequential execution with optional stopOn short-circuit
|
|
77
|
+
for (const [name, scanner] of entries) {
|
|
78
|
+
try {
|
|
79
|
+
const out = await runWithTimeout(() => toScanFn(scanner)(input, ctx), opts.timeoutMsPerScanner);
|
|
80
|
+
if (Array.isArray(out)) {
|
|
81
|
+
const matches = opts.tagSourceName
|
|
82
|
+
? out.map((m) => ({ ...m, meta: { ...m.meta, _sourceName: name } }))
|
|
83
|
+
: out;
|
|
84
|
+
all.push(...matches);
|
|
85
|
+
if (shouldStop(all, opts.stopOn))
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// individual scanner failure is non-fatal
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return all;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
// ── Variadic form (backward-compatible) ───────────────────────────────────
|
|
98
|
+
const scanners = [first, ...rest].filter(Boolean);
|
|
10
99
|
return async (input, ctx) => {
|
|
11
100
|
const all = [];
|
|
12
101
|
for (const s of scanners) {
|