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.
package/dist/pompelmi.cjs CHANGED
@@ -1001,7 +1001,7 @@ function composeScanners(...args) {
1001
1001
  const all = [];
1002
1002
  if (opts.parallel) {
1003
1003
  // Parallel execution — collect all results then return
1004
- const results = await Promise.allSettled(entries.map(([name, scanner]) => runWithTimeout(() => toScanFn(scanner)(input, ctx), opts.timeoutMsPerScanner)));
1004
+ const results = await Promise.allSettled(entries.map(([_name, scanner]) => runWithTimeout(() => toScanFn(scanner)(input, ctx), opts.timeoutMsPerScanner)));
1005
1005
  for (let i = 0; i < results.length; i++) {
1006
1006
  const result = results[i];
1007
1007
  if (result.status === "fulfilled" && Array.isArray(result.value)) {
@@ -1055,75 +1055,62 @@ function composeScanners(...args) {
1055
1055
  };
1056
1056
  }
1057
1057
  function createPresetScanner(preset, opts = {}) {
1058
- const scanners = [];
1059
- // Always include heuristics (EICAR, PHP webshells, JS obfuscation, PE hints, etc.)
1060
- scanners.push(CommonHeuristicsScanner);
1058
+ const baseScanners = [CommonHeuristicsScanner];
1059
+ const dynamicScannerPromises = [];
1061
1060
  // Add decompilation scanners based on preset
1062
1061
  if (preset === "decompilation-basic" ||
1063
1062
  preset === "decompilation-deep" ||
1064
1063
  preset === "malware-analysis" ||
1065
1064
  opts.enableDecompilation) {
1066
- const depth = preset === "decompilation-deep"
1065
+ const depth = preset === "decompilation-deep" || preset === "malware-analysis"
1067
1066
  ? "deep"
1068
1067
  : preset === "decompilation-basic"
1069
1068
  ? "basic"
1070
1069
  : opts.decompilationDepth || "basic";
1071
- if (!opts.decompilationEngine ||
1072
- opts.decompilationEngine === "binaryninja-hlil" ||
1073
- opts.decompilationEngine === "both") {
1074
- try {
1075
- // Dynamic import to avoid bundling issues - using Function to bypass TypeScript type checking
1076
- const importModule = new Function("specifier", "return import(specifier)");
1077
- importModule("@pompelmi/engine-binaryninja")
1078
- .then((mod) => {
1079
- const binjaScanner = mod.createBinaryNinjaScanner({
1080
- timeout: opts.decompilationTimeout || opts.timeout || 30000,
1081
- depth,
1082
- pythonPath: opts.pythonPath,
1083
- binaryNinjaPath: opts.binaryNinjaPath,
1084
- });
1085
- scanners.push(binjaScanner);
1086
- })
1087
- .catch(() => {
1088
- // Binary Ninja engine not available - silently skip
1089
- });
1090
- }
1091
- catch {
1092
- // Engine not installed
1093
- }
1094
- }
1095
- if (!opts.decompilationEngine ||
1096
- opts.decompilationEngine === "ghidra-pcode" ||
1097
- opts.decompilationEngine === "both") {
1098
- try {
1099
- // Dynamic import for Ghidra engine (when implemented) - using Function to bypass TypeScript type checking
1100
- const importModule = new Function("specifier", "return import(specifier)");
1101
- importModule("@pompelmi/engine-ghidra")
1102
- .then((mod) => {
1103
- const ghidraScanner = mod.createGhidraScanner({
1104
- timeout: opts.decompilationTimeout || opts.timeout || 30000,
1105
- depth,
1106
- ghidraPath: opts.ghidraPath,
1107
- analyzeHeadless: opts.analyzeHeadless,
1108
- });
1109
- scanners.push(ghidraScanner);
1110
- })
1111
- .catch(() => {
1112
- // Ghidra engine not available - silently skip
1113
- });
1114
- }
1115
- catch {
1116
- // Engine not installed
1117
- }
1070
+ let importModule;
1071
+ try {
1072
+ // Dynamic import to avoid bundling issues - using Function to bypass TypeScript type checking
1073
+ importModule = new Function("specifier", "return import(specifier)");
1118
1074
  }
1119
- }
1120
- if (scanners.length === 0) {
1121
- // Fallback scanner that returns no matches
1122
- return async (_input, _ctx) => {
1123
- return [];
1124
- };
1125
- }
1126
- return composeScanners(...scanners);
1075
+ catch {
1076
+ importModule = undefined;
1077
+ }
1078
+ if (importModule &&
1079
+ (!opts.decompilationEngine ||
1080
+ opts.decompilationEngine === "binaryninja-hlil" ||
1081
+ opts.decompilationEngine === "both")) {
1082
+ dynamicScannerPromises.push(importModule("@pompelmi/engine-binaryninja")
1083
+ .then((mod) => mod.createBinaryNinjaScanner({
1084
+ timeout: opts.decompilationTimeout || opts.timeout || 30000,
1085
+ depth,
1086
+ pythonPath: opts.pythonPath,
1087
+ binaryNinjaPath: opts.binaryNinjaPath,
1088
+ }))
1089
+ .catch(() => null));
1090
+ }
1091
+ if (importModule &&
1092
+ (!opts.decompilationEngine ||
1093
+ opts.decompilationEngine === "ghidra-pcode" ||
1094
+ opts.decompilationEngine === "both")) {
1095
+ dynamicScannerPromises.push(importModule("@pompelmi/engine-ghidra")
1096
+ .then((mod) => mod.createGhidraScanner({
1097
+ timeout: opts.decompilationTimeout || opts.timeout || 30000,
1098
+ depth,
1099
+ ghidraPath: opts.ghidraPath,
1100
+ analyzeHeadless: opts.analyzeHeadless,
1101
+ }))
1102
+ .catch(() => null));
1103
+ }
1104
+ }
1105
+ let composedScannerPromise;
1106
+ const getComposedScanner = async () => {
1107
+ composedScannerPromise ?? (composedScannerPromise = Promise.all(dynamicScannerPromises).then((dynamicScanners) => composeScanners(...baseScanners, ...dynamicScanners.filter((scanner) => scanner !== null))));
1108
+ return composedScannerPromise;
1109
+ };
1110
+ return async (input, ctx) => {
1111
+ const scanner = await getComposedScanner();
1112
+ return scanner(input, ctx);
1113
+ };
1127
1114
  }
1128
1115
 
1129
1116
  /**
@@ -2015,10 +2002,14 @@ class BatchScanner {
2015
2002
  processingQueue.push(promise);
2016
2003
  currentIndex++;
2017
2004
  // Remove completed promises from queue
2018
- promise.finally(() => {
2005
+ promise
2006
+ .finally(() => {
2019
2007
  const idx = processingQueue.indexOf(promise);
2020
2008
  if (idx > -1)
2021
2009
  processingQueue.splice(idx, 1);
2010
+ })
2011
+ .catch(() => {
2012
+ // Rejections are handled by the main queue waits; swallow the cleanup chain.
2022
2013
  });
2023
2014
  }
2024
2015
  // Wait for at least one task to complete before continuing