muaddib-scanner 2.10.30 → 2.10.31

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
@@ -30,7 +30,7 @@
30
30
 
31
31
  npm and PyPI supply-chain attacks are exploding. Shai-Hulud compromised 25K+ repos in 2025. Existing tools detect threats but don't help you respond.
32
32
 
33
- MUAD'DIB combines **14 parallel scanners** (176 detection rules), a **deobfuscation engine**, **inter-module dataflow analysis**, **compound scoring**, **ML classifiers** (XGBoost), and Docker sandbox to detect known threats and suspicious behavioral patterns in npm and PyPI packages.
33
+ MUAD'DIB combines **14 parallel scanners** (195 detection rules), a **deobfuscation engine**, **inter-module dataflow analysis**, **compound scoring**, **ML classifiers** (XGBoost), and Docker sandbox to detect known threats and suspicious behavioral patterns in npm and PyPI packages.
34
34
 
35
35
  ---
36
36
 
@@ -195,7 +195,7 @@ muaddib replay # Ground truth validation (46/49 TPR)
195
195
  | GitHub Actions | Shai-Hulud backdoor detection |
196
196
  | Hash Scanner | Known malicious file hashes |
197
197
 
198
- ### 176 detection rules
198
+ ### 195 detection rules
199
199
 
200
200
  All rules are mapped to MITRE ATT&CK techniques. See [SECURITY.md](SECURITY.md#detection-rules-v21021) for the complete rules reference.
201
201
 
@@ -271,7 +271,7 @@ With pre-commit framework:
271
271
  ```yaml
272
272
  repos:
273
273
  - repo: https://github.com/DNSZLSK/muad-dib
274
- rev: v2.10.21
274
+ rev: v2.10.31
275
275
  hooks:
276
276
  - id: muaddib-scan
277
277
  ```
@@ -288,7 +288,7 @@ repos:
288
288
  | **FPR** (Benign random) | **7.5%** (15/200) | 200 random npm packages, stratified sampling |
289
289
  | **ADR** (Adversarial + Holdout) | **94.0%** (101/107) | 67 adversarial + 40 holdout (107 available on disk), global threshold=20 |
290
290
 
291
- **2793 tests** across 57 files. **176 rules** (171 RULES + 5 PARANOID).
291
+ **2868 tests** across 62 files. **195 rules** (190 RULES + 5 PARANOID).
292
292
 
293
293
  > **Methodology caveats:**
294
294
  > - TPR measured on 49 Node.js attack samples (3 browser-only excluded from 51 total)
@@ -329,7 +329,7 @@ npm test
329
329
 
330
330
  ### Testing
331
331
 
332
- - **2793 tests** across 57 modular test files
332
+ - **2868 tests** across 62 modular test files
333
333
  - **56 fuzz tests** - Malformed inputs, ReDoS, unicode, binary
334
334
  - **Datadog 17K benchmark** - 14,587 confirmed malware samples (in-scope)
335
335
  - **Ground truth validation** - 51 real-world attacks (93.9% TPR)
@@ -351,7 +351,7 @@ npm test
351
351
  - [Evaluation Methodology](docs/EVALUATION_METHODOLOGY.md) - Experimental protocol, holdout scores
352
352
  - [Threat Model](docs/threat-model.md) - What MUAD'DIB detects and doesn't detect
353
353
  - [Adversarial Evaluation](ADVERSARIAL.md) - Red team samples and ADR results
354
- - [Security Policy](SECURITY.md) - Detection rules reference (176 rules)
354
+ - [Security Policy](SECURITY.md) - Detection rules reference (195 rules)
355
355
  - [Security Audit](docs/SECURITY_AUDIT.md) - Bypass validation report
356
356
  - [FP Analysis](docs/EVALUATION.md) - Historical false positive analysis
357
357
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "muaddib-scanner",
3
- "version": "2.10.30",
3
+ "version": "2.10.31",
4
4
  "description": "Supply-chain threat detection & response for npm & PyPI/Python",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/sbom.json ADDED
Binary file
@@ -511,6 +511,13 @@ const PLAYBOOKS = {
511
511
  'CRITIQUE: Un Proxy JavaScript avec trap set/get/apply est combine avec un appel reseau. ' +
512
512
  'Technique d\'interception: le Proxy capture toutes les ecritures de proprietes (credentials, tokens, config) ' +
513
513
  'et les exfiltre via HTTPS/fetch/dgram. Supprimer le package. Auditer tous les modules qui importent ce package.',
514
+ proxy_globalthis_intercept:
515
+ 'CRITIQUE: new Proxy(globalThis/global) intercepte tous les acces au scope global. ' +
516
+ 'L\'attaquant peut hooker eval, Function, require de maniere transparente via le handler Proxy. ' +
517
+ 'Supprimer le package immediatement.',
518
+ reflect_bind_code_execution:
519
+ 'CRITIQUE: Reflect.apply() avec methode prototype (bind/call/apply) et thisArg=Function/eval. ' +
520
+ 'Evasion de 2nd niveau contournant la detection Reflect.apply(eval). Supprimer le package.',
514
521
  detached_credential_exfil:
515
522
  'CRITIQUE: Process detache avec acces aux credentials et exfiltration reseau. ' +
516
523
  'Technique DPRK/Lazarus: le process fils survit au parent (detached:true, unref()) et exfiltre des secrets en arriere-plan. ' +
@@ -2142,6 +2142,24 @@ const RULES = {
2142
2142
  ],
2143
2143
  mitre: 'T1082'
2144
2144
  },
2145
+ proxy_globalthis_intercept: {
2146
+ id: 'MUADDIB-AST-083',
2147
+ name: 'Proxy GlobalThis Interception',
2148
+ severity: 'CRITICAL',
2149
+ confidence: 'high',
2150
+ description: 'new Proxy(globalThis/global/window/self) — intercepts all global scope access, enabling transparent hooking of eval/Function/require.',
2151
+ references: ['https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy'],
2152
+ mitre: 'T1574'
2153
+ },
2154
+ reflect_bind_code_execution: {
2155
+ id: 'MUADDIB-AST-084',
2156
+ name: 'Reflect.apply Prototype Method Code Execution',
2157
+ severity: 'CRITICAL',
2158
+ confidence: 'high',
2159
+ description: 'Reflect.apply(Function.prototype.bind/call/apply, Function, [...]) — indirect code execution via Reflect with prototype method as target.',
2160
+ references: ['https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/apply'],
2161
+ mitre: 'T1059'
2162
+ },
2145
2163
  lifecycle_missing_script: {
2146
2164
  id: 'MUADDIB-PKG-017',
2147
2165
  name: 'Phantom Lifecycle Script',
@@ -1496,6 +1496,26 @@ function handleCallExpression(node, ctx) {
1496
1496
  file: ctx.relFile
1497
1497
  });
1498
1498
  }
