pompelmi 0.32.1 → 0.33.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.
@@ -3,6 +3,81 @@ import { createHash } from 'crypto';
3
3
  import * as os from 'os';
4
4
  import * as path from 'path';
5
5
 
6
+ function hasAsciiToken(buf, token) {
7
+ // Use latin1 so we can safely search binary
8
+ return buf.indexOf(token, 0, 'latin1') !== -1;
9
+ }
10
+ function startsWith(buf, bytes) {
11
+ if (buf.length < bytes.length)
12
+ return false;
13
+ for (let i = 0; i < bytes.length; i++)
14
+ if (buf[i] !== bytes[i])
15
+ return false;
16
+ return true;
17
+ }
18
+ function isPDF(buf) {
19
+ // %PDF-
20
+ return startsWith(buf, [0x25, 0x50, 0x44, 0x46, 0x2d]);
21
+ }
22
+ function isOleCfb(buf) {
23
+ // D0 CF 11 E0 A1 B1 1A E1
24
+ const sig = [0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1];
25
+ return startsWith(buf, sig);
26
+ }
27
+ function isZipLike$1(buf) {
28
+ // PK\x03\x04
29
+ return startsWith(buf, [0x50, 0x4b, 0x03, 0x04]);
30
+ }
31
+ function isPeExecutable(buf) {
32
+ // "MZ"
33
+ return startsWith(buf, [0x4d, 0x5a]);
34
+ }
35
+ /** OOXML macro hint via filename token in ZIP container */
36
+ function hasOoxmlMacros(buf) {
37
+ if (!isZipLike$1(buf))
38
+ return false;
39
+ return hasAsciiToken(buf, 'vbaProject.bin');
40
+ }
41
+ /** PDF risky features (/JavaScript, /OpenAction, /AA, /Launch) */
42
+ function pdfRiskTokens(buf) {
43
+ const tokens = ['/JavaScript', '/OpenAction', '/AA', '/Launch'];
44
+ return tokens.filter(t => hasAsciiToken(buf, t));
45
+ }
46
+ const CommonHeuristicsScanner = {
47
+ async scan(input) {
48
+ const buf = Buffer.from(input);
49
+ const matches = [];
50
+ // Office macros (OLE / OOXML)
51
+ if (isOleCfb(buf)) {
52
+ matches.push({ rule: 'office_ole_container', severity: 'suspicious' });
53
+ }
54
+ if (hasOoxmlMacros(buf)) {
55
+ matches.push({ rule: 'office_ooxml_macros', severity: 'suspicious' });
56
+ }
57
+ // PDF risky tokens
58
+ if (isPDF(buf)) {
59
+ const toks = pdfRiskTokens(buf);
60
+ if (toks.length) {
61
+ matches.push({
62
+ rule: 'pdf_risky_actions',
63
+ severity: 'suspicious',
64
+ meta: { tokens: toks }
65
+ });
66
+ }
67
+ }
68
+ // Executable header
69
+ if (isPeExecutable(buf)) {
70
+ matches.push({ rule: 'pe_executable_signature', severity: 'suspicious' });
71
+ }
72
+ // EICAR test file
73
+ const EICAR_NEEDLE = "X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!";
74
+ if (hasAsciiToken(buf, EICAR_NEEDLE)) {
75
+ matches.push({ rule: 'eicar_test_file', severity: 'high', meta: { note: 'EICAR standard antivirus test file detected' } });
76
+ }
77
+ return matches;
78
+ }
79
+ };
80
+
6
81
  function toScanFn(s) {
7
82
  return (typeof s === "function" ? s : s.scan);
8
83
  }
@@ -113,6 +188,8 @@ function composeScanners(...args) {
113
188
  }
114
189
  function createPresetScanner(preset, opts = {}) {
115
190
  const scanners = [];
191
+ // Always include heuristics (EICAR, PHP webshells, JS obfuscation, PE hints, etc.)
192
+ scanners.push(CommonHeuristicsScanner);
116
193
  // Add decompilation scanners based on preset
117
194
  if (preset === 'decompilation-basic' || preset === 'decompilation-deep' ||
118
195
  preset === 'malware-analysis' || opts.enableDecompilation) {
@@ -160,17 +237,6 @@ function createPresetScanner(preset, opts = {}) {
160
237
  }
161
238
  }
162
239
  }
