muaddib-scanner 2.10.21 → 2.10.23
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 +11 -11
- package/bin/muaddib.js +365 -39
- package/package.json +1 -1
- package/scripts/benchmark.js +326 -0
- package/src/index.js +34 -3
- package/src/ioc/scraper.js +4 -141
- package/src/ioc/updater.js +26 -7
- package/src/response/playbooks.js +112 -0
- package/src/rules/index.js +324 -1
- package/src/scanner/ast-detectors.js +744 -12
- package/src/scanner/ast.js +62 -1
- package/src/scanner/package.js +70 -0
- package/src/scanner/shell.js +5 -5
- package/src/scoring.js +76 -6
- package/src/shared/constants.js +5 -5
- package/src/utils.js +8 -0
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** (
|
|
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.
|
|
34
34
|
|
|
35
35
|
---
|
|
36
36
|
|
|
@@ -136,7 +136,7 @@ Ultra-strict detection with lower tolerance. Detects any network access, subproc
|
|
|
136
136
|
muaddib scan . --webhook "https://discord.com/api/webhooks/..."
|
|
137
137
|
```
|
|
138
138
|
|
|
139
|
-
Strict filtering (v2.1.2): alerts only for IOC matches, sandbox-confirmed threats, or canary token exfiltration. Priority triage (v2.10.
|
|
139
|
+
Strict filtering (v2.1.2): alerts only for IOC matches, sandbox-confirmed threats, or canary token exfiltration. Priority triage (v2.10.21): P1 (red, IOC/sandbox/canary), P2 (orange, high-score/compounds), P3 (yellow, rest).
|
|
140
140
|
|
|
141
141
|
### Behavioral anomaly detection (v2.0)
|
|
142
142
|
|
|
@@ -195,9 +195,9 @@ 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
|
-
###
|
|
198
|
+
### 176 detection rules
|
|
199
199
|
|
|
200
|
-
All rules are mapped to MITRE ATT&CK techniques. See [SECURITY.md](SECURITY.md#detection-rules-
|
|
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
|
|
|
202
202
|
### Detected campaigns
|
|
203
203
|
|
|
@@ -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.
|
|
274
|
+
rev: v2.10.21
|
|
275
275
|
hooks:
|
|
276
276
|
- id: muaddib-scan
|
|
277
277
|
```
|
|
@@ -284,11 +284,11 @@ repos:
|
|
|
284
284
|
|--------|--------|---------|
|
|
285
285
|
| **Wild TPR** (Datadog 17K) | **92.8%** (13,538/14,587 in-scope) | 17,922 packages. 3,335 skipped (no JS). By category: compromised_lib 97.8%, malicious_intent 92.1% |
|
|
286
286
|
| **TPR** (Ground Truth) | **93.9%** (46/49) | 51 real attacks. 3 out-of-scope: browser-only |
|
|
287
|
-
| **FPR** (Benign curated) | **
|
|
287
|
+
| **FPR** (Benign curated) | **10.6%** (56/529) | 529 npm packages, real source via `npm pack` |
|
|
288
288
|
| **FPR** (Benign random) | **7.5%** (15/200) | 200 random npm packages, stratified sampling |
|
|
289
|
-
| **ADR** (Adversarial + Holdout) | **
|
|
289
|
+
| **ADR** (Adversarial + Holdout) | **94.0%** (101/107) | 67 adversarial + 40 holdout (107 available on disk), global threshold=20 |
|
|
290
290
|
|
|
291
|
-
**
|
|
291
|
+
**2793 tests** across 57 files. **176 rules** (171 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,11 +329,11 @@ npm test
|
|
|
329
329
|
|
|
330
330
|
### Testing
|
|
331
331
|
|
|
332
|
-
- **
|
|
332
|
+
- **2793 tests** across 57 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)
|
|
336
|
-
- **False positive validation** -
|
|
336
|
+
- **False positive validation** - 10.6% FPR on 529 curated npm packages, 7.5% on 200 random
|
|
337
337
|
|
|
338
338
|
---
|
|
339
339
|
|
|
@@ -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 (
|
|
354
|
+
- [Security Policy](SECURITY.md) - Detection rules reference (176 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/bin/muaddib.js
CHANGED
|
@@ -1,4 +1,26 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Auto-respawn with memory flags for evaluate command (OOM prevention)
|
|
4
|
+
if (process.argv[2] === 'evaluate') {
|
|
5
|
+
const hasMaxOld = process.execArgv.some(a => a.includes('--max-old-space-size'));
|
|
6
|
+
const hasGC = process.execArgv.some(a => a === '--expose-gc');
|
|
7
|
+
if (!hasMaxOld || !hasGC) {
|
|
8
|
+
const { execFileSync } = require('child_process');
|
|
9
|
+
const flags = [];
|
|
10
|
+
if (!hasMaxOld) flags.push('--max-old-space-size=8192');
|
|
11
|
+
if (!hasGC) flags.push('--expose-gc');
|
|
12
|
+
try {
|
|
13
|
+
execFileSync(process.execPath, [...flags, __filename, ...process.argv.slice(2)], {
|
|
14
|
+
stdio: 'inherit',
|
|
15
|
+
env: process.env
|
|
16
|
+
});
|
|
17
|
+
process.exit(0);
|
|
18
|
+
} catch (e) {
|
|
19
|
+
process.exit(e.status || 1);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
2
24
|
const { execFile } = require('child_process');
|
|
3
25
|
const { run } = require('../src/index.js');
|
|
4
26
|
const { updateIOCs } = require('../src/ioc/updater.js');
|
|
@@ -13,6 +35,9 @@ const args = process.argv.slice(2);
|
|
|
13
35
|
const command = args[0];
|
|
14
36
|
const options = args.slice(1);
|
|
15
37
|
|
|
38
|
+
// Helper: detect --help / -h in options
|
|
39
|
+
const wantHelp = options.includes('--help') || options.includes('-h');
|
|
40
|
+
|
|
16
41
|
// Parse options
|
|
17
42
|
let target = '.';
|
|
18
43
|
let jsonOutput = false;
|
|
@@ -394,19 +419,45 @@ const helpText = `
|
|
|
394
419
|
muaddib diff <ref> [path] Compare threats with a previous version
|
|
395
420
|
muaddib install <pkg> [options] Safe install (scan before install)
|
|
396
421
|
muaddib watch [path] Watch in real-time
|
|
422
|
+
muaddib sandbox <pkg> [options] Analyze in isolated Docker container
|
|
423
|
+
muaddib sandbox-report <pkg> Sandbox + detailed network report
|
|
424
|
+
muaddib evaluate [options] Run TPR/FPR/ADR evaluation suite
|
|
425
|
+
muaddib monitor [options] Start real-time npm/PyPI monitor
|
|
397
426
|
muaddib daemon [options] Start daemon
|
|
398
427
|
muaddib init-hooks [options] Setup git pre-commit hooks
|
|
399
428
|
muaddib remove-hooks [path] Remove MUAD'DIB git hooks
|
|
400
429
|
muaddib update Update IOCs
|
|
401
430
|
muaddib scrape Scrape new IOCs
|
|
402
|
-
muaddib
|
|
403
|
-
muaddib
|
|
431
|
+
muaddib feed [options] Threat feed (JSON)
|
|
432
|
+
muaddib serve [options] Threat feed HTTP server
|
|
433
|
+
muaddib detections [options] View monitor detections
|
|
434
|
+
muaddib stats [options] View scan statistics
|
|
435
|
+
muaddib replay [options] Replay ground-truth attacks
|
|
404
436
|
muaddib version Show version
|
|
405
437
|
|
|
406
|
-
|
|
407
|
-
--
|
|
408
|
-
--
|
|
409
|
-
|
|
438
|
+
Scan Options:
|
|
439
|
+
--json JSON output
|
|
440
|
+
--html [file] HTML report (default: muaddib-report.html)
|
|
441
|
+
--sarif [file] SARIF report (default: muaddib-results.sarif)
|
|
442
|
+
--explain Detailed explanations with MITRE references
|
|
443
|
+
--breakdown Show score breakdown by threat
|
|
444
|
+
--fail-on [level] Exit code threshold (critical|high|medium|low, default: high)
|
|
445
|
+
--webhook [url] Discord/Slack webhook (HTTPS only)
|
|
446
|
+
--paranoid Ultra-strict detection mode
|
|
447
|
+
--exclude [dir] Exclude directory from scan (repeatable)
|
|
448
|
+
--config [file] Custom config file (.muaddibrc.json format)
|
|
449
|
+
--entropy-threshold [n] Custom entropy threshold (0-8, default: 5.5)
|
|
450
|
+
--no-deobfuscate Disable deobfuscation pre-processing
|
|
451
|
+
--no-module-graph Disable cross-file dataflow analysis
|
|
452
|
+
--no-reachability Disable entry-point reachability analysis
|
|
453
|
+
--auto-sandbox Auto-trigger sandbox when static scan score >= 20 (requires Docker)
|
|
454
|
+
|
|
455
|
+
Temporal Options (scan):
|
|
456
|
+
--temporal Detect sudden lifecycle script changes
|
|
457
|
+
--temporal-ast Detect sudden dangerous API additions via AST diff
|
|
458
|
+
--temporal-publish Detect publish frequency anomalies
|
|
459
|
+
--temporal-maintainer Detect maintainer changes
|
|
460
|
+
--temporal-full All temporal analyses combined
|
|
410
461
|
|
|
411
462
|
Diff Examples:
|
|
412
463
|
muaddib diff HEAD~1 Compare with previous commit
|
|
@@ -414,40 +465,303 @@ const helpText = `
|
|
|
414
465
|
muaddib diff main Compare with branch
|
|
415
466
|
muaddib diff abc1234 ./myproject Compare specific commit
|
|
416
467
|
|
|
468
|
+
Install Options:
|
|
469
|
+
--save-dev, -D Install as dev dependency
|
|
470
|
+
-g, --global Install globally
|
|
471
|
+
--force Force install despite threats
|
|
472
|
+
|
|
473
|
+
Sandbox Options:
|
|
474
|
+
--local Analyze a local path instead of npm package
|
|
475
|
+
--strict Block non-essential network access
|
|
476
|
+
--no-canary Disable honey token injection
|
|
477
|
+
|
|
417
478
|
Init-hooks Options:
|
|
418
479
|
--type [auto|husky|pre-commit|git] Hook system (default: auto)
|
|
419
480
|
--mode [scan|diff] scan=all threats, diff=new only
|
|
420
481
|
|
|
482
|
+
Evaluate Options:
|
|
483
|
+
--json JSON output
|
|
484
|
+
--benign-limit [n] Limit benign packages to scan
|
|
485
|
+
--refresh-benign Re-download benign packages
|
|
486
|
+
|
|
487
|
+
Feed/Serve Options:
|
|
488
|
+
--limit [n] Limit feed entries (default: 50)
|
|
489
|
+
--severity [level] Filter by severity (CRITICAL|HIGH|MEDIUM|LOW)
|
|
490
|
+
--since [date] Filter detections after date (ISO 8601)
|
|
491
|
+
--port [n] HTTP server port (default: 3000)
|
|
492
|
+
|
|
493
|
+
Monitor Options:
|
|
494
|
+
--verbose Show detailed output
|
|
495
|
+
--temporal --test <pkg> Test temporal analysis on a single package
|
|
496
|
+
--temporal-ast --test <pkg> Test AST diff on a single package
|
|
497
|
+
|
|
498
|
+
Detections Options:
|
|
499
|
+
--json JSON output
|
|
500
|
+
--stats Show aggregated detection statistics
|
|
501
|
+
|
|
502
|
+
Stats Options:
|
|
503
|
+
--json JSON output
|
|
504
|
+
--daily Show daily breakdown (last 7 days)
|
|
505
|
+
|
|
506
|
+
Replay Options:
|
|
507
|
+
--verbose Show detailed findings per attack
|
|
508
|
+
--json Machine-readable JSON output
|
|
509
|
+
GT-NNN Replay single attack by ID
|
|
510
|
+
`;
|
|
511
|
+
|
|
512
|
+
// Per-command help texts for subcommand --help
|
|
513
|
+
const commandHelp = {
|
|
514
|
+
scan: `
|
|
515
|
+
Usage: muaddib scan [path] [options]
|
|
516
|
+
|
|
517
|
+
Scan a project for supply-chain threats.
|
|
518
|
+
|
|
519
|
+
Arguments:
|
|
520
|
+
path Target directory to scan (default: .)
|
|
521
|
+
|
|
421
522
|
Options:
|
|
422
523
|
--json JSON output
|
|
423
|
-
--html [file] HTML report
|
|
424
|
-
--sarif [file] SARIF report (
|
|
425
|
-
--explain Detailed explanations
|
|
524
|
+
--html [file] HTML report (default: muaddib-report.html)
|
|
525
|
+
--sarif [file] SARIF report (default: muaddib-results.sarif)
|
|
526
|
+
--explain Detailed explanations with MITRE references
|
|
426
527
|
--breakdown Show score breakdown by threat
|
|
427
|
-
--fail-on [level]
|
|
428
|
-
--webhook [url] Discord/Slack webhook
|
|
429
|
-
--paranoid Ultra-strict mode
|
|
430
|
-
--
|
|
431
|
-
--
|
|
432
|
-
--
|
|
433
|
-
--temporal-maintainer Detect maintainer changes (new maintainer, account takeover)
|
|
434
|
-
--temporal-full All temporal analyses (lifecycle + AST + publish + maintainer)
|
|
435
|
-
--auto-sandbox Auto-trigger sandbox when static scan score >= 20 (requires Docker)
|
|
436
|
-
--no-canary Disable honey token injection in sandbox
|
|
528
|
+
--fail-on [level] Exit code threshold (critical|high|medium|low, default: high)
|
|
529
|
+
--webhook [url] Discord/Slack webhook (HTTPS only)
|
|
530
|
+
--paranoid Ultra-strict detection mode
|
|
531
|
+
--exclude [dir] Exclude directory from scan (repeatable)
|
|
532
|
+
--config [file] Custom config file (.muaddibrc.json format)
|
|
533
|
+
--entropy-threshold [n] Custom entropy threshold (0-8, default: 5.5)
|
|
437
534
|
--no-deobfuscate Disable deobfuscation pre-processing
|
|
438
535
|
--no-module-graph Disable cross-file dataflow analysis
|
|
439
536
|
--no-reachability Disable entry-point reachability analysis
|
|
440
|
-
--
|
|
441
|
-
--
|
|
442
|
-
--
|
|
443
|
-
--
|
|
444
|
-
--
|
|
445
|
-
--
|
|
446
|
-
|
|
537
|
+
--auto-sandbox Auto-trigger sandbox when static scan score >= 20 (requires Docker)
|
|
538
|
+
--temporal Detect sudden lifecycle script changes
|
|
539
|
+
--temporal-ast Detect sudden dangerous API additions
|
|
540
|
+
--temporal-publish Detect publish frequency anomalies
|
|
541
|
+
--temporal-maintainer Detect maintainer changes
|
|
542
|
+
--temporal-full All temporal analyses combined
|
|
543
|
+
|
|
544
|
+
Examples:
|
|
545
|
+
muaddib scan .
|
|
546
|
+
muaddib scan ./my-project --explain --paranoid
|
|
547
|
+
muaddib scan . --json > results.json
|
|
548
|
+
muaddib scan . --html report.html --explain
|
|
549
|
+
`,
|
|
550
|
+
diff: `
|
|
551
|
+
Usage: muaddib diff <ref> [path] [options]
|
|
552
|
+
|
|
553
|
+
Compare threats between two versions of a project.
|
|
554
|
+
Without <ref>, shows available tags and recent commits.
|
|
555
|
+
|
|
556
|
+
Arguments:
|
|
557
|
+
ref Commit hash, tag, or branch to compare with
|
|
558
|
+
path Target directory (default: .)
|
|
559
|
+
|
|
560
|
+
Options:
|
|
561
|
+
--json JSON output
|
|
562
|
+
--explain Detailed explanations
|
|
563
|
+
--fail-on [level] Exit code threshold (critical|high|medium|low)
|
|
564
|
+
--paranoid Ultra-strict detection mode
|
|
565
|
+
|
|
566
|
+
Examples:
|
|
567
|
+
muaddib diff HEAD~1
|
|
568
|
+
muaddib diff v1.2.0
|
|
569
|
+
muaddib diff main ./myproject
|
|
570
|
+
`,
|
|
571
|
+
install: `
|
|
572
|
+
Usage: muaddib install <package> [<package>...] [options]
|
|
573
|
+
|
|
574
|
+
Scan packages for threats before installing them.
|
|
575
|
+
|
|
576
|
+
Options:
|
|
447
577
|
--save-dev, -D Install as dev dependency
|
|
448
578
|
-g, --global Install globally
|
|
449
|
-
--force Force install despite threats
|
|
450
|
-
|
|
579
|
+
--force Force install despite detected threats
|
|
580
|
+
|
|
581
|
+
Examples:
|
|
582
|
+
muaddib install lodash
|
|
583
|
+
muaddib install express morgan --save-dev
|
|
584
|
+
muaddib install -g typescript
|
|
585
|
+
`,
|
|
586
|
+
sandbox: `
|
|
587
|
+
Usage: muaddib sandbox <package-name|path> [options]
|
|
588
|
+
|
|
589
|
+
Run dynamic analysis in an isolated Docker container.
|
|
590
|
+
Requires Docker to be installed and running.
|
|
591
|
+
|
|
592
|
+
Options:
|
|
593
|
+
--local Analyze a local path instead of npm package
|
|
594
|
+
--strict Block non-essential network access
|
|
595
|
+
--no-canary Disable honey token injection
|
|
596
|
+
|
|
597
|
+
Examples:
|
|
598
|
+
muaddib sandbox suspicious-pkg
|
|
599
|
+
muaddib sandbox ./local-pkg --local --strict
|
|
600
|
+
`,
|
|
601
|
+
'sandbox-report': `
|
|
602
|
+
Usage: muaddib sandbox-report <package-name|path> [options]
|
|
603
|
+
|
|
604
|
+
Run sandbox analysis with detailed network traffic report.
|
|
605
|
+
Same options as 'muaddib sandbox'.
|
|
606
|
+
`,
|
|
607
|
+
evaluate: `
|
|
608
|
+
Usage: muaddib evaluate [options]
|
|
609
|
+
|
|
610
|
+
Run the full TPR/FPR/ADR evaluation suite against ground truth,
|
|
611
|
+
benign packages, and adversarial samples.
|
|
612
|
+
|
|
613
|
+
Options:
|
|
614
|
+
--json JSON output
|
|
615
|
+
--benign-limit [n] Limit benign packages to scan
|
|
616
|
+
--refresh-benign Re-download benign packages
|
|
617
|
+
|
|
618
|
+
Examples:
|
|
619
|
+
muaddib evaluate
|
|
620
|
+
muaddib evaluate --json > eval-results.json
|
|
621
|
+
`,
|
|
622
|
+
monitor: `
|
|
623
|
+
Usage: muaddib monitor [options]
|
|
624
|
+
|
|
625
|
+
Start real-time monitoring of npm/PyPI registries for
|
|
626
|
+
newly published malicious packages.
|
|
627
|
+
|
|
628
|
+
Options:
|
|
629
|
+
--verbose Show detailed output
|
|
630
|
+
--temporal --test <pkg> Test temporal analysis on a single package
|
|
631
|
+
--temporal-ast --test <pkg> Test AST diff on a single package
|
|
632
|
+
|
|
633
|
+
Examples:
|
|
634
|
+
muaddib monitor
|
|
635
|
+
muaddib monitor --verbose
|
|
636
|
+
muaddib monitor --temporal --test lodash
|
|
637
|
+
`,
|
|
638
|
+
feed: `
|
|
639
|
+
Usage: muaddib feed [options]
|
|
640
|
+
|
|
641
|
+
Output the threat detection feed as JSON.
|
|
642
|
+
|
|
643
|
+
Options:
|
|
644
|
+
--limit [n] Limit entries (default: 50)
|
|
645
|
+
--severity [level] Filter by severity (CRITICAL|HIGH|MEDIUM|LOW)
|
|
646
|
+
--since [date] Filter after date (ISO 8601)
|
|
647
|
+
|
|
648
|
+
Examples:
|
|
649
|
+
muaddib feed
|
|
650
|
+
muaddib feed --severity CRITICAL --limit 10
|
|
651
|
+
`,
|
|
652
|
+
serve: `
|
|
653
|
+
Usage: muaddib serve [options]
|
|
654
|
+
|
|
655
|
+
Start an HTTP server serving the threat feed.
|
|
656
|
+
|
|
657
|
+
Options:
|
|
658
|
+
--port [n] Server port (default: 3000)
|
|
659
|
+
|
|
660
|
+
Examples:
|
|
661
|
+
muaddib serve
|
|
662
|
+
muaddib serve --port 8080
|
|
663
|
+
`,
|
|
664
|
+
detections: `
|
|
665
|
+
Usage: muaddib detections [options]
|
|
666
|
+
|
|
667
|
+
View detections recorded by the monitor.
|
|
668
|
+
|
|
669
|
+
Options:
|
|
670
|
+
--json Full JSON output
|
|
671
|
+
--stats Show aggregated detection statistics
|
|
672
|
+
|
|
673
|
+
Examples:
|
|
674
|
+
muaddib detections
|
|
675
|
+
muaddib detections --stats
|
|
676
|
+
muaddib detections --json > detections.json
|
|
677
|
+
`,
|
|
678
|
+
stats: `
|
|
679
|
+
Usage: muaddib stats [options]
|
|
680
|
+
|
|
681
|
+
View scan statistics from the monitor.
|
|
682
|
+
|
|
683
|
+
Options:
|
|
684
|
+
--json JSON output
|
|
685
|
+
--daily Show daily breakdown (last 7 days)
|
|
686
|
+
|
|
687
|
+
Examples:
|
|
688
|
+
muaddib stats
|
|
689
|
+
muaddib stats --daily
|
|
690
|
+
`,
|
|
691
|
+
replay: `
|
|
692
|
+
Usage: muaddib replay [options] [GT-NNN]
|
|
693
|
+
|
|
694
|
+
Replay ground-truth attack samples to verify detection.
|
|
695
|
+
|
|
696
|
+
Options:
|
|
697
|
+
--verbose, -v Show detailed findings per attack
|
|
698
|
+
--json Machine-readable JSON output
|
|
699
|
+
GT-NNN Replay a single attack by ID
|
|
700
|
+
|
|
701
|
+
Examples:
|
|
702
|
+
muaddib replay
|
|
703
|
+
muaddib replay --verbose
|
|
704
|
+
muaddib replay GT-001
|
|
705
|
+
`,
|
|
706
|
+
'init-hooks': `
|
|
707
|
+
Usage: muaddib init-hooks [options]
|
|
708
|
+
|
|
709
|
+
Setup git pre-commit hooks for automatic scanning.
|
|
710
|
+
|
|
711
|
+
Options:
|
|
712
|
+
--type [auto|husky|pre-commit|git] Hook system (default: auto)
|
|
713
|
+
--mode [scan|diff] scan=all threats, diff=new only
|
|
714
|
+
|
|
715
|
+
Examples:
|
|
716
|
+
muaddib init-hooks
|
|
717
|
+
muaddib init-hooks --type husky --mode diff
|
|
718
|
+
`,
|
|
719
|
+
'remove-hooks': `
|
|
720
|
+
Usage: muaddib remove-hooks [path]
|
|
721
|
+
|
|
722
|
+
Remove MUAD'DIB git hooks from a project.
|
|
723
|
+
`,
|
|
724
|
+
watch: `
|
|
725
|
+
Usage: muaddib watch [path]
|
|
726
|
+
|
|
727
|
+
Watch a project directory and re-scan on file changes.
|
|
728
|
+
|
|
729
|
+
Examples:
|
|
730
|
+
muaddib watch
|
|
731
|
+
muaddib watch ./my-project
|
|
732
|
+
`,
|
|
733
|
+
daemon: `
|
|
734
|
+
Usage: muaddib daemon [options]
|
|
735
|
+
|
|
736
|
+
Start the MUAD'DIB daemon for background monitoring.
|
|
737
|
+
|
|
738
|
+
Options:
|
|
739
|
+
--webhook [url] Discord/Slack webhook URL
|
|
740
|
+
|
|
741
|
+
Examples:
|
|
742
|
+
muaddib daemon
|
|
743
|
+
muaddib daemon --webhook https://discord.com/api/webhooks/...
|
|
744
|
+
`,
|
|
745
|
+
report: `
|
|
746
|
+
Usage: muaddib report --now | --status
|
|
747
|
+
|
|
748
|
+
Send or check status of daily monitor reports.
|
|
749
|
+
|
|
750
|
+
Options:
|
|
751
|
+
--now Send report immediately
|
|
752
|
+
--status Show report status
|
|
753
|
+
`,
|
|
754
|
+
};
|
|
755
|
+
|
|
756
|
+
// Show per-command help or global help
|
|
757
|
+
function showHelp(cmd) {
|
|
758
|
+
if (cmd && commandHelp[cmd]) {
|
|
759
|
+
console.log(commandHelp[cmd]);
|
|
760
|
+
} else {
|
|
761
|
+
console.log(helpText);
|
|
762
|
+
}
|
|
763
|
+
process.exit(0);
|
|
764
|
+
}
|
|
451
765
|
|
|
452
766
|
// Main
|
|
453
767
|
if (command === 'version' || command === '--version' || command === '-v') {
|
|
@@ -456,18 +770,14 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
456
770
|
process.exit(0);
|
|
457
771
|
} else if (!command || command === '--help' || command === '-h') {
|
|
458
772
|
if (command === '--help' || command === '-h') {
|
|
459
|
-
|
|
460
|
-
process.exit(0);
|
|
773
|
+
showHelp();
|
|
461
774
|
}
|
|
462
775
|
interactiveMenu().catch(err => {
|
|
463
776
|
console.error('[ERROR]', err.message);
|
|
464
777
|
process.exit(1);
|
|
465
778
|
});
|
|
466
779
|
} else if (command === 'scan') {
|
|
467
|
-
if (
|
|
468
|
-
console.log(helpText);
|
|
469
|
-
process.exit(0);
|
|
470
|
-
}
|
|
780
|
+
if (wantHelp) showHelp('scan');
|
|
471
781
|
run(target, {
|
|
472
782
|
json: jsonOutput,
|
|
473
783
|
html: htmlOutput,
|
|
@@ -495,6 +805,7 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
495
805
|
process.exit(1);
|
|
496
806
|
});
|
|
497
807
|
} else if (command === 'feed') {
|
|
808
|
+
if (wantHelp) showHelp('feed');
|
|
498
809
|
const { getFeed } = require('../src/threat-feed.js');
|
|
499
810
|
const feedOpts = {};
|
|
500
811
|
if (feedLimit) feedOpts.limit = feedLimit;
|
|
@@ -504,10 +815,12 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
504
815
|
console.log(JSON.stringify(result, null, 2));
|
|
505
816
|
process.exit(0);
|
|
506
817
|
} else if (command === 'serve') {
|
|
818
|
+
if (wantHelp) showHelp('serve');
|
|
507
819
|
const { startServer } = require('../src/serve.js');
|
|
508
820
|
startServer({ port: servePort || 3000 });
|
|
509
821
|
// Server runs indefinitely — no process.exit
|
|
510
822
|
} else if (command === 'watch') {
|
|
823
|
+
if (wantHelp) showHelp('watch');
|
|
511
824
|
watch(target);
|
|
512
825
|
} else if (command === 'update') {
|
|
513
826
|
updateIOCs().then(() => {
|
|
@@ -525,6 +838,7 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
525
838
|
process.exit(1);
|
|
526
839
|
});
|
|
527
840
|
} else if (command === 'monitor') {
|
|
841
|
+
if (wantHelp) showHelp('monitor');
|
|
528
842
|
const testPkg = options.filter(o => !o.startsWith('-'));
|
|
529
843
|
const isTemporal = options.includes('--temporal');
|
|
530
844
|
const isTemporalAst = options.includes('--temporal-ast');
|
|
@@ -614,9 +928,11 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
614
928
|
});
|
|
615
929
|
}
|
|
616
930
|
} else if (command === 'daemon') {
|
|
931
|
+
if (wantHelp) showHelp('daemon');
|
|
617
932
|
const { startDaemon } = require('../src/daemon.js');
|
|
618
933
|
startDaemon({ webhook: webhookUrl });
|
|
619
934
|
} else if (command === 'install' || command === 'i') {
|
|
935
|
+
if (wantHelp) showHelp('install');
|
|
620
936
|
const packages = options.filter(o => !o.startsWith('-'));
|
|
621
937
|
const isDev = options.includes('--save-dev') || options.includes('-D');
|
|
622
938
|
const isGlobal = options.includes('-g') || options.includes('--global');
|
|
@@ -637,13 +953,14 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
637
953
|
process.exit(1);
|
|
638
954
|
});
|
|
639
955
|
} else if (command === 'sandbox') {
|
|
956
|
+
if (wantHelp) showHelp('sandbox');
|
|
640
957
|
const sandboxOpts = options.filter(o => !o.startsWith('-'));
|
|
641
958
|
const packageName = sandboxOpts[0];
|
|
642
959
|
const strict = options.includes('--strict');
|
|
643
960
|
const canary = !options.includes('--no-canary');
|
|
644
961
|
const local = options.includes('--local');
|
|
645
962
|
if (!packageName) {
|
|
646
|
-
console.log('
|
|
963
|
+
console.log(commandHelp['sandbox']);
|
|
647
964
|
process.exit(1);
|
|
648
965
|
}
|
|
649
966
|
|
|
@@ -657,13 +974,14 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
657
974
|
process.exit(1);
|
|
658
975
|
});
|
|
659
976
|
} else if (command === 'sandbox-report') {
|
|
977
|
+
if (wantHelp) showHelp('sandbox-report');
|
|
660
978
|
const sandboxOpts = options.filter(o => !o.startsWith('-'));
|
|
661
979
|
const packageName = sandboxOpts[0];
|
|
662
980
|
const strict = options.includes('--strict');
|
|
663
981
|
const canary = !options.includes('--no-canary');
|
|
664
982
|
const local = options.includes('--local');
|
|
665
983
|
if (!packageName) {
|
|
666
|
-
console.log('
|
|
984
|
+
console.log(commandHelp['sandbox-report']);
|
|
667
985
|
process.exit(1);
|
|
668
986
|
}
|
|
669
987
|
|
|
@@ -680,6 +998,7 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
680
998
|
process.exit(1);
|
|
681
999
|
});
|
|
682
1000
|
} else if (command === 'diff') {
|
|
1001
|
+
if (wantHelp) showHelp('diff');
|
|
683
1002
|
// Parse diff arguments: muaddib diff <ref> [path] [options]
|
|
684
1003
|
const diffArgs = options.filter(o => !o.startsWith('-'));
|
|
685
1004
|
const baseRef = diffArgs[0];
|
|
@@ -702,6 +1021,7 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
702
1021
|
process.exit(1);
|
|
703
1022
|
});
|
|
704
1023
|
} else if (command === 'detections') {
|
|
1024
|
+
if (wantHelp) showHelp('detections');
|
|
705
1025
|
const { loadDetections, getDetectionStats } = require('../src/monitor.js');
|
|
706
1026
|
const wantStats = options.includes('--stats');
|
|
707
1027
|
const wantJson = options.includes('--json');
|
|
@@ -753,6 +1073,7 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
753
1073
|
console.log('');
|
|
754
1074
|
process.exit(0);
|
|
755
1075
|
} else if (command === 'stats') {
|
|
1076
|
+
if (wantHelp) showHelp('stats');
|
|
756
1077
|
const { loadScanStats } = require('../src/monitor.js');
|
|
757
1078
|
const wantDaily = options.includes('--daily');
|
|
758
1079
|
const wantJson = options.includes('--json');
|
|
@@ -796,6 +1117,7 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
796
1117
|
console.log('');
|
|
797
1118
|
process.exit(0);
|
|
798
1119
|
} else if (command === 'evaluate') {
|
|
1120
|
+
if (wantHelp) showHelp('evaluate');
|
|
799
1121
|
const { evaluate } = require('../src/commands/evaluate.js');
|
|
800
1122
|
const evalOpts = { json: jsonOutput };
|
|
801
1123
|
for (let i = 0; i < options.length; i++) {
|
|
@@ -813,6 +1135,7 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
813
1135
|
process.exit(1);
|
|
814
1136
|
});
|
|
815
1137
|
} else if (command === 'init-hooks') {
|
|
1138
|
+
if (wantHelp) showHelp('init-hooks');
|
|
816
1139
|
// Parse init-hooks arguments
|
|
817
1140
|
let hookType = 'auto';
|
|
818
1141
|
let hookMode = 'scan';
|
|
@@ -834,6 +1157,7 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
834
1157
|
process.exit(1);
|
|
835
1158
|
});
|
|
836
1159
|
} else if (command === 'remove-hooks') {
|
|
1160
|
+
if (wantHelp) showHelp('remove-hooks');
|
|
837
1161
|
removeHooks(target).then(success => {
|
|
838
1162
|
process.exit(success ? 0 : 1);
|
|
839
1163
|
}).catch(err => {
|
|
@@ -841,6 +1165,7 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
841
1165
|
process.exit(1);
|
|
842
1166
|
});
|
|
843
1167
|
} else if (command === 'replay' || command === 'ground-truth') {
|
|
1168
|
+
if (wantHelp) showHelp('replay');
|
|
844
1169
|
const { replay } = require('../tests/ground-truth/replay.js');
|
|
845
1170
|
const replayOpts = {};
|
|
846
1171
|
for (const o of options) {
|
|
@@ -858,7 +1183,7 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
858
1183
|
process.exit(1);
|
|
859
1184
|
});
|
|
860
1185
|
} else if (command === 'report') {
|
|
861
|
-
|
|
1186
|
+
if (wantHelp) showHelp('report');
|
|
862
1187
|
if (options.includes('--now')) {
|
|
863
1188
|
const { sendReportNow } = require('../src/monitor.js');
|
|
864
1189
|
sendReportNow().then(result => {
|
|
@@ -889,8 +1214,9 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
889
1214
|
process.exit(1);
|
|
890
1215
|
}
|
|
891
1216
|
} else if (command === 'help') {
|
|
892
|
-
|
|
893
|
-
|
|
1217
|
+
// muaddib help <command> — show per-command help
|
|
1218
|
+
const helpCmd = options.filter(o => !o.startsWith('-'))[0];
|
|
1219
|
+
showHelp(helpCmd);
|
|
894
1220
|
} else {
|
|
895
1221
|
console.log(`Unknown command: ${String(command).replace(/[\x00-\x1f\x7f-\x9f]/g, '')}`);
|
|
896
1222
|
console.log('Type "muaddib help" to see available commands.');
|