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