163
- // Add other scanners for advanced presets
164
- if (preset === 'advanced' || preset === 'malware-analysis') {
165
- // Add heuristics scanner
166
- try {
167
- const { CommonHeuristicsScanner } = require('./scanners/common-heuristics');
168
- scanners.push(new CommonHeuristicsScanner());
169
- }
170
- catch {
171
- // Heuristics not available
172
- }
173
- }
174
240
  if (scanners.length === 0) {
175
241
  // Fallback scanner that returns no matches
176
242
  return async (_input, _ctx) => {
@@ -3121,76 +3187,6 @@ function mapMatchesToVerdict(matches = []) {
3121
3187
  return isMal ? 'malicious' : 'suspicious';
3122
3188
  }
3123
3189
 
3124
- function hasAsciiToken(buf, token) {
3125
- // Use latin1 so we can safely search binary
3126
- return buf.indexOf(token, 0, 'latin1') !== -1;
3127
- }
3128
- function startsWith(buf, bytes) {
3129
- if (buf.length < bytes.length)
3130
- return false;
3131
- for (let i = 0; i < bytes.length; i++)
3132
- if (buf[i] !== bytes[i])
3133
- return false;
3134
- return true;
3135
- }
3136
- function isPDF(buf) {
3137
- // %PDF-
3138
- return startsWith(buf, [0x25, 0x50, 0x44, 0x46, 0x2d]);
3139
- }
3140
- function isOleCfb(buf) {
3141
- // D0 CF 11 E0 A1 B1 1A E1
3142
- const sig = [0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1];
3143
- return startsWith(buf, sig);
3144
- }
3145
- function isZipLike$1(buf) {
3146
- // PK\x03\x04
3147
- return startsWith(buf, [0x50, 0x4b, 0x03, 0x04]);
3148
- }
3149
- function isPeExecutable(buf) {
3150
- // "MZ"
3151
- return startsWith(buf, [0x4d, 0x5a]);
3152
- }
3153
- /** OOXML macro hint via filename token in ZIP container */
3154
- function hasOoxmlMacros(buf) {
3155
- if (!isZipLike$1(buf))
3156
- return false;
3157
- return hasAsciiToken(buf, 'vbaProject.bin');
3158
- }
3159
- /** PDF risky features (/JavaScript, /OpenAction, /AA, /Launch) */
3160
- function pdfRiskTokens(buf) {
3161
- const tokens = ['/JavaScript', '/OpenAction', '/AA', '/Launch'];
3162
- return tokens.filter(t => hasAsciiToken(buf, t));
3163
- }
3164
- const CommonHeuristicsScanner = {
3165
- async scan(input) {
3166
- const buf = Buffer.from(input);
3167
- const matches = [];
3168
- // Office macros (OLE / OOXML)
3169
- if (isOleCfb(buf)) {
3170
- matches.push({ rule: 'office_ole_container', severity: 'suspicious' });
3171
- }
3172
- if (hasOoxmlMacros(buf)) {
3173
- matches.push({ rule: 'office_ooxml_macros', severity: 'suspicious' });
3174
- }
3175
- // PDF risky tokens
3176
- if (isPDF(buf)) {
3177
- const toks = pdfRiskTokens(buf);
3178
- if (toks.length) {
3179
- matches.push({
3180
- rule: 'pdf_risky_actions',
3181
- severity: 'suspicious',
3182
- meta: { tokens: toks }
3183
- });
3184
- }
3185
- }
3186
- // Executable header
3187
- if (isPeExecutable(buf)) {
3188
- matches.push({ rule: 'pe_executable_signature', severity: 'suspicious' });
3189
- }
3190
- return matches;
3191
- }
3192
- };
3193
-
3194
3190
  const SIG_CEN = 0x02014b50;
3195
3191
  const DEFAULTS = {
3196
3192
  maxEntries: 1000,