muaddib-scanner 2.6.0 → 2.6.2

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/README.md CHANGED
@@ -642,7 +642,7 @@ Alerts appear in Security > Code scanning alerts.
642
642
  ## Architecture
643
643
 
644
644
  ```
645
- MUAD'DIB 2.5.17 Scanner
645
+ MUAD'DIB 2.6.1 Scanner
646
646
  |
647
647
  +-- IOC Match (225,000+ packages, JSON DB)
648
648
  | +-- OSV.dev npm dump (200K+ MAL-* entries)
@@ -773,7 +773,7 @@ All 2,077 misses lack Node.js malware patterns. MUAD'DIB performs AST-based Node
773
773
  | Large (50-100 JS files) | 40 | 10 | 25.0% |
774
774
  | Very large (100+ JS files) | 62 | 25 | 40.3% |
775
775
 
776
- **FPR progression**: 0% (invalid, empty dirs, v2.2.0-v2.2.6) → 38% (first real measurement, v2.2.7) → 19.4% (v2.2.8) → 17.5% (v2.2.9) → ~13% (v2.2.11, per-file max scoring) → 8.9% (v2.3.0, P2) → 7.4% (v2.3.1, P3) → 6.0% (v2.5.8, P4 + IOC wildcard audit) → ~13.6% (v2.5.14, audit hardening added stricter detection) → **12.3%** (v2.5.16, P5 + P6) → **12.3%** (v2.6.0, intent graph v2 — zero FP added)
776
+ **FPR progression**: 0% (invalid, empty dirs, v2.2.0-v2.2.6) → 38% (first real measurement, v2.2.7) → 19.4% (v2.2.8) → 17.5% (v2.2.9) → ~13% (v2.2.11, per-file max scoring) → 8.9% (v2.3.0, P2) → 7.4% (v2.3.1, P3) → 6.0% (v2.5.8, P4 + IOC wildcard audit) → ~13.6% (v2.5.14, audit hardening added stricter detection) → **12.3%** (v2.5.16, P5 + P6) → **12.3%** (v2.6.0, intent graph v2 — zero FP added) → **12.3%** (v2.6.1, module-graph bounded path — zero FP added)
777
777
 
778
778
  > **Note on FPR evolution:** The historic 6.0% FPR (v2.5.8) relied on a `BENIGN_PACKAGE_WHITELIST` that excluded certain known packages from scoring — a data leakage bias removed in v2.5.10. The current 12.3% FPR is an honest measurement without whitelisting, against 532 real benign packages. The intent graph (v2.6.0) adds zero false positives by using intra-file pairing only and excluding LOW-severity threats.
779
779
 
@@ -793,7 +793,7 @@ All 2,077 misses lack Node.js malware patterns. MUAD'DIB performs AST-based Node
793
793
  - **ADR** (Adversarial Detection Rate): detection rate on 120 evasive malicious samples — 53 adversarial + 40 holdout (6 adversarial waves + 4 holdout batches). 75 available on disk. 2 misses on available samples: `require-cache-poison` (P3 trade-off), `getter-defineProperty-exfil`.
794
794
  - **Holdout** (pre-tuning): detection rate on 10 unseen samples with rules frozen (measures generalization)
795
795
 
796
- Datasets: 17,922 Datadog malware samples, 532 npm + 132 PyPI benign packages, 120 adversarial/holdout samples (75 available on disk), 51 ground-truth attacks (65 documented malware packages). **1905 tests**, 86% code coverage.
796
+ Datasets: 17,922 Datadog malware samples, 532 npm + 132 PyPI benign packages, 120 adversarial/holdout samples (75 available on disk), 51 ground-truth attacks (65 documented malware packages). **1932 tests**, 86% code coverage.
797
797
 
798
798
  See [Evaluation Methodology](docs/EVALUATION_METHODOLOGY.md) for the full experimental protocol.
799
799
 
@@ -829,7 +829,7 @@ npm test
829
829
 
830
830
  ### Testing
831
831
 
832
- - **1905 unit/integration tests** across 44 modular test files - 86% code coverage via [Codecov](https://codecov.io/gh/DNSZLSK/muad-dib)
832
+ - **1932 unit/integration tests** across 44 modular test files - 86% code coverage via [Codecov](https://codecov.io/gh/DNSZLSK/muad-dib)
833
833
  - **56 fuzz tests** - Malformed YAML, invalid JSON, binary files, ReDoS, unicode, 10MB inputs
834
834
  - **Datadog 17K benchmark** - 17,922 real malware samples, 88.2% raw TPR, ~100% on JS/Node.js malware (2,077 out-of-scope misses: phishing, binaries, corrected libs)
835
835
  - **120 adversarial/holdout samples** - 53 adversarial + 40 holdout (75 available on disk), 73/75 detection rate (97.3% ADR). 2 misses: `require-cache-poison` (P3 trade-off), `getter-defineProperty-exfil`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "muaddib-scanner",
3
- "version": "2.6.0",
3
+ "version": "2.6.2",
4
4
  "description": "Supply-chain threat detection & response for npm & PyPI/Python",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -55,7 +55,7 @@
55
55
  },
56
56
  "devDependencies": {
57
57
  "@eslint/js": "10.0.1",
58
- "eslint": "10.0.2",
58
+ "eslint": "10.0.3",
59
59
  "eslint-plugin-security": "^4.0.0",
60
60
  "globals": "17.4.0"
61
61
  }
package/src/index.js CHANGED
@@ -23,7 +23,7 @@ const { ensureIOCs } = require('./ioc/bootstrap.js');
23
23
  const { scanEntropy } = require('./scanner/entropy.js');
24
24
  const { scanAIConfig } = require('./scanner/ai-config.js');
25
25
  const { deobfuscate } = require('./scanner/deobfuscate.js');
26
- const { buildModuleGraph, annotateTaintedExports, detectCrossFileFlows, annotateSinkExports, detectCallbackCrossFileFlows } = require('./scanner/module-graph.js');
26
+ const { buildModuleGraph, annotateTaintedExports, detectCrossFileFlows, annotateSinkExports, detectCallbackCrossFileFlows, detectEventEmitterFlows } = require('./scanner/module-graph.js');
27
27
  const { computeReachableFiles } = require('./scanner/reachability.js');
28
28
  const { runTemporalAnalyses } = require('./temporal-runner.js');
29
29
  const { formatOutput } = require('./output-formatter.js');
@@ -357,16 +357,27 @@ async function run(targetPath, options = {}) {
357
357
 
358
358
  // Cross-file module graph analysis (before individual scanners)
359
359
  // Wrapped in yieldThen to unblock spinner animation
360
+ // Bounded: 5s timeout to prevent DoS on large/adversarial packages
361
+ const MODULE_GRAPH_TIMEOUT_MS = 5000;
360
362
  let crossFileFlows = [];
361
363
  if (!options.noModuleGraph) {
362
- try {
364
+ const moduleGraphWork = async () => {
363
365
  const graph = await yieldThen(() => buildModuleGraph(targetPath));
364
366
  const tainted = await yieldThen(() => annotateTaintedExports(graph, targetPath));
365
- crossFileFlows = await yieldThen(() => detectCrossFileFlows(graph, tainted, targetPath));
366
- // Callback-based cross-file flow detection
367
367
  const sinkAnnotations = await yieldThen(() => annotateSinkExports(graph, targetPath));
368
+ crossFileFlows = await yieldThen(() => detectCrossFileFlows(graph, tainted, sinkAnnotations, targetPath));
369
+ // Callback-based cross-file flow detection
368
370
  const callbackFlows = await yieldThen(() => detectCallbackCrossFileFlows(graph, tainted, sinkAnnotations, targetPath));
369
371
  crossFileFlows = crossFileFlows.concat(callbackFlows);
372
+ // EventEmitter cross-module flow detection
373
+ const emitterFlows = await yieldThen(() => detectEventEmitterFlows(graph, tainted, sinkAnnotations, targetPath));
374
+ crossFileFlows = crossFileFlows.concat(emitterFlows);
375
+ };
376
+ const timeout = new Promise((_, reject) =>
377
+ setTimeout(() => reject(new Error('Module graph timeout')), MODULE_GRAPH_TIMEOUT_MS)
378
+ );
379
+ try {
380
+ await Promise.race([moduleGraphWork(), timeout]);
370
381
  } catch (e) {
371
382
  // Graceful fallback — module graph is best-effort
372
383
  debugLog('[MODULE-GRAPH] Error:', e && e.message);