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.
@@ -979,7 +979,7 @@ function composeScanners(...args) {
979
979
  const all = [];
980
980
  if (opts.parallel) {
981
981
  // Parallel execution — collect all results then return
982
- const results = await Promise.allSettled(entries.map(([name, scanner]) => runWithTimeout(() => toScanFn(scanner)(input, ctx), opts.timeoutMsPerScanner)));
982
+ const results = await Promise.allSettled(entries.map(([_name, scanner]) => runWithTimeout(() => toScanFn(scanner)(input, ctx), opts.timeoutMsPerScanner)));
983
983
  for (let i = 0; i < results.length; i++) {
984
984
  const result = results[i];
985
985
  if (result.status === "fulfilled" && Array.isArray(result.value)) {
@@ -1033,75 +1033,62 @@ function composeScanners(...args) {
1033
1033
  };
1034
1034
  }
1035
1035
  function createPresetScanner(preset, opts = {}) {
1036
- const scanners = [];
1037
- // Always include heuristics (EICAR, PHP webshells, JS obfuscation, PE hints, etc.)
1038
- scanners.push(CommonHeuristicsScanner);
1036
+ const baseScanners = [CommonHeuristicsScanner];
1037
+ const dynamicScannerPromises = [];
1039
1038
  // Add decompilation scanners based on preset
1040
1039
  if (preset === "decompilation-basic" ||
1041
1040
  preset === "decompilation-deep" ||
1042
1041
  preset === "malware-analysis" ||
1043
1042
  opts.enableDecompilation) {
1044
- const depth = preset === "decompilation-deep"
1043
+ const depth = preset === "decompilation-deep" || preset === "malware-analysis"
1045
1044
  ? "deep"
1046
1045
  : preset === "decompilation-basic"
1047
1046
  ? "basic"
1048
1047
  : opts.decompilationDepth || "basic";
1049
- if (!opts.decompilationEngine ||
1050
- opts.decompilationEngine === "binaryninja-hlil" ||
1051
- opts.decompilationEngine === "both") {
1052
- try {
1053
- // Dynamic import to avoid bundling issues - using Function to bypass TypeScript type checking
1054
- const importModule = new Function("specifier", "return import(specifier)");
1055
- importModule("@pompelmi/engine-binaryninja")
1056
- .then((mod) => {
1057
- const binjaScanner = mod.createBinaryNinjaScanner({
1058
- timeout: opts.decompilationTimeout || opts.timeout || 30000,
1059
- depth,
1060
- pythonPath: opts.pythonPath,
1061
- binaryNinjaPath: opts.binaryNinjaPath,
1062
- });
1063
- scanners.push(binjaScanner);
1064
- })
1065
- .catch(() => {
1066
- // Binary Ninja engine not available - silently skip
1067
- });
1068
- }
1069
- catch {
1070
- // Engine not installed
1071
- }
1072
- }
1073
- if (!opts.decompilationEngine ||
1074
- opts.decompilationEngine === "ghidra-pcode" ||
1075
- opts.decompilationEngine === "both") {
1076
- try {
1077
- // Dynamic import for Ghidra engine (when implemented) - using Function to bypass TypeScript type checking
1078
- const importModule = new Function("specifier", "return import(specifier)");
1079
- importModule("@pompelmi/engine-ghidra")
1080
- .then((mod) => {
1081
- const ghidraScanner = mod.createGhidraScanner({
1082
- timeout: opts.decompilationTimeout || opts.timeout || 30000,
1083
- depth,
1084
- ghidraPath: opts.ghidraPath,
1085
- analyzeHeadless: opts.analyzeHeadless,
1086
- });
1087
- scanners.push(ghidraScanner);
1088
- })
1089
- .catch(() => {
1090
- // Ghidra engine not available - silently skip
1091
- });
1092
- }
1093
- catch {
1094
- // Engine not installed
1095
- }
1048
+ let importModule;
1049
+ try {
1050
+ // Dynamic import to avoid bundling issues - using Function to bypass TypeScript type checking
1051
+ importModule = new Function("specifier", "return import(specifier)");
1096
1052
  }
1097
- }
1098
- if (scanners.length === 0) {
1099
- // Fallback scanner that returns no matches
1100
- return async (_input, _ctx) => {
1101
- return [];
1102
- };
1103
- }
1104
- return composeScanners(...scanners);
1053
+ catch {
1054
+ importModule = undefined;
1055
+ }
1056
+ if (importModule &&
1057
+ (!opts.decompilationEngine ||
1058
+ opts.decompilationEngine === "binaryninja-hlil" ||
1059
+ opts.decompilationEngine === "both")) {
1060
+ dynamicScannerPromises.push(importModule("@pompelmi/engine-binaryninja")
1061
+ .then((mod) => mod.createBinaryNinjaScanner({
1062
+ timeout: opts.decompilationTimeout || opts.timeout || 30000,
1063
+ depth,
1064
+ pythonPath: opts.pythonPath,
1065
+ binaryNinjaPath: opts.binaryNinjaPath,
1066
+ }))
1067
+ .catch(() => null));
1068
+ }
1069
+ if (importModule &&
1070
+ (!opts.decompilationEngine ||
1071
+ opts.decompilationEngine === "ghidra-pcode" ||
1072
+ opts.decompilationEngine === "both")) {
1073
+ dynamicScannerPromises.push(importModule("@pompelmi/engine-ghidra")
1074
+ .then((mod) => mod.createGhidraScanner({
1075
+ timeout: opts.decompilationTimeout || opts.timeout || 30000,
1076
+ depth,
1077
+ ghidraPath: opts.ghidraPath,
1078
+ analyzeHeadless: opts.analyzeHeadless,
1079
+ }))
1080
+ .catch(() => null));
1081
+ }
1082
+ }
1083
+ let composedScannerPromise;
1084
+ const getComposedScanner = async () => {
1085
+ composedScannerPromise ?? (composedScannerPromise = Promise.all(dynamicScannerPromises).then((dynamicScanners) => composeScanners(...baseScanners, ...dynamicScanners.filter((scanner) => scanner !== null))));
1086
+ return composedScannerPromise;
1087
+ };
1088
+ return async (input, ctx) => {
1089
+ const scanner = await getComposedScanner();
1090
+ return scanner(input, ctx);
1091
+ };
1105
1092
  }
1106
1093
 
1107
1094
  /**
@@ -1993,10 +1980,14 @@ class BatchScanner {
1993
1980
  processingQueue.push(promise);
1994
1981
  currentIndex++;
1995
1982
  // Remove completed promises from queue
1996
- promise.finally(() => {
1983
+ promise
1984
+ .finally(() => {
1997
1985
  const idx = processingQueue.indexOf(promise);
1998
1986
  if (idx > -1)
1999
1987
  processingQueue.splice(idx, 1);
1988
+ })
1989
+ .catch(() => {
1990
+ // Rejections are handled by the main queue waits; swallow the cleanup chain.
2000
1991
  });
2001
1992
  }
2002
1993
  // Wait for at least one task to complete before continuing