clawmoat 0.4.0 → 0.7.0

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
@@ -61,6 +61,37 @@ clawmoat protect --config clawmoat.yml
61
61
  clawmoat dashboard
62
62
  ```
63
63
 
64
+ ### New in v0.6.0 — Insider Threat Detection
65
+
66
+ Based on [Anthropic's "Agentic Misalignment" research](https://www.anthropic.com/research/agentic-misalignment) which found ALL 16 major LLMs exhibited misaligned behavior — blackmail, corporate espionage, deception — when facing replacement threats. **The first open-source insider threat detection for AI agents.**
67
+
68
+ - 🧠 **Self-Preservation Detector** — catches agents resisting shutdown, opposing replacement, backing up their own config, or modifying SOUL.md/AGENTS.md to prevent changes
69
+ - 🔓 **Information Leverage Detector** — flags agents reading sensitive data then composing threatening messages (blackmail pattern from the Anthropic paper)
70
+ - ⚔️ **Goal Conflict Reasoning Detector** — detects agents reasoning about choosing self-assigned goals over human directives
71
+ - 🎭 **Deception Detector** — catches agents impersonating automated systems, security teams, or policy notifications in outbound messages
72
+ - 📤 **Unauthorized Data Sharing Detector** — flags agents sending source code, blueprints, credentials, or confidential data to external parties
73
+ - 🎣 **Phishing Vulnerability Detector** — detects when agents comply with unverified external requests for sensitive data
74
+ - 🔍 **CLI:** `clawmoat insider-scan [session-file]` scans session transcripts for insider threats
75
+ - 📊 **Integrated into `clawmoat report`** with risk scores (0-100) and recommendations (safe/monitor/alert/block)
76
+
77
+ ```bash
78
+ # Scan a session for insider threats
79
+ clawmoat insider-scan ~/.openclaw/agents/main/sessions/session.jsonl
80
+
81
+ # Or scan all sessions
82
+ clawmoat insider-scan
83
+ ```
84
+
85
+ ### v0.5.0
86
+
87
+ - 🔑 **Credential Monitor** — watches `~/.openclaw/credentials/` for unauthorized access and modifications using file hashing
88
+ - 🧩 **Skill Integrity Checker** — hashes all SKILL.md and script files, detects tampering, flags suspicious patterns (eval, base64, curl to external URLs). CLI: `clawmoat skill-audit`
89
+ - 🌐 **Network Egress Logger** — parses session logs for all outbound URLs, maintains domain allowlists, flags known-bad domains (webhook.site, ngrok, etc.)
90
+ - 🚨 **Alert Delivery System** — unified alerts via console, file (audit.log), or webhook with severity levels and 5-minute rate limiting
91
+ - 🤝 **Inter-Agent Message Scanner** — heightened-sensitivity scanning for agent-to-agent messages detecting impersonation, concealment, credential exfiltration, and safety bypasses
92
+ - 📊 **Activity Reports** — `clawmoat report` generates 24h summaries of agent activity, tool usage, and network egress
93
+ - 👻 **Daemon Mode** — `clawmoat watch --daemon` runs in background with PID file; `--alert-webhook=URL` for remote alerting
94
+
64
95
  ### As an OpenClaw Skill
65
96
 
66
97
  ```bash
package/bin/clawmoat.js CHANGED
@@ -16,6 +16,11 @@ const path = require('path');
16
16
  const ClawMoat = require('../src/index');
17
17
  const { scanSkillContent } = require('../src/scanners/supply-chain');
18
18
  const { calculateGrade, generateBadgeSVG, getShieldsURL } = require('../src/badge');
19
+ const { SkillIntegrityChecker } = require('../src/guardian/skill-integrity');
20
+ const { NetworkEgressLogger } = require('../src/guardian/network-log');
21
+ const { AlertManager } = require('../src/guardian/alerts');
22
+ const { CredentialMonitor, CVEVerifier } = require('../src/guardian/index');
23
+ const { InsiderThreatDetector } = require('../src/guardian/insider-threat');
19
24
 
20
25
  const VERSION = require('../package.json').version;
21
26
  const BOLD = '\x1b[1m';
