pompelmi 0.35.0 → 0.35.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.
@@ -374,7 +374,7 @@ function composeScanners(...args) {
374
374
  const all = [];
375
375
  if (opts.parallel) {
376
376
  // Parallel execution — collect all results then return
377
- const results = await Promise.allSettled(entries.map(([name, scanner]) => runWithTimeout(() => toScanFn(scanner)(input, ctx), opts.timeoutMsPerScanner)));
377
+ const results = await Promise.allSettled(entries.map(([_name, scanner]) => runWithTimeout(() => toScanFn(scanner)(input, ctx), opts.timeoutMsPerScanner)));
378
378
  for (let i = 0; i < results.length; i++) {
379
379
  const result = results[i];
380
380
  if (result.status === "fulfilled" && Array.isArray(result.value)) {
@@ -428,75 +428,62 @@ function composeScanners(...args) {
428
428
  };
429
429
  }
430
430
  function createPresetScanner(preset, opts = {}) {
431
- const scanners = [];
432
- // Always include heuristics (EICAR, PHP webshells, JS obfuscation, PE hints, etc.)
433
- scanners.push(CommonHeuristicsScanner);
431
+ const baseScanners = [CommonHeuristicsScanner];
432
+ const dynamicScannerPromises = [];
434
433
  // Add decompilation scanners based on preset
435
434
  if (preset === "decompilation-basic" ||
436
435
  preset === "decompilation-deep" ||
437
436
  preset === "malware-analysis" ||
438
437
  opts.enableDecompilation) {
439
- const depth = preset === "decompilation-deep"
438
+ const depth = preset === "decompilation-deep" || preset === "malware-analysis"
440
439
  ? "deep"
441
440
  : preset === "decompilation-basic"
442
441
  ? "basic"
443
442
  : opts.decompilationDepth || "basic";
444
- if (!opts.decompilationEngine ||
445
- opts.decompilationEngine === "binaryninja-hlil" ||
446
- opts.decompilationEngine === "both") {
447
- try {
448
- // Dynamic import to avoid bundling issues - using Function to bypass TypeScript type checking
449
- const importModule = new Function("specifier", "return import(specifier)");
450
- importModule("@pompelmi/engine-binaryninja")
451
- .then((mod) => {
452
- const binjaScanner = mod.createBinaryNinjaScanner({
453
- timeout: opts.decompilationTimeout || opts.timeout || 30000,
454
- depth,
455
- pythonPath: opts.pythonPath,
456
- binaryNinjaPath: opts.binaryNinjaPath,
457
- });
458
- scanners.push(binjaScanner);
459
- })
460
- .catch(() => {
461
- // Binary Ninja engine not available - silently skip
462
- });
463
- }
464
- catch {
465
- // Engine not installed
466
- }
443
+ let importModule;
444
+ try {
445
+ // Dynamic import to avoid bundling issues - using Function to bypass TypeScript type checking
446
+ importModule = new Function("specifier", "return import(specifier)");
467
447
  }
468
- if (!opts.decompilationEngine ||
469
- opts.decompilationEngine === "ghidra-pcode" ||
470
- opts.decompilationEngine === "both") {
471
- try {
472
- // Dynamic import for Ghidra engine (when implemented) - using Function to bypass TypeScript type checking
473
- const importModule = new Function("specifier", "return import(specifier)");
474
- importModule("@pompelmi/engine-ghidra")
475
- .then((mod) => {
476
- const ghidraScanner = mod.createGhidraScanner({
477
- timeout: opts.decompilationTimeout || opts.timeout || 30000,
478
- depth,
479
- ghidraPath: opts.ghidraPath,
480
- analyzeHeadless: opts.analyzeHeadless,
481
- });
482
- scanners.push(ghidraScanner);
483
- })
484
- .catch(() => {
485
- // Ghidra engine not available - silently skip
486
- });
487
- }
488
- catch {
489
- // Engine not installed
490
- }
448
+ catch {
449
+ importModule = undefined;
450
+ }
451
+ if (importModule &&
452
+ (!opts.decompilationEngine ||
453
+ opts.decompilationEngine === "binaryninja-hlil" ||
454
+ opts.decompilationEngine === "both")) {
455
+ dynamicScannerPromises.push(importModule("@pompelmi/engine-binaryninja")
456
+ .then((mod) => mod.createBinaryNinjaScanner({
457
+ timeout: opts.decompilationTimeout || opts.timeout || 30000,
458
+ depth,
459
+ pythonPath: opts.pythonPath,
460
+ binaryNinjaPath: opts.binaryNinjaPath,
461
+ }))
462
+ .catch(() => null));
463
+ }
464
+ if (importModule &&
465
+ (!opts.decompilationEngine ||
466
+ opts.decompilationEngine === "ghidra-pcode" ||
467
+ opts.decompilationEngine === "both")) {
468
+ dynamicScannerPromises.push(importModule("@pompelmi/engine-ghidra")
469
+ .then((mod) => mod.createGhidraScanner({
470
+ timeout: opts.decompilationTimeout || opts.timeout || 30000,
471
+ depth,
472
+ ghidraPath: opts.ghidraPath,
473
+ analyzeHeadless: opts.analyzeHeadless,
474
+ }))
475
+ .catch(() => null));
491
476
  }
492
477
  }
493
- if (scanners.length === 0) {
494
- // Fallback scanner that returns no matches
495
- return async (_input, _ctx) => {
496
- return [];
497
- };
498
- }
499
- return composeScanners(...scanners);
478
+ let composedScannerPromise;
479
+ const getComposedScanner = async () => {
480
+ composedScannerPromise ?? (composedScannerPromise = Promise.all(dynamicScannerPromises).then((dynamicScanners) => composeScanners(...baseScanners, ...dynamicScanners.filter((scanner) => scanner !== null))));
481
+ return composedScannerPromise;
482
+ };
483
+ return async (input, ctx) => {
484
+ const scanner = await getComposedScanner();
485
+ return scanner(input, ctx);
486
+ };
500
487
  }
501
488
 
502
489
  /**