1499
+ // Bypass fix: Reflect.apply(Function.prototype.bind/call/apply, Function, [...])
1500
+ if (target.type === 'MemberExpression') {
1501
+ const methodProp = target.property;
1502
+ const methodName = methodProp?.type === 'Identifier' ? methodProp.name :
1503
+ (methodProp?.type === 'Literal' ? String(methodProp.value) : null);
1504
+ if (methodName === 'bind' || methodName === 'call' || methodName === 'apply') {
1505
+ const thisArg = node.arguments[1];
1506
+ if (thisArg?.type === 'Identifier' &&
1507
+ (thisArg.name === 'Function' || thisArg.name === 'eval' ||
1508
+ ctx.evalAliases?.has(thisArg.name))) {
1509
+ ctx.hasDynamicExec = true;
1510
+ ctx.threats.push({
1511
+ type: 'reflect_bind_code_execution',
1512
+ severity: 'CRITICAL',
1513
+ message: `Reflect.apply(*.${methodName}, ${thisArg.name}, [...]) — indirect ${thisArg.name} invocation via prototype method, bypasses Reflect.apply(${thisArg.name}) detection.`,
1514
+ file: ctx.relFile
1515
+ });
1516
+ }
1517
+ }
1518
+ }
1499
1519
  // B1: Reflect.apply(require, null, ['child_process']) — bypasses require() call detection
1500
1520
  if (target.type === 'Identifier' && target.name === 'require') {
1501
1521
  const argsArray = node.arguments[2];
@@ -58,6 +58,18 @@ function handleNewExpression(node, ctx) {
58
58
  file: ctx.relFile
59
59
  });
60
60
  }
61
+ // Detect new Proxy(globalThis/global/window/self, handler) — intercepts all global access
62
+ if (target.type === 'Identifier' &&
63
+ (target.name === 'globalThis' || target.name === 'global' ||
64
+ target.name === 'window' || target.name === 'self' ||
65
+ ctx.globalThisAliases.has(target.name))) {
66
+ ctx.threats.push({
67
+ type: 'proxy_globalthis_intercept',
68
+ severity: 'CRITICAL',
69
+ message: `new Proxy(${target.name}, handler) — intercepts all global object access. Attacker can hook eval/Function/require transparently.`,
70
+ file: ctx.relFile
71
+ });
72
+ }
61
73
  // Detect new Proxy(obj, handler) where handler has set/get traps — data interception
62
74
  // Real-world technique: export a Proxy that intercepts all property sets/gets to exfiltrate
63
75
  // data flowing through the module. Combined with network (hasNetworkInFile) → credential theft.
@@ -58,6 +58,19 @@ function handleVariableDeclarator(node, ctx) {
58
58
  ctx.globalThisAliases.add(node.id.name);
59
59
  }
60
60
 
61
+ // Track Proxy(globalThis) as globalThis alias for downstream detection
62
+ if (node.init?.type === 'NewExpression' &&
63
+ node.init.callee?.type === 'Identifier' && node.init.callee.name === 'Proxy' &&
64
+ node.init.arguments?.length >= 2) {
65
+ const proxyTarget = node.init.arguments[0];
66
+ if (proxyTarget?.type === 'Identifier' &&
67
+ (proxyTarget.name === 'globalThis' || proxyTarget.name === 'global' ||
68
+ proxyTarget.name === 'window' || proxyTarget.name === 'self' ||
69
+ ctx.globalThisAliases.has(proxyTarget.name))) {
70
+ ctx.globalThisAliases.add(node.id.name);
71
+ }
72
+ }
73
+
61
74
  // B1: const E = eval; const F = Function;
62
75
  if (node.init?.type === 'Identifier' &&
63
76
  (node.init.name === 'eval' || node.init.name === 'Function')) {