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