muaddib-scanner 2.10.20 → 2.10.22
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 +343 -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 +42 -0
- package/src/rules/index.js +118 -1
- package/src/scanner/ast-detectors.js +183 -0
- package/src/scanner/ast.js +5 -1
- package/src/scanner/npm-registry.js +16 -3
- package/src/scanner/shell.js +5 -5
- package/src/scoring.js +2 -2
- package/src/shared/constants.js +4 -4
- package/src/shared/http-limiter.js +54 -0
- package/src/temporal-analysis.js +9 -33
- package/src/temporal-ast-diff.js +19 -2
- 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
|
@@ -13,6 +13,9 @@ const args = process.argv.slice(2);
|
|
|
13
13
|
const command = args[0];
|
|
14
14
|
const options = args.slice(1);
|
|
15
15
|
|
|
16
|
+
// Helper: detect --help / -h in options
|
|
17
|
+
const wantHelp = options.includes('--help') || options.includes('-h');
|
|
18
|
+
|
|
16
19
|
// Parse options
|
|
17
20
|
let target = '.';
|
|
18
21
|
let jsonOutput = false;
|
|
@@ -394,19 +397,45 @@ const helpText = `
|
|
|
394
397
|
muaddib diff <ref> [path] Compare threats with a previous version
|
|
395
398
|
muaddib install <pkg> [options] Safe install (scan before install)
|
|
396
399
|
muaddib watch [path] Watch in real-time
|
|
400
|
+
muaddib sandbox <pkg> [options] Analyze in isolated Docker container
|
|
401
|
+
muaddib sandbox-report <pkg> Sandbox + detailed network report
|
|
402
|
+
muaddib evaluate [options] Run TPR/FPR/ADR evaluation suite
|
|
403
|
+
muaddib monitor [options] Start real-time npm/PyPI monitor
|
|
397
404
|
muaddib daemon [options] Start daemon
|
|
398
405
|
muaddib init-hooks [options] Setup git pre-commit hooks
|
|
399
406
|
muaddib remove-hooks [path] Remove MUAD'DIB git hooks
|
|
400
407
|
muaddib update Update IOCs
|
|
401
408
|
muaddib scrape Scrape new IOCs
|
|
402
|
-
muaddib
|
|
403
|
-
muaddib
|
|
409
|
+
muaddib feed [options] Threat feed (JSON)
|
|
410
|
+
muaddib serve [options] Threat feed HTTP server
|
|
411
|
+
muaddib detections [options] View monitor detections
|
|
412
|
+
muaddib stats [options] View scan statistics
|
|
413
|
+
muaddib replay [options] Replay ground-truth attacks
|
|
404
414
|
muaddib version Show version
|
|
405
415
|
|
|
406
|
-
|
|
407
|
-
--
|
|
408
|
-
--
|
|
409
|
-
|
|
416
|
+
Scan Options:
|
|
417
|
+
--json JSON output
|
|
418
|
+
--html [file] HTML report (default: muaddib-report.html)
|
|
419
|
+
--sarif [file] SARIF report (default: muaddib-results.sarif)
|
|
420
|
+
--explain Detailed explanations with MITRE references
|
|
421
|
+
--breakdown Show score breakdown by threat
|
|
422
|
+
--fail-on [level] Exit code threshold (critical|high|medium|low, default: high)
|
|
423
|
+
--webhook [url] Discord/Slack webhook (HTTPS only)
|
|
424
|
+
--paranoid Ultra-strict detection mode
|
|
425
|
+
--exclude [dir] Exclude directory from scan (repeatable)
|
|
426
|
+
--config [file] Custom config file (.muaddibrc.json format)
|
|
427
|
+
--entropy-threshold [n] Custom entropy threshold (0-8, default: 5.5)
|
|
428
|
+
--no-deobfuscate Disable deobfuscation pre-processing
|
|
429
|
+
--no-module-graph Disable cross-file dataflow analysis
|
|
430
|
+
--no-reachability Disable entry-point reachability analysis
|
|
431
|
+
--auto-sandbox Auto-trigger sandbox when static scan score >= 20 (requires Docker)
|
|
432
|
+
|
|
433
|
+
Temporal Options (scan):
|
|
434
|
+
--temporal Detect sudden lifecycle script changes
|
|
435
|
+
--temporal-ast Detect sudden dangerous API additions via AST diff
|
|
436
|
+
--temporal-publish Detect publish frequency anomalies
|
|
437
|
+
--temporal-maintainer Detect maintainer changes
|
|
438
|
+
--temporal-full All temporal analyses combined
|
|
410
439
|
|
|
411
440
|
Diff Examples:
|
|
412
441
|
muaddib diff HEAD~1 Compare with previous commit
|
|
@@ -414,40 +443,303 @@ const helpText = `
|
|
|
414
443
|
muaddib diff main Compare with branch
|
|
415
444
|
muaddib diff abc1234 ./myproject Compare specific commit
|
|
416
445
|
|
|
446
|
+
Install Options:
|
|
447
|
+
--save-dev, -D Install as dev dependency
|
|
448
|
+
-g, --global Install globally
|
|
449
|
+
--force Force install despite threats
|
|
450
|
+
|
|
451
|
+
Sandbox Options:
|
|
452
|
+
--local Analyze a local path instead of npm package
|
|
453
|
+
--strict Block non-essential network access
|
|
454
|
+
--no-canary Disable honey token injection
|
|
455
|
+
|
|
417
456
|
Init-hooks Options:
|
|
418
457
|
--type [auto|husky|pre-commit|git] Hook system (default: auto)
|
|
419
458
|
--mode [scan|diff] scan=all threats, diff=new only
|
|
420
459
|
|
|
460
|
+
Evaluate Options:
|
|
461
|
+
--json JSON output
|
|
462
|
+
--benign-limit [n] Limit benign packages to scan
|
|
463
|
+
--refresh-benign Re-download benign packages
|
|
464
|
+
|
|
465
|
+
Feed/Serve Options:
|
|
466
|
+
--limit [n] Limit feed entries (default: 50)
|
|
467
|
+
--severity [level] Filter by severity (CRITICAL|HIGH|MEDIUM|LOW)
|
|
468
|
+
--since [date] Filter detections after date (ISO 8601)
|
|
469
|
+
--port [n] HTTP server port (default: 3000)
|
|
470
|
+
|
|
471
|
+
Monitor Options:
|
|
472
|
+
--verbose Show detailed output
|
|
473
|
+
--temporal --test <pkg> Test temporal analysis on a single package
|
|
474
|
+
--temporal-ast --test <pkg> Test AST diff on a single package
|
|
475
|
+
|
|
476
|
+
Detections Options:
|
|
477
|
+
--json JSON output
|
|
478
|
+
--stats Show aggregated detection statistics
|
|
479
|
+
|
|
480
|
+
Stats Options:
|
|
481
|
+
--json JSON output
|
|
482
|
+
--daily Show daily breakdown (last 7 days)
|
|
483
|
+
|
|
484
|
+
Replay Options:
|
|
485
|
+
--verbose Show detailed findings per attack
|
|
486
|
+
--json Machine-readable JSON output
|
|
487
|
+
GT-NNN Replay single attack by ID
|
|
488
|
+
`;
|
|
489
|
+
|
|
490
|
+
// Per-command help texts for subcommand --help
|
|
491
|
+
const commandHelp = {
|
|
492
|
+
scan: `
|
|
493
|
+
Usage: muaddib scan [path] [options]
|
|
494
|
+
|
|
495
|
+
Scan a project for supply-chain threats.
|
|
496
|
+
|
|
497
|
+
Arguments:
|
|
498
|
+
path Target directory to scan (default: .)
|
|
499
|
+
|
|
421
500
|
Options:
|
|
422
501
|
--json JSON output
|
|
423
|
-
--html [file] HTML report
|
|
424
|
-
--sarif [file] SARIF report (
|
|
425
|
-
--explain Detailed explanations
|
|
502
|
+
--html [file] HTML report (default: muaddib-report.html)
|
|
503
|
+
--sarif [file] SARIF report (default: muaddib-results.sarif)
|
|
504
|
+
--explain Detailed explanations with MITRE references
|
|
426
505
|
--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
|
|
506
|
+
--fail-on [level] Exit code threshold (critical|high|medium|low, default: high)
|
|
507
|
+
--webhook [url] Discord/Slack webhook (HTTPS only)
|
|
508
|
+
--paranoid Ultra-strict detection mode
|
|
509
|
+
--exclude [dir] Exclude directory from scan (repeatable)
|
|
510
|
+
--config [file] Custom config file (.muaddibrc.json format)
|
|
511
|
+
--entropy-threshold [n] Custom entropy threshold (0-8, default: 5.5)
|
|
437
512
|
--no-deobfuscate Disable deobfuscation pre-processing
|
|
438
513
|
--no-module-graph Disable cross-file dataflow analysis
|
|
439
514
|
--no-reachability Disable entry-point reachability analysis
|
|
440
|
-
--
|
|
441
|
-
--
|
|
442
|
-
--
|
|
443
|
-
--
|
|
444
|
-
--
|
|
445
|
-
--
|
|
446
|
-
|
|
515
|
+
--auto-sandbox Auto-trigger sandbox when static scan score >= 20 (requires Docker)
|
|
516
|
+
--temporal Detect sudden lifecycle script changes
|
|
517
|
+
--temporal-ast Detect sudden dangerous API additions
|
|
518
|
+
--temporal-publish Detect publish frequency anomalies
|
|
519
|
+
--temporal-maintainer Detect maintainer changes
|
|
520
|
+
--temporal-full All temporal analyses combined
|
|
521
|
+
|
|
522
|
+
Examples:
|
|
523
|
+
muaddib scan .
|
|
524
|
+
muaddib scan ./my-project --explain --paranoid
|
|
525
|
+
muaddib scan . --json > results.json
|
|
526
|
+
muaddib scan . --html report.html --explain
|
|
527
|
+
`,
|
|
528
|
+
diff: `
|
|
529
|
+
Usage: muaddib diff <ref> [path] [options]
|
|
530
|
+
|
|
531
|
+
Compare threats between two versions of a project.
|
|
532
|
+
Without <ref>, shows available tags and recent commits.
|
|
533
|
+
|
|
534
|
+
Arguments:
|
|
535
|
+
ref Commit hash, tag, or branch to compare with
|
|
536
|
+
path Target directory (default: .)
|
|
537
|
+
|
|
538
|
+
Options:
|
|
539
|
+
--json JSON output
|
|
540
|
+
--explain Detailed explanations
|
|
541
|
+
--fail-on [level] Exit code threshold (critical|high|medium|low)
|
|
542
|
+
--paranoid Ultra-strict detection mode
|
|
543
|
+
|
|
544
|
+
Examples:
|
|
545
|
+
muaddib diff HEAD~1
|
|
546
|
+
muaddib diff v1.2.0
|
|
547
|
+
muaddib diff main ./myproject
|
|
548
|
+
`,
|
|
549
|
+
install: `
|
|
550
|
+
Usage: muaddib install <package> [<package>...] [options]
|
|
551
|
+
|
|
552
|
+
Scan packages for threats before installing them.
|
|
553
|
+
|
|
554
|
+
Options:
|
|
447
555
|
--save-dev, -D Install as dev dependency
|
|
448
556
|
-g, --global Install globally
|
|
449
|
-
--force Force install despite threats
|
|
450
|
-
|
|
557
|
+
--force Force install despite detected threats
|
|
558
|
+
|
|
559
|
+
Examples:
|
|
560
|
+
muaddib install lodash
|
|
561
|
+
muaddib install express morgan --save-dev
|
|
562
|
+
muaddib install -g typescript
|
|
563
|
+
`,
|
|
564
|
+
sandbox: `
|
|
565
|
+
Usage: muaddib sandbox <package-name|path> [options]
|
|
566
|
+
|
|
567
|
+
Run dynamic analysis in an isolated Docker container.
|
|
568
|
+
Requires Docker to be installed and running.
|
|
569
|
+
|
|
570
|
+
Options:
|
|
571
|
+
--local Analyze a local path instead of npm package
|
|
572
|
+
--strict Block non-essential network access
|
|
573
|
+
--no-canary Disable honey token injection
|
|
574
|
+
|
|
575
|
+
Examples:
|
|
576
|
+
muaddib sandbox suspicious-pkg
|
|
577
|
+
muaddib sandbox ./local-pkg --local --strict
|
|
578
|
+
`,
|
|
579
|
+
'sandbox-report': `
|
|
580
|
+
Usage: muaddib sandbox-report <package-name|path> [options]
|
|
581
|
+
|
|
582
|
+
Run sandbox analysis with detailed network traffic report.
|
|
583
|
+
Same options as 'muaddib sandbox'.
|
|
584
|
+
`,
|
|
585
|
+
evaluate: `
|
|
586
|
+
Usage: muaddib evaluate [options]
|
|
587
|
+
|
|
588
|
+
Run the full TPR/FPR/ADR evaluation suite against ground truth,
|
|
589
|
+
benign packages, and adversarial samples.
|
|
590
|
+
|
|
591
|
+
Options:
|
|
592
|
+
--json JSON output
|
|
593
|
+
--benign-limit [n] Limit benign packages to scan
|
|
594
|
+
--refresh-benign Re-download benign packages
|
|
595
|
+
|
|
596
|
+
Examples:
|
|
597
|
+
muaddib evaluate
|
|
598
|
+
muaddib evaluate --json > eval-results.json
|
|
599
|
+
`,
|
|
600
|
+
monitor: `
|
|
601
|
+
Usage: muaddib monitor [options]
|
|
602
|
+
|
|
603
|
+
Start real-time monitoring of npm/PyPI registries for
|
|
604
|
+
newly published malicious packages.
|
|
605
|
+
|
|
606
|
+
Options:
|
|
607
|
+
--verbose Show detailed output
|
|
608
|
+
--temporal --test <pkg> Test temporal analysis on a single package
|
|
609
|
+
--temporal-ast --test <pkg> Test AST diff on a single package
|
|
610
|
+
|
|
611
|
+
Examples:
|
|
612
|
+
muaddib monitor
|
|
613
|
+
muaddib monitor --verbose
|
|
614
|
+
muaddib monitor --temporal --test lodash
|
|
615
|
+
`,
|
|
616
|
+
feed: `
|
|
617
|
+
Usage: muaddib feed [options]
|
|
618
|
+
|
|
619
|
+
Output the threat detection feed as JSON.
|
|
620
|
+
|
|
621
|
+
Options:
|
|
622
|
+
--limit [n] Limit entries (default: 50)
|
|
623
|
+
--severity [level] Filter by severity (CRITICAL|HIGH|MEDIUM|LOW)
|
|
624
|
+
--since [date] Filter after date (ISO 8601)
|
|
625
|
+
|
|
626
|
+
Examples:
|
|
627
|
+
muaddib feed
|
|
628
|
+
muaddib feed --severity CRITICAL --limit 10
|
|
629
|
+
`,
|
|
630
|
+
serve: `
|
|
631
|
+
Usage: muaddib serve [options]
|
|
632
|
+
|
|
633
|
+
Start an HTTP server serving the threat feed.
|
|
634
|
+
|
|
635
|
+
Options:
|
|
636
|
+
--port [n] Server port (default: 3000)
|
|
637
|
+
|
|
638
|
+
Examples:
|
|
639
|
+
muaddib serve
|
|
640
|
+
muaddib serve --port 8080
|
|
641
|
+
`,
|
|
642
|
+
detections: `
|
|
643
|
+
Usage: muaddib detections [options]
|
|
644
|
+
|
|
645
|
+
View detections recorded by the monitor.
|
|
646
|
+
|
|
647
|
+
Options:
|
|
648
|
+
--json Full JSON output
|
|
649
|
+
--stats Show aggregated detection statistics
|
|
650
|
+
|
|
651
|
+
Examples:
|
|
652
|
+
muaddib detections
|
|
653
|
+
muaddib detections --stats
|
|
654
|
+
muaddib detections --json > detections.json
|
|
655
|
+
`,
|
|
656
|
+
stats: `
|
|
657
|
+
Usage: muaddib stats [options]
|
|
658
|
+
|
|
659
|
+
View scan statistics from the monitor.
|
|
660
|
+
|
|
661
|
+
Options:
|
|
662
|
+
--json JSON output
|
|
663
|
+
--daily Show daily breakdown (last 7 days)
|
|
664
|
+
|
|
665
|
+
Examples:
|
|
666
|
+
muaddib stats
|
|
667
|
+
muaddib stats --daily
|
|
668
|
+
`,
|
|
669
|
+
replay: `
|
|
670
|
+
Usage: muaddib replay [options] [GT-NNN]
|
|
671
|
+
|
|
672
|
+
Replay ground-truth attack samples to verify detection.
|
|
673
|
+
|
|
674
|
+
Options:
|
|
675
|
+
--verbose, -v Show detailed findings per attack
|
|
676
|
+
--json Machine-readable JSON output
|
|
677
|
+
GT-NNN Replay a single attack by ID
|
|
678
|
+
|
|
679
|
+
Examples:
|
|
680
|
+
muaddib replay
|
|
681
|
+
muaddib replay --verbose
|
|
682
|
+
muaddib replay GT-001
|
|
683
|
+
`,
|
|
684
|
+
'init-hooks': `
|
|
685
|
+
Usage: muaddib init-hooks [options]
|
|
686
|
+
|
|
687
|
+
Setup git pre-commit hooks for automatic scanning.
|
|
688
|
+
|
|
689
|
+
Options:
|
|
690
|
+
--type [auto|husky|pre-commit|git] Hook system (default: auto)
|
|
691
|
+
--mode [scan|diff] scan=all threats, diff=new only
|
|
692
|
+
|
|
693
|
+
Examples:
|
|
694
|
+
muaddib init-hooks
|
|
695
|
+
muaddib init-hooks --type husky --mode diff
|
|
696
|
+
`,
|
|
697
|
+
'remove-hooks': `
|
|
698
|
+
Usage: muaddib remove-hooks [path]
|
|
699
|
+
|
|
700
|
+
Remove MUAD'DIB git hooks from a project.
|
|
701
|
+
`,
|
|
702
|
+
watch: `
|
|
703
|
+
Usage: muaddib watch [path]
|
|
704
|
+
|
|
705
|
+
Watch a project directory and re-scan on file changes.
|
|
706
|
+
|
|
707
|
+
Examples:
|
|
708
|
+
muaddib watch
|
|
709
|
+
muaddib watch ./my-project
|
|
710
|
+
`,
|
|
711
|
+
daemon: `
|
|
712
|
+
Usage: muaddib daemon [options]
|
|
713
|
+
|
|
714
|
+
Start the MUAD'DIB daemon for background monitoring.
|
|
715
|
+
|
|
716
|
+
Options:
|
|
717
|
+
--webhook [url] Discord/Slack webhook URL
|
|
718
|
+
|
|
719
|
+
Examples:
|
|
720
|
+
muaddib daemon
|
|
721
|
+
muaddib daemon --webhook https://discord.com/api/webhooks/...
|
|
722
|
+
`,
|
|
723
|
+
report: `
|
|
724
|
+
Usage: muaddib report --now | --status
|
|
725
|
+
|
|
726
|
+
Send or check status of daily monitor reports.
|
|
727
|
+
|
|
728
|
+
Options:
|
|
729
|
+
--now Send report immediately
|
|
730
|
+
--status Show report status
|
|
731
|
+
`,
|
|
732
|
+
};
|
|
733
|
+
|
|
734
|
+
// Show per-command help or global help
|
|
735
|
+
function showHelp(cmd) {
|
|
736
|
+
if (cmd && commandHelp[cmd]) {
|
|
737
|
+
console.log(commandHelp[cmd]);
|
|
738
|
+
} else {
|
|
739
|
+
console.log(helpText);
|
|
740
|
+
}
|
|
741
|
+
process.exit(0);
|
|
742
|
+
}
|
|
451
743
|
|
|
452
744
|
// Main
|
|
453
745
|
if (command === 'version' || command === '--version' || command === '-v') {
|
|
@@ -456,18 +748,14 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
456
748
|
process.exit(0);
|
|
457
749
|
} else if (!command || command === '--help' || command === '-h') {
|
|
458
750
|
if (command === '--help' || command === '-h') {
|
|
459
|
-
|
|
460
|
-
process.exit(0);
|
|
751
|
+
showHelp();
|
|
461
752
|
}
|
|
462
753
|
interactiveMenu().catch(err => {
|
|
463
754
|
console.error('[ERROR]', err.message);
|
|
464
755
|
process.exit(1);
|
|
465
756
|
});
|
|
466
757
|
} else if (command === 'scan') {
|
|
467
|
-
if (
|
|
468
|
-
console.log(helpText);
|
|
469
|
-
process.exit(0);
|
|
470
|
-
}
|
|
758
|
+
if (wantHelp) showHelp('scan');
|
|
471
759
|
run(target, {
|
|
472
760
|
json: jsonOutput,
|
|
473
761
|
html: htmlOutput,
|
|
@@ -495,6 +783,7 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
495
783
|
process.exit(1);
|
|
496
784
|
});
|
|
497
785
|
} else if (command === 'feed') {
|
|
786
|
+
if (wantHelp) showHelp('feed');
|
|
498
787
|
const { getFeed } = require('../src/threat-feed.js');
|
|
499
788
|
const feedOpts = {};
|
|
500
789
|
if (feedLimit) feedOpts.limit = feedLimit;
|
|
@@ -504,10 +793,12 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
504
793
|
console.log(JSON.stringify(result, null, 2));
|
|
505
794
|
process.exit(0);
|
|
506
795
|
} else if (command === 'serve') {
|
|
796
|
+
if (wantHelp) showHelp('serve');
|
|
507
797
|
const { startServer } = require('../src/serve.js');
|
|
508
798
|
startServer({ port: servePort || 3000 });
|
|
509
799
|
// Server runs indefinitely — no process.exit
|
|
510
800
|
} else if (command === 'watch') {
|
|
801
|
+
if (wantHelp) showHelp('watch');
|
|
511
802
|
watch(target);
|
|
512
803
|
} else if (command === 'update') {
|
|
513
804
|
updateIOCs().then(() => {
|
|
@@ -525,6 +816,7 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
525
816
|
process.exit(1);
|
|
526
817
|
});
|
|
527
818
|
} else if (command === 'monitor') {
|
|
819
|
+
if (wantHelp) showHelp('monitor');
|
|
528
820
|
const testPkg = options.filter(o => !o.startsWith('-'));
|
|
529
821
|
const isTemporal = options.includes('--temporal');
|
|
530
822
|
const isTemporalAst = options.includes('--temporal-ast');
|
|
@@ -614,9 +906,11 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
614
906
|
});
|
|
615
907
|
}
|
|
616
908
|
} else if (command === 'daemon') {
|
|
909
|
+
if (wantHelp) showHelp('daemon');
|
|
617
910
|
const { startDaemon } = require('../src/daemon.js');
|
|
618
911
|
startDaemon({ webhook: webhookUrl });
|
|
619
912
|
} else if (command === 'install' || command === 'i') {
|
|
913
|
+
if (wantHelp) showHelp('install');
|
|
620
914
|
const packages = options.filter(o => !o.startsWith('-'));
|
|
621
915
|
const isDev = options.includes('--save-dev') || options.includes('-D');
|
|
622
916
|
const isGlobal = options.includes('-g') || options.includes('--global');
|
|
@@ -637,13 +931,14 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
637
931
|
process.exit(1);
|
|
638
932
|
});
|
|
639
933
|
} else if (command === 'sandbox') {
|
|
934
|
+
if (wantHelp) showHelp('sandbox');
|
|
640
935
|
const sandboxOpts = options.filter(o => !o.startsWith('-'));
|
|
641
936
|
const packageName = sandboxOpts[0];
|
|
642
937
|
const strict = options.includes('--strict');
|
|
643
938
|
const canary = !options.includes('--no-canary');
|
|
644
939
|
const local = options.includes('--local');
|
|
645
940
|
if (!packageName) {
|
|
646
|
-
console.log('
|
|
941
|
+
console.log(commandHelp['sandbox']);
|
|
647
942
|
process.exit(1);
|
|
648
943
|
}
|
|
649
944
|
|
|
@@ -657,13 +952,14 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
657
952
|
process.exit(1);
|
|
658
953
|
});
|
|
659
954
|
} else if (command === 'sandbox-report') {
|
|
955
|
+
if (wantHelp) showHelp('sandbox-report');
|
|
660
956
|
const sandboxOpts = options.filter(o => !o.startsWith('-'));
|
|
661
957
|
const packageName = sandboxOpts[0];
|
|
662
958
|
const strict = options.includes('--strict');
|
|
663
959
|
const canary = !options.includes('--no-canary');
|
|
664
960
|
const local = options.includes('--local');
|
|
665
961
|
if (!packageName) {
|
|
666
|
-
console.log('
|
|
962
|
+
console.log(commandHelp['sandbox-report']);
|
|
667
963
|
process.exit(1);
|
|
668
964
|
}
|
|
669
965
|
|
|
@@ -680,6 +976,7 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
680
976
|
process.exit(1);
|
|
681
977
|
});
|
|
682
978
|
} else if (command === 'diff') {
|
|
979
|
+
if (wantHelp) showHelp('diff');
|
|
683
980
|
// Parse diff arguments: muaddib diff <ref> [path] [options]
|
|
684
981
|
const diffArgs = options.filter(o => !o.startsWith('-'));
|
|
685
982
|
const baseRef = diffArgs[0];
|
|
@@ -702,6 +999,7 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
702
999
|
process.exit(1);
|
|
703
1000
|
});
|
|
704
1001
|
} else if (command === 'detections') {
|
|
1002
|
+
if (wantHelp) showHelp('detections');
|
|
705
1003
|
const { loadDetections, getDetectionStats } = require('../src/monitor.js');
|
|
706
1004
|
const wantStats = options.includes('--stats');
|
|
707
1005
|
const wantJson = options.includes('--json');
|
|
@@ -753,6 +1051,7 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
753
1051
|
console.log('');
|
|
754
1052
|
process.exit(0);
|
|
755
1053
|
} else if (command === 'stats') {
|
|
1054
|
+
if (wantHelp) showHelp('stats');
|
|
756
1055
|
const { loadScanStats } = require('../src/monitor.js');
|
|
757
1056
|
const wantDaily = options.includes('--daily');
|
|
758
1057
|
const wantJson = options.includes('--json');
|
|
@@ -796,6 +1095,7 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
796
1095
|
console.log('');
|
|
797
1096
|
process.exit(0);
|
|
798
1097
|
} else if (command === 'evaluate') {
|
|
1098
|
+
if (wantHelp) showHelp('evaluate');
|
|
799
1099
|
const { evaluate } = require('../src/commands/evaluate.js');
|
|
800
1100
|
const evalOpts = { json: jsonOutput };
|
|
801
1101
|
for (let i = 0; i < options.length; i++) {
|
|
@@ -813,6 +1113,7 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
813
1113
|
process.exit(1);
|
|
814
1114
|
});
|
|
815
1115
|
} else if (command === 'init-hooks') {
|
|
1116
|
+
if (wantHelp) showHelp('init-hooks');
|
|
816
1117
|
// Parse init-hooks arguments
|
|
817
1118
|
let hookType = 'auto';
|
|
818
1119
|
let hookMode = 'scan';
|
|
@@ -834,6 +1135,7 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
834
1135
|
process.exit(1);
|
|
835
1136
|
});
|
|
836
1137
|
} else if (command === 'remove-hooks') {
|
|
1138
|
+
if (wantHelp) showHelp('remove-hooks');
|
|
837
1139
|
removeHooks(target).then(success => {
|
|
838
1140
|
process.exit(success ? 0 : 1);
|
|
839
1141
|
}).catch(err => {
|
|
@@ -841,6 +1143,7 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
841
1143
|
process.exit(1);
|
|
842
1144
|
});
|
|
843
1145
|
} else if (command === 'replay' || command === 'ground-truth') {
|
|
1146
|
+
if (wantHelp) showHelp('replay');
|
|
844
1147
|
const { replay } = require('../tests/ground-truth/replay.js');
|
|
845
1148
|
const replayOpts = {};
|
|
846
1149
|
for (const o of options) {
|
|
@@ -858,7 +1161,7 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
858
1161
|
process.exit(1);
|
|
859
1162
|
});
|
|
860
1163
|
} else if (command === 'report') {
|
|
861
|
-
|
|
1164
|
+
if (wantHelp) showHelp('report');
|
|
862
1165
|
if (options.includes('--now')) {
|
|
863
1166
|
const { sendReportNow } = require('../src/monitor.js');
|
|
864
1167
|
sendReportNow().then(result => {
|
|
@@ -889,8 +1192,9 @@ if (command === 'version' || command === '--version' || command === '-v') {
|
|
|
889
1192
|
process.exit(1);
|
|
890
1193
|
}
|
|
891
1194
|
} else if (command === 'help') {
|
|
892
|
-
|
|
893
|
-
|
|
1195
|
+
// muaddib help <command> — show per-command help
|
|
1196
|
+
const helpCmd = options.filter(o => !o.startsWith('-'))[0];
|
|
1197
|
+
showHelp(helpCmd);
|
|
894
1198
|
} else {
|
|
895
1199
|
console.log(`Unknown command: ${String(command).replace(/[\x00-\x1f\x7f-\x9f]/g, '')}`);
|
|
896
1200
|
console.log('Type "muaddib help" to see available commands.');
|