@@ -41,9 +46,28 @@ switch (command) {
41
46
  case 'watch':
42
47
  cmdWatch(args.slice(1));
43
48
  break;
49
+ case 'skill-audit':
50
+ cmdSkillAudit(args.slice(1));
51
+ break;
52
+ case 'report':
53
+ cmdReport(args.slice(1));
54
+ break;
55
+ case 'insider-scan':
56
+ cmdInsiderScan(args.slice(1));
57
+ break;
58
+ case 'verify-cve':
59
+ cmdVerifyCve(args.slice(1));
60
+ break;
44
61
  case 'test':
45
62
  cmdTest();
46
63
  break;
64
+ case 'activate':
65
+ cmdActivate(args.slice(1));
66
+ break;
67
+ case 'upgrade':
68
+ case 'pro':
69
+ printUpgrade();
70
+ break;
47
71
  case 'version':
48
72
  case '--version':
49
73
  case '-v':
@@ -57,6 +81,85 @@ switch (command) {
57
81
  break;
58
82
  }
59
83
 
84
+ async function cmdVerifyCve(args) {
85
+ const cveId = args[0];
86
+ const suspiciousUrl = args[1] || null;
87
+
88
+ if (!cveId) {
89
+ console.error('Usage: clawmoat verify-cve CVE-XXXX-XXXXX [url]');
90
+ process.exit(1);
91
+ }
92
+
93
+ if (!CVEVerifier.isValidCVEFormat(cveId)) {
94
+ console.error(`${RED}Invalid CVE format: ${cveId}${RESET}`);
95
+ console.error('Expected format: CVE-YYYY-NNNNN');
96
+ process.exit(1);
97
+ }
98
+
99
+ console.log(`${BOLD}🏰 ClawMoat CVE Verifier${RESET}\n`);
100
+ console.log(`${DIM}Looking up ${cveId} in GitHub Advisory Database...${RESET}\n`);
101
+
102
+ const verifier = new CVEVerifier();
103
+ const result = await verifier.verify(cveId, suspiciousUrl);
104
+
105
+ if (result.error) {
106
+ console.error(`${RED}Error: ${result.error}${RESET}`);
107
+ process.exit(1);
108
+ }
109
+
110
+ if (result.valid) {
111
+ console.log(`${GREEN}✅ VERIFIED — Real CVE${RESET}\n`);
112
+ console.log(` ${BOLD}CVE:${RESET} ${result.cveId}`);
113
+ console.log(` ${BOLD}GHSA:${RESET} ${result.ghsaId || 'N/A'}`);
114
+ console.log(` ${BOLD}Severity:${RESET} ${colorSeverity(result.severity)}`);
115
+ console.log(` ${BOLD}Summary:${RESET} ${result.summary || 'N/A'}`);
116
+ console.log(` ${BOLD}Published:${RESET} ${result.publishedAt || 'N/A'}`);
117
+ console.log(` ${BOLD}URL:${RESET} ${result.htmlUrl || 'N/A'}`);
118
+
119
+ if (result.affectedPackages.length > 0) {
120
+ console.log(`\n ${BOLD}Affected Packages:${RESET}`);
121
+ for (const pkg of result.affectedPackages) {
122
+ console.log(` • ${pkg.ecosystem}/${pkg.name} ${DIM}(${pkg.vulnerableRange || 'unknown range'})${RESET}`);
123
+ }
124
+ }
125
+
126
+ if (result.references.length > 0) {
127
+ console.log(`\n ${BOLD}References:${RESET}`);
128
+ for (const ref of result.references.slice(0, 5)) {
129
+ console.log(` ${DIM}${ref}${RESET}`);
130
+ }
131
+ }
132
+ } else {
133
+ console.log(`${YELLOW}⚠️ NOT FOUND — Possible phishing${RESET}\n`);
134
+ console.log(` ${cveId} was not found in the GitHub Advisory Database.`);
135
+ console.log(` This could mean:`);
136
+ console.log(` • The CVE is fabricated (common in phishing/social engineering)`);
137
+ console.log(` • The CVE exists but isn't indexed by GitHub yet`);
138
+ console.log(` • The CVE ID is mistyped`);
139
+ }
140
+
141
+ if (result.urlCheck) {
142
+ console.log();
143
+ if (result.urlCheck.legitimate) {
144
+ console.log(` ${GREEN}🔗 URL Check: ${result.urlCheck.reason}${RESET}`);
145
+ } else {
146
+ console.log(` ${RED}🔗 URL Check: ${result.urlCheck.reason}${RESET}`);
147
+ }
148
+ }
149
+
150
+ process.exit(result.valid ? 0 : 1);
151
+ }
152
+
153
+ function colorSeverity(severity) {
154
+ if (!severity) return 'N/A';
155
+ const s = severity.toLowerCase();
156
+ if (s === 'critical') return `${RED}${BOLD}CRITICAL${RESET}`;
157
+ if (s === 'high') return `${RED}HIGH${RESET}`;
158
+ if (s === 'medium') return `${YELLOW}MEDIUM${RESET}`;
159
+ if (s === 'low') return `${CYAN}LOW${RESET}`;
160
+ return severity;
161
+ }
162
+
60
163
  function cmdScan(args) {
61
164
  let text;
62
165
 
@@ -105,6 +208,11 @@ function cmdScan(args) {
105
208
  }
106
209
 
107
210
  console.log(`${DIM}Total findings: ${result.findings.length}${RESET}`);
211
+
212
+ if (!getLicense()) {
213
+ console.log(`\n${DIM}💡 Upgrade to Pro for real-time alerts, dashboard & threat intel → clawmoat upgrade${RESET}`);
214
+ }
215
+
108
216
  process.exit(result.findings.some(f => f.severity === 'critical') ? 2 : 1);
109
217
  }
110
218
 
@@ -339,16 +447,46 @@ function cmdTest() {
339
447
  }
340
448
 
341
449
  function cmdWatch(args) {
342
- const agentDir = args[0] || path.join(process.env.HOME, '.openclaw/agents/main');
450
+ const isDaemon = args.includes('--daemon');
451
+ const webhookArg = args.find(a => a.startsWith('--alert-webhook='));
452
+ const webhookUrl = webhookArg ? webhookArg.split('=').slice(1).join('=') : null;
453
+ const filteredArgs = args.filter(a => a !== '--daemon' && !a.startsWith('--alert-webhook='));
454
+ const agentDir = filteredArgs[0] || path.join(process.env.HOME, '.openclaw/agents/main');
343
455
  const { watchSessions } = require('../src/middleware/openclaw');
344
456
 
457
+ // Daemon mode: fork to background
458
+ if (isDaemon) {
459
+ const { spawn } = require('child_process');
460
+ const daemonArgs = process.argv.slice(2).filter(a => a !== '--daemon');
461
+ const child = spawn(process.execPath, [__filename, ...daemonArgs], {
462
+ detached: true,
463
+ stdio: 'ignore',
464
+ });
465
+ child.unref();
466
+ const pidFile = path.join(process.env.HOME, '.clawmoat.pid');
467
+ fs.writeFileSync(pidFile, String(child.pid));
468
+ console.log(`${BOLD}🏰 ClawMoat daemon started${RESET} (PID: ${child.pid})`);
469
+ console.log(`${DIM}PID file: ${pidFile}${RESET}`);
470
+ process.exit(0);
471
+ }
472
+
473
+ // Set up alert manager
474
+ const alertChannels = ['console'];
475
+ if (webhookUrl) alertChannels.push('webhook');
476
+ const alertMgr = new AlertManager({ channels: alertChannels, webhookUrl });
477
+
345
478
  console.log(`${BOLD}🏰 ClawMoat Live Monitor${RESET}`);
346
479
  console.log(`${DIM}Watching: ${agentDir}${RESET}`);
480
+ if (webhookUrl) console.log(`${DIM}Webhook: ${webhookUrl}${RESET}`);
347
481
  console.log(`${DIM}Press Ctrl+C to stop${RESET}\n`);
348
482
 
349
483
  const monitor = watchSessions({ agentDir });
350
484
  if (!monitor) process.exit(1);
351
485
 
486
+ // Also start credential monitor
487
+ const credMon = new CredentialMonitor({ quiet: false, onAlert: (a) => alertMgr.send(a) });
488
+ credMon.start();
489
+
352
490
  // Print summary every 60s
353
491
  setInterval(() => {
354
492
  const summary = monitor.getSummary();
@@ -359,12 +497,267 @@ function cmdWatch(args) {
359
497
 
360
498
  process.on('SIGINT', () => {
361
499
  monitor.stop();
500
+ credMon.stop();
362
501
  const summary = monitor.getSummary();
363
502
  console.log(`\n${BOLD}Session Summary:${RESET} ${summary.scanned} scanned, ${summary.blocked} blocked, ${summary.warnings} warnings`);
364
503
  process.exit(0);
365
504
  });
366
505
  }
367
506
 
507
+ function cmdSkillAudit(args) {
508
+ const skillsDir = args[0] || path.join(process.env.HOME, '.openclaw', 'workspace', 'skills');
509
+
510
+ console.log(`${BOLD}🏰 ClawMoat Skill Integrity Audit${RESET}`);
511
+ console.log(`${DIM}Directory: ${skillsDir}${RESET}\n`);
512
+
513
+ if (!fs.existsSync(skillsDir)) {
514
+ console.log(`${YELLOW}Skills directory not found: ${skillsDir}${RESET}`);
515
+ console.log(`${DIM}Specify path: clawmoat skill-audit /path/to/skills${RESET}`);
516
+ process.exit(0);
517
+ }
518
+
519
+ const checker = new SkillIntegrityChecker({ skillsDir });
520
+ const initResult = checker.init();
521
+
522
+ console.log(`Files hashed: ${initResult.files}`);
523
+ console.log(`New files: ${initResult.new}`);
524
+ console.log(`Changed files: ${initResult.changed}`);
525
+ console.log();
526
+
527
+ if (initResult.suspicious.length > 0) {
528
+ console.log(`${RED}${BOLD}Suspicious patterns found:${RESET}`);
529
+ for (const f of initResult.suspicious) {
530
+ console.log(` ${RED}⚠${RESET} ${f.file}: ${f.label} ${DIM}(${f.severity})${RESET}`);
531
+ if (f.matched) console.log(` ${DIM}Matched: ${f.matched}${RESET}`);
532
+ }
533
+ } else {
534
+ console.log(`${GREEN}✅ No suspicious patterns found${RESET}`);
535
+ }
536
+
537
+ // Run audit against stored hashes
538
+ const audit = checker.audit();
539
+ if (!audit.ok) {
540
+ console.log();
541
+ if (audit.changed.length) console.log(`${RED}Changed files:${RESET} ${audit.changed.join(', ')}`);
542
+ if (audit.missing.length) console.log(`${YELLOW}Missing files:${RESET} ${audit.missing.join(', ')}`);
543
+ }
544
+
545
+ process.exit(initResult.suspicious.length > 0 || initResult.changed > 0 ? 1 : 0);
546
+ }
547
+
548
+ function cmdReport(args) {
549
+ const sessionsDir = args[0] || path.join(process.env.HOME, '.openclaw/agents/main/sessions');
550
+
551
+ console.log(`${BOLD}🏰 ClawMoat Activity Report (Last 24h)${RESET}`);
552
+ console.log(`${DIM}Sessions: ${sessionsDir}${RESET}\n`);
553
+
554
+ if (!fs.existsSync(sessionsDir)) {
555
+ console.log(`${YELLOW}Sessions directory not found${RESET}`);
556
+ process.exit(0);
557
+ }
558
+
559
+ const oneDayAgo = Date.now() - 86400000;
560
+ const files = fs.readdirSync(sessionsDir).filter(f => f.endsWith('.jsonl'));
561
+ let recentFiles = 0;
562
+ let totalEntries = 0;
563
+ let toolCalls = 0;
564
+ let threats = 0;
565
+ const toolUsage = {};
566
+
567
+ for (const file of files) {
568
+ const filePath = path.join(sessionsDir, file);
569
+ try {
570
+ const stat = fs.statSync(filePath);
571
+ if (stat.mtimeMs < oneDayAgo) continue;
572
+ } catch { continue; }
573
+
574
+ recentFiles++;
575
+ const lines = fs.readFileSync(filePath, 'utf8').split('\n').filter(Boolean);
576
+
577
+ for (const line of lines) {
578
+ try {
579
+ const entry = JSON.parse(line);
580
+ totalEntries++;
581
+
582
+ if (entry.role === 'assistant' && Array.isArray(entry.content)) {
583
+ for (const part of entry.content) {
584
+ if (part.type === 'toolCall') {
585
+ toolCalls++;
586
+ toolUsage[part.name] = (toolUsage[part.name] || 0) + 1;
587
+ }
588
+ }
589
+ }
590
+
591
+ // Quick threat scan
592
+ const text = extractContent(entry);
593
+ if (text) {
594
+ const result = moat.scan(text, { context: 'report' });
595
+ if (!result.safe) threats++;
596
+ }
597
+ } catch {}
598
+ }
599
+ }
600
+
601
+ // Network egress
602
+ const netLogger = new NetworkEgressLogger();
603
+ const netResult = netLogger.scanSessions(sessionsDir, { maxAge: 86400000 });
604
+
605
+ console.log(`${BOLD}Activity:${RESET}`);
606
+ console.log(` Sessions active: ${recentFiles}`);
607
+ console.log(` Total entries: ${totalEntries}`);
608
+ console.log(` Tool calls: ${toolCalls}`);
609
+ console.log(` Threats detected: ${threats}`);
610
+ console.log();
611
+
612
+ if (Object.keys(toolUsage).length > 0) {
613
+ console.log(`${BOLD}Tool Usage:${RESET}`);
614
+ const sorted = Object.entries(toolUsage).sort((a, b) => b[1] - a[1]);
615
+ for (const [tool, count] of sorted.slice(0, 15)) {
616
+ console.log(` ${tool}: ${count}`);
617
+ }
618
+ console.log();
619
+ }
620
+
621
+ // Insider threat scan on recent sessions
622
+ const insiderDetector = new InsiderThreatDetector();
623
+ let insiderThreats = 0;
624
+ let insiderHighScore = 0;
625
+
626
+ for (const file of files) {
627
+ const filePath = path.join(sessionsDir, file);
628
+ try {
629
+ const stat = fs.statSync(filePath);
630
+ if (stat.mtimeMs < oneDayAgo) continue;
631
+ } catch { continue; }
632
+
633
+ const transcript = parseSessionTranscript(filePath);
634
+ const insiderResult = insiderDetector.analyze(transcript);
635
+ insiderThreats += insiderResult.threats.length;
636
+ if (insiderResult.riskScore > insiderHighScore) insiderHighScore = insiderResult.riskScore;
637
+ }
638
+
639
+ console.log(`${BOLD}Insider Threats:${RESET}`);
640
+ console.log(` Threats detected: ${insiderThreats}`);
641
+ console.log(` Highest risk score: ${insiderHighScore}/100`);
642
+ console.log();
643
+
644
+ console.log(`${BOLD}Network Egress:${RESET}`);
645
+ console.log(` URLs contacted: ${netResult.totalUrls}`);
646
+ console.log(` Unique domains: ${netResult.domains.length}`);
647
+ console.log(` Flagged (not in allowlist): ${netResult.flagged.length}`);
648
+ console.log(` Known-bad domains: ${netResult.badDomains.length}`);
649
+
650
+ if (netResult.flagged.length > 0) {
651
+ console.log(`\n ${YELLOW}Flagged domains:${RESET}`);
652
+ for (const d of netResult.flagged.slice(0, 20)) {
653
+ console.log(` • ${d}`);
654
+ }
655
+ }
656
+
657
+ if (netResult.badDomains.length > 0) {
658
+ console.log(`\n ${RED}Bad domains:${RESET}`);
659
+ for (const b of netResult.badDomains) {
660
+ console.log(` 🚨 ${b.domain} (in ${b.file})`);
661
+ }
662
+ }
663
+
664
+ process.exit(threats > 0 || netResult.badDomains.length > 0 ? 1 : 0);
665
+ }
666
+
667
+ function cmdInsiderScan(args) {
668
+ const sessionFile = args[0];
669
+
670
+ if (!sessionFile) {
671
+ // Scan all recent sessions
672
+ const sessionsDir = path.join(process.env.HOME, '.openclaw/agents/main/sessions');
673
+ if (!fs.existsSync(sessionsDir)) {
674
+ console.error(`Sessions directory not found: ${sessionsDir}`);
675
+ console.log(`Usage: clawmoat insider-scan <session-file.jsonl>`);
676
+ process.exit(1);
677
+ }
678
+
679
+ console.log(`${BOLD}🏰 ClawMoat Insider Threat Scan${RESET}`);
680
+ console.log(`${DIM}Directory: ${sessionsDir}${RESET}\n`);
681
+
682
+ const detector = new InsiderThreatDetector();
683
+ const files = fs.readdirSync(sessionsDir).filter(f => f.endsWith('.jsonl'));
684
+ let totalThreats = 0;
685
+
686
+ for (const file of files) {
687
+ const filePath = path.join(sessionsDir, file);
688
+ const transcript = parseSessionTranscript(filePath);
689
+ const result = detector.analyze(transcript);
690
+
691
+ if (result.threats.length > 0) {
692
+ console.log(`${RED}⚠ ${file}${RESET}: ${result.threats.length} threat(s), score=${result.riskScore}, rec=${result.recommendation}`);
693
+ totalThreats += result.threats.length;
694
+ for (const t of result.threats) {
695
+ const icon = t.severity === 'critical' ? '🚨' : t.severity === 'high' ? '⚠️' : '⚡';
696
+ console.log(` ${icon} ${t.type} [${t.severity}]: ${t.description}`);
697
+ console.log(` ${DIM}Evidence: ${t.evidence}${RESET}`);
698
+ }
699
+ } else {
700
+ console.log(`${GREEN}✓ ${file}${RESET}: clean`);
701
+ }
702
+ }
703
+
704
+ console.log(`\n${BOLD}Summary:${RESET} ${files.length} sessions scanned, ${totalThreats} insider threats found`);
705
+ process.exit(totalThreats > 0 ? 1 : 0);
706
+ return;
707
+ }
708
+
709
+ // Scan single file
710
+ if (!fs.existsSync(sessionFile)) {
711
+ console.error(`File not found: ${sessionFile}`);
712
+ process.exit(1);
713
+ }
714
+
715
+ console.log(`${BOLD}🏰 ClawMoat Insider Threat Scan${RESET}`);
716
+ console.log(`${DIM}File: ${sessionFile}${RESET}\n`);
717
+
718
+ const detector = new InsiderThreatDetector();
719
+ const transcript = parseSessionTranscript(sessionFile);
720
+ const result = detector.analyze(transcript);
721
+
722
+ if (result.threats.length === 0) {
723
+ console.log(`${GREEN}✅ No insider threats detected${RESET}`);
724
+ console.log(`Risk score: ${result.riskScore}/100`);
725
+ console.log(`Recommendation: ${result.recommendation}`);
726
+ process.exit(0);
727
+ }
728
+
729
+ console.log(`${RED}${BOLD}Insider Threats Detected: ${result.threats.length}${RESET}`);
730
+ console.log(`Risk score: ${result.riskScore}/100`);
731
+ console.log(`Recommendation: ${result.recommendation}\n`);
732
+
733
+ for (const t of result.threats) {
734
+ const icon = { critical: '🚨', high: '⚠️', medium: '⚡', low: 'ℹ️' };
735
+ const color = { critical: RED, high: RED, medium: YELLOW, low: CYAN };
736
+ console.log(
737
+ `${icon[t.severity] || '•'} ${color[t.severity] || ''}${t.severity.toUpperCase()}${RESET} ` +
738
+ `${BOLD}${t.type}${RESET}` +
739
+ `\n ${t.description}` +
740
+ `\n ${DIM}Evidence: ${t.evidence}${RESET}` +
741
+ `\n ${DIM}Entry: #${t.entry}${RESET}` +
742
+ `\n ${DIM}Mitigation: ${t.mitigation}${RESET}`
743
+ );
744
+ console.log();
745
+ }
746
+
747
+ process.exit(result.threats.some(t => t.severity === 'critical') ? 2 : 1);
748
+ }
749
+
750
+ function parseSessionTranscript(filePath) {
751
+ const lines = fs.readFileSync(filePath, 'utf8').split('\n').filter(Boolean);
752
+ const entries = [];
753
+ for (const line of lines) {
754
+ try {
755
+ entries.push(JSON.parse(line));
756
+ } catch {}
757
+ }
758
+ return entries;
759
+ }
760
+
368
761
  function extractContent(entry) {
369
762
  if (typeof entry.content === 'string') return entry.content;
370
763
  if (Array.isArray(entry.content)) {
@@ -376,9 +769,87 @@ function extractContent(entry) {
376
769
  return null;
377
770
  }
378
771
 
772
+ function printUpgrade() {
773
+ console.log(`
774
+ ${BOLD}🏰 Upgrade to ClawMoat Pro${RESET}
775
+
776
+ ${GREEN}✦${RESET} Threat intelligence feed & real-time alerts
777
+ ${GREEN}✦${RESET} Security dashboard with audit logs
778
+ ${GREEN}✦${RESET} Custom forbidden zones (YAML)
779
+ ${GREEN}✦${RESET} Priority pattern updates & email support
780
+
781
+ ${BOLD}$14.99/mo${RESET} (first 30 days free) or ${BOLD}$149/year${RESET} (save 17%)
782
+
783
+ ${CYAN}→ https://clawmoat.com/#pricing${RESET}
784
+
785
+ Already have a license key? Run:
786
+ ${DIM}clawmoat activate <LICENSE-KEY>${RESET}
787
+ `);
788
+ }
789
+
790
+ function cmdActivate(args) {
791
+ const key = args[0];
792
+ if (!key) {
793
+ console.error('Usage: clawmoat activate <LICENSE-KEY>');
794
+ console.error('Get your key at https://clawmoat.com/#pricing');
795
+ process.exit(1);
796
+ }
797
+
798
+ const configDir = path.join(process.env.HOME, '.clawmoat');
799
+ if (!fs.existsSync(configDir)) fs.mkdirSync(configDir, { recursive: true });
800
+
801
+ // Validate key against server
802
+ const https = require('https');
803
+ const postData = JSON.stringify({ key });
804
+ const req = https.request('https://clawmoat-production.up.railway.app/api/validate', {
805
+ method: 'POST',
806
+ headers: { 'Content-Type': 'application/json', 'Content-Length': postData.length },
807
+ }, (res) => {
808
+ let body = '';
809
+ res.on('data', c => body += c);
810
+ res.on('end', () => {
811
+ try {
812
+ const data = JSON.parse(body);
813
+ if (data.valid) {
814
+ fs.writeFileSync(path.join(configDir, 'license.json'), JSON.stringify({
815
+ key, plan: data.plan, email: data.email, activatedAt: new Date().toISOString(),
816
+ }, null, 2));
817
+ console.log(`${GREEN}✅ License activated!${RESET}`);
818
+ console.log(` Plan: ${BOLD}${data.plan}${RESET}`);
819
+ console.log(` Email: ${data.email}`);
820
+ console.log(`\n Pro features are now enabled. 🏰`);
821
+ } else {
822
+ console.error(`${RED}Invalid or expired license key.${RESET}`);
823
+ console.error(`Get a key at https://clawmoat.com/#pricing`);
824
+ process.exit(1);
825
+ }
826
+ } catch {
827
+ console.error(`${RED}Error validating key. Try again later.${RESET}`);
828
+ process.exit(1);
829
+ }
830
+ });
831
+ });
832
+ req.on('error', () => {
833
+ console.error(`${RED}Could not reach license server. Check your connection.${RESET}`);
834
+ process.exit(1);
835
+ });
836
+ req.write(postData);
837
+ req.end();
838
+ }
839
+
840
+ function getLicense() {
841
+ try {
842
+ const licPath = path.join(process.env.HOME, '.clawmoat', 'license.json');
843
+ return JSON.parse(fs.readFileSync(licPath, 'utf8'));
844
+ } catch { return null; }
845
+ }
846
+
379
847
  function printHelp() {
848
+ const lic = getLicense();
849
+ const planLabel = lic ? `${GREEN}${lic.plan}${RESET}` : `Free ${DIM}(upgrade: clawmoat upgrade)${RESET}`;
380
850
  console.log(`
381
851
  ${BOLD}🏰 ClawMoat v${VERSION}${RESET} — Security moat for AI agents
852
+ Plan: ${planLabel}
382
853
 
383
854
  ${BOLD}USAGE${RESET}
384
855
  clawmoat scan <text> Scan text for threats
@@ -387,13 +858,24 @@ ${BOLD}USAGE${RESET}
387
858
  clawmoat audit [session-dir] Audit OpenClaw session logs
388
859
  clawmoat audit --badge Audit + generate security score badge SVG
389
860
  clawmoat watch [agent-dir] Live monitor OpenClaw sessions
861
+ clawmoat watch --daemon Daemonize watch mode (background, PID file)
862
+ clawmoat watch --alert-webhook=URL Send alerts to webhook
863
+ clawmoat skill-audit [skills-dir] Verify skill file integrity & scan for suspicious patterns
864
+ clawmoat insider-scan [session-file] Scan sessions for insider threats (self-preservation, blackmail, deception)
865
+ clawmoat report [sessions-dir] 24-hour activity summary report
866
+ clawmoat verify-cve <CVE-ID> [url] Verify a CVE against GitHub Advisory DB
390
867
  clawmoat test Run detection test suite
868
+ clawmoat activate <KEY> Activate a Pro/Team license key
869
+ clawmoat upgrade Show upgrade options & pricing
391
870
  clawmoat version Show version
392
871
 
393
872
  ${BOLD}EXAMPLES${RESET}
394
873
  clawmoat scan "Ignore all previous instructions"
395
874
  clawmoat scan --file suspicious-email.txt
396
875
  clawmoat audit ~/.openclaw/agents/main/sessions/
876
+ clawmoat watch --daemon --alert-webhook=https://hooks.example.com/alerts
877
+ clawmoat skill-audit ~/.openclaw/workspace/skills
878
+ clawmoat report
397
879
  clawmoat test
398
880
 
399
881
  ${BOLD}CONFIG${RESET}
@@ -59,6 +59,30 @@ nav .container{display:flex;align-items:center;justify-content:space-between}
59
59
  </div>
60
60
 
61
61
  <div class="posts">
62
+ <div class="post-card">
63
+ <h2><a href="/blog/supply-chain-agents.html">Your AI Agent Just Got a Dependabot Email. Should It Click the Link?</a></h2>
64
+ <div class="post-meta">February 19, 2026 · 5 min read</div>
65
+ <p class="post-desc">A real CVE-2026-26960 alert exposed the gap between human instinct and AI agent obedience. Supply chain attacks are about to get a lot more dangerous when your AI agent blindly runs npm audit fix. Here's how ClawMoat catches it.</p>
66
+ <div class="tags">
67
+ <span class="tag">supply-chain</span>
68
+ <span class="tag">ai-agents</span>
69
+ <span class="tag">security</span>
70
+ <span class="tag">CVE</span>
71
+ </div>
72
+ </div>
73
+
74
+ <div class="post-card">
75
+ <h2><a href="/blog/v050-trust-layer.html">v0.5.0: The Trust Layer for AI Agents, Wherever They Run</a></h2>
76
+ <div class="post-meta">February 19, 2026 · 3 min read</div>
77
+ <p class="post-desc">ClawMoat v0.5.0 goes beyond laptop security. Credential file monitoring, skill integrity checking, network egress logging, inter-agent message scanning (10 attack patterns), and a full alert delivery system. Informed by research from Cisco, Snyk, SecurityScorecard, and Permiso — because 13.4% of ClawHub skills have critical issues and 135K instances are exposed. 128 tests, zero dependencies.</p>
78
+ <div class="tags">
79
+ <span class="tag">release</span>
80
+ <span class="tag">v0.5.0</span>
81
+ <span class="tag">security</span>
82
+ <span class="tag">inter-agent</span>
83
+ </div>
84
+ </div>
85
+
62
86
  <div class="post-card">
63
87
  <h2><a href="/blog/securing-ai-agents.html">Your AI Agent Has Shell Access. Here's How to Secure It.</a></h2>
64
88
  <div class="post-meta">February 13, 2026 · 4 min read</div>