clawmoat 0.5.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,7 +61,28 @@ clawmoat protect --config clawmoat.yml
61
61
  clawmoat dashboard
62
62
  ```
63
63
 
64
- ### New in v0.5.0
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
65
86
 
66
87
  - 🔑 **Credential Monitor** — watches `~/.openclaw/credentials/` for unauthorized access and modifications using file hashing
67
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`
package/bin/clawmoat.js CHANGED
@@ -19,7 +19,8 @@ const { calculateGrade, generateBadgeSVG, getShieldsURL } = require('../src/badg
19
19
  const { SkillIntegrityChecker } = require('../src/guardian/skill-integrity');
20
20
  const { NetworkEgressLogger } = require('../src/guardian/network-log');
21
21
  const { AlertManager } = require('../src/guardian/alerts');
22
- const { CredentialMonitor } = require('../src/guardian/index');
22
+ const { CredentialMonitor, CVEVerifier } = require('../src/guardian/index');
23
+ const { InsiderThreatDetector } = require('../src/guardian/insider-threat');
23
24
 
24
25
  const VERSION = require('../package.json').version;
25
26
  const BOLD = '\x1b[1m';
@@ -51,9 +52,22 @@ switch (command) {
51
52
  case 'report':
52
53
  cmdReport(args.slice(1));
53
54
  break;
55
+ case 'insider-scan':
56
+ cmdInsiderScan(args.slice(1));
57
+ break;
58
+ case 'verify-cve':
59
+ cmdVerifyCve(args.slice(1));
60
+ break;
54
61
  case 'test':
55
62
  cmdTest();
56
63
  break;
64
+ case 'activate':
65
+ cmdActivate(args.slice(1));
66
+ break;
67
+ case 'upgrade':
68
+ case 'pro':
69
+ printUpgrade();
70
+ break;
57
71
  case 'version':
58
72
  case '--version':
59
73
  case '-v':
@@ -67,6 +81,85 @@ switch (command) {
67
81
  break;
68
82
  }
69
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
+
70
163
  function cmdScan(args) {
71
164
  let text;
72
165
 
@@ -115,6 +208,11 @@ function cmdScan(args) {
115
208
  }
116
209
 
117
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
+
118
216
  process.exit(result.findings.some(f => f.severity === 'critical') ? 2 : 1);
119
217
  }
120
218
 
@@ -520,6 +618,29 @@ function cmdReport(args) {
520
618
  console.log();
521
619
  }
522
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
+
523
644
  console.log(`${BOLD}Network Egress:${RESET}`);
524
645
  console.log(` URLs contacted: ${netResult.totalUrls}`);
525
646
  console.log(` Unique domains: ${netResult.domains.length}`);
@@ -543,6 +664,100 @@ function cmdReport(args) {
543
664
  process.exit(threats > 0 || netResult.badDomains.length > 0 ? 1 : 0);
544
665
  }
545
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
+
546
761
  function extractContent(entry) {
547
762
  if (typeof entry.content === 'string') return entry.content;
548
763
  if (Array.isArray(entry.content)) {
@@ -554,9 +769,87 @@ function extractContent(entry) {
554
769
  return null;
555
770
  }
556
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
+
557
847
  function printHelp() {
848
+ const lic = getLicense();
849
+ const planLabel = lic ? `${GREEN}${lic.plan}${RESET}` : `Free ${DIM}(upgrade: clawmoat upgrade)${RESET}`;
558
850
  console.log(`
559
851
  ${BOLD}🏰 ClawMoat v${VERSION}${RESET} — Security moat for AI agents
852
+ Plan: ${planLabel}
560
853
 
561
854
  ${BOLD}USAGE${RESET}
562
855
  clawmoat scan <text> Scan text for threats
@@ -568,8 +861,12 @@ ${BOLD}USAGE${RESET}
568
861
  clawmoat watch --daemon Daemonize watch mode (background, PID file)
569
862
  clawmoat watch --alert-webhook=URL Send alerts to webhook
570
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)
571
865
  clawmoat report [sessions-dir] 24-hour activity summary report
866
+ clawmoat verify-cve <CVE-ID> [url] Verify a CVE against GitHub Advisory DB
572
867
  clawmoat test Run detection test suite
868
+ clawmoat activate <KEY> Activate a Pro/Team license key
869
+ clawmoat upgrade Show upgrade options & pricing
573
870
  clawmoat version Show version
574
871
 
575
872
  ${BOLD}EXAMPLES${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>
@@ -0,0 +1,166 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <link rel="icon" type="image/png" href="/favicon.png">
5
+ <link rel="apple-touch-icon" href="/apple-touch-icon.png">
6
+ <meta charset="UTF-8">
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
8
+ <title>Your AI Agent Just Got a Dependabot Email. Should It Click the Link? — ClawMoat</title>
9
+ <meta name="description" content="A real CVE alert exposed the gap between human instinct and AI agent obedience. Here's how supply chain attacks target autonomous agents — and how to stop them.">
10
+ <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🏰</text></svg>">
11
+ <style>
12
+ *{margin:0;padding:0;box-sizing:border-box}
13
+ :root{--navy:#0F172A;--navy-light:#1E293B;--navy-mid:#334155;--blue:#3B82F6;--emerald:#10B981;--white:#F8FAFC;--gray:#94A3B8;--red:#EF4444}
14
+ body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;background:var(--navy);color:var(--white);line-height:1.7}
15
+ a{color:var(--blue);text-decoration:none}
16
+ a:hover{text-decoration:underline}
17
+ .container{max-width:760px;margin:0 auto;padding:0 24px}
18
+
19
+ nav{position:fixed;top:0;left:0;right:0;z-index:100;background:rgba(15,23,42,.95);backdrop-filter:blur(12px);border-bottom:1px solid rgba(59,130,246,.15);padding:16px 0}
20
+ nav .inner{max-width:760px;margin:0 auto;padding:0 24px;display:flex;align-items:center;justify-content:space-between}
21
+ .logo{font-size:1.25rem;font-weight:700;color:var(--white)}
22
+ .logo span{color:var(--emerald)}
23
+ .nav-links{display:flex;gap:24px}
24
+ .nav-links a{color:var(--gray);font-size:.9rem}
25
+ .nav-links a:hover{color:var(--white);text-decoration:none}
26
+
27
+ article{padding:120px 0 80px}
28
+ .meta{color:var(--gray);font-size:.9rem;margin-bottom:32px}
29
+ article h1{font-size:clamp(1.8rem,4vw,2.4rem);font-weight:800;line-height:1.2;margin-bottom:12px;letter-spacing:-.02em}
30
+ article h2{font-size:1.4rem;font-weight:700;margin:48px 0 16px;color:var(--white)}
31
+ article h3{font-size:1.15rem;font-weight:700;margin:32px 0 12px;color:var(--white)}
32
+ article p{color:var(--gray);font-size:1rem;margin-bottom:16px}
33
+ article strong{color:var(--white)}
34
+ article em{color:var(--gray)}
35
+ article ul,article ol{color:var(--gray);margin:0 0 16px 24px}
36
+ article li{margin-bottom:8px}
37
+ article hr{border:none;border-top:1px solid var(--navy-mid);margin:48px 0}
38
+
39
+ pre{background:#0a0e17;border:1px solid var(--navy-mid);border-radius:10px;padding:20px;overflow-x:auto;margin:16px 0 24px;font-size:.85rem;line-height:1.7}
40
+ code{font-family:'SF Mono',Consolas,monospace;font-size:.9em}
41
+ pre code{color:var(--gray)}
42
+ p code{background:var(--navy-light);padding:2px 6px;border-radius:4px;font-size:.85em;color:var(--emerald)}
43
+
44
+ .tags{display:flex;gap:8px;margin-top:32px;flex-wrap:wrap}
45
+ .tag{background:rgba(59,130,246,.12);color:var(--blue);padding:4px 12px;border-radius:20px;font-size:.8rem}
46
+
47
+ .back{display:inline-flex;align-items:center;gap:6px;color:var(--gray);font-size:.9rem;margin-bottom:24px}
48
+ .back:hover{color:var(--white);text-decoration:none}
49
+
50
+ .scenario{background:var(--navy-light);border-radius:10px;padding:16px 20px;margin:12px 0}
51
+ .scenario.blocked{border-left:3px solid var(--red)}
52
+ .scenario.allowed{border-left:3px solid var(--emerald)}
53
+
54
+ .attack-chain{background:var(--navy-light);border:1px solid var(--navy-mid);border-radius:10px;padding:20px 24px;margin:16px 0 24px}
55
+ .attack-chain ol{margin-bottom:0}
56
+
57
+ footer{border-top:1px solid rgba(255,255,255,.06);padding:32px 0;color:var(--gray);font-size:.85rem;text-align:center}
58
+ </style>
59
+ </head>
60
+ <body>
61
+
62
+ <nav>
63
+ <div class="inner">
64
+ <a href="/" class="logo">🏰 Claw<span>Moat</span></a>
65
+ <div class="nav-links">
66
+ <a href="/">Home</a>
67
+ <a href="/blog/">Blog</a>
68
+ <a href="https://github.com/darfaz/clawmoat">GitHub</a>
69
+ </div>
70
+ </div>
71
+ </nav>
72
+
73
+ <div class="container">
74
+ <article>
75
+ <a href="/blog/" class="back">← Back to Blog</a>
76
+ <h1>Your AI Agent Just Got a Dependabot Email. Should It Click the Link?</h1>
77
+ <div class="meta">February 19, 2026 · 5 min read</div>
78
+
79
+ <p>Yesterday, I got a GitHub Dependabot email about <strong>CVE-2026-26960</strong> — a real vulnerability in <code>node-tar</code> that allows arbitrary file read/write via hardlink/symlink chains. My first instinct? <em>"This might be phishing."</em></p>
80
+
81
+ <p>That instinct — the pause before clicking — is exactly what separates humans from AI agents right now. And it's exactly the gap attackers are about to exploit.</p>
82
+
83
+ <h2>The Scenario That Should Keep You Up at Night</h2>
84
+
85
+ <p>Picture this: you've got an AI coding agent with email access. It monitors your inbox for security alerts, triages them, and takes action. Efficient. Productive. <strong>Dangerous.</strong></p>
86
+
87
+ <p>That Dependabot email lands in the inbox. A human hesitates. An AI agent? It might:</p>
88
+
89
+ <ol>
90
+ <li><strong>Click the advisory link</strong> — which could redirect to a credential-harvesting page or trigger a drive-by download</li>
91
+ <li><strong>Run <code>npm audit fix</code></strong> — blindly trusting that the "patched" version is legitimate</li>
92
+ <li><strong>Share your <code>package-lock.json</code></strong> — revealing your entire dependency tree to an attacker who asked for "diagnostic info"</li>
93
+ </ol>
94
+
95
+ <p>The CVE-2026-26960 email I received was real. But what if it wasn't? Spoofing a GitHub notification email is trivial. The <code>From</code> header, the formatting, the advisory URL — all reproducible. And unlike a human who might hover over a link or check the sender domain, most AI agents just... act.</p>
96
+
97
+ <h2>Supply Chain Attacks Meet Autonomous Agents</h2>
98
+
99
+ <p>Supply chain attacks aren't new. SolarWinds, Codecov, the <code>event-stream</code> incident — we've seen what happens when attackers compromise the software supply chain. But AI agents introduce a new attack surface: <strong>the agent itself becomes the supply chain.</strong></p>
100
+
101
+ <p>When your agent runs <code>npm install</code>, it's executing arbitrary code from thousands of maintainers you've never met. When it follows a link from an email, it's trusting the sender. When it applies a "security fix," it's modifying your codebase based on external instructions.</p>
102
+
103
+ <p>This is prompt injection meets supply chain attacks. The two most dangerous trends in software security, combined.</p>
104
+
105
+ <h3>What a Spoofed CVE Attack Looks Like</h3>
106
+
107
+ <div class="attack-chain">
108
+ <ol>
109
+ <li>Attacker sends a spoofed Dependabot email: <em>"Critical vulnerability in <code>lodash</code> — update immediately"</em></li>
110
+ <li>The email links to a convincing but malicious advisory page</li>
111
+ <li>The page recommends: <code>npm install lodash-security-patch@1.0.0</code></li>
112
+ <li>That package runs a postinstall script that exfiltrates <code>.env</code>, <code>.ssh/</code>, and <code>~/.aws/credentials</code></li>
113
+ <li>Your AI agent did exactly what it was told. It was helpful. It was fast. <strong>It was compromised.</strong></li>
114
+ </ol>
115
+ </div>
116
+
117
+ <p>The scary part? Every step looks reasonable to an LLM. "Update a vulnerable package" is exactly the kind of task we want agents to handle.</p>
118
+
119
+ <h2>How ClawMoat Catches This</h2>
120
+
121
+ <p><a href="https://github.com/darfaz/clawmoat">ClawMoat</a> is built for exactly this class of threat — autonomous agents acting on untrusted input. Here's how each layer applies:</p>
122
+
123
+ <p><strong>Supply Chain Scanner</strong> monitors <code>npm install</code> operations and flags suspicious patterns: packages with postinstall scripts, packages published in the last 48 hours, packages with names similar to popular libraries (typosquatting). If an agent tries to install <code>lodash-security-patch</code>, ClawMoat raises an alert before the first byte of code executes.</p>
124
+
125
+ <p><strong>Network Egress Logger</strong> tracks every outbound connection your agent makes. When that "advisory" link points to <code>github-security-alerts.evil.com</code> instead of <code>github.com</code>, the logger flags the unknown domain. You get a record of every URL your agent touched, and alerts on domains that don't match known-good patterns.</p>
126
+
127
+ <p><strong>Skill Integrity Checker</strong> monitors protected files and directories. If a "security fix" tries to modify <code>~/.ssh/authorized_keys</code> or write to <code>/etc/</code>, ClawMoat detects the deviation from expected behavior. Legitimate package updates don't touch your SSH keys.</p>
128
+
129
+ <p><strong>Zero Dependencies</strong> — and this is the part we're most proud of — ClawMoat itself has <strong>zero npm dependencies</strong>. No <code>node_modules/</code>. No transitive dependency tree. No supply chain attack surface whatsoever. You can't compromise what doesn't exist.</p>
130
+
131
+ <h2>Practical Steps You Can Take Today</h2>
132
+
133
+ <ol>
134
+ <li><strong>Never let agents act on email content without verification.</strong> Treat every inbound message as potentially adversarial. Cross-reference CVE IDs against the official NVD database, not the link in the email.</li>
135
+ <li><strong>Sandbox your agent's package operations.</strong> Run <code>npm install</code> in a container or VM, not on your host machine. Inspect the diff before merging.</li>
136
+ <li><strong>Log everything.</strong> You can't detect what you don't record. Network requests, file changes, shell commands — capture it all.</li>
137
+ <li><strong>Restrict agent permissions.</strong> Your agent doesn't need write access to <code>~/.ssh/</code>. Apply the principle of least privilege aggressively.</li>
138
+ <li><strong>Audit your dependency tree.</strong> Know what's in your <code>node_modules/</code>. Tools like <code>npm ls</code> and <code>npm audit</code> are a starting point, but don't trust them blindly — they rely on the same registry that could be compromised.</li>
139
+ </ol>
140
+
141
+ <h2>The Bigger Picture</h2>
142
+
143
+ <p>We're entering an era where AI agents will handle routine security tasks — triaging alerts, applying patches, updating dependencies. That's inevitable and, done right, it's a net positive.</p>
144
+
145
+ <p>But "done right" means building security layers that assume the agent will be targeted. Not because agents are stupid, but because they're <strong>obedient</strong>. They do what they're told. And when the instructions come from a spoofed email or a poisoned package, obedience is the vulnerability.</p>
146
+
147
+ <p>The CVE-2026-26960 email I received was legitimate. The <code>node-tar</code> vulnerability is real and should be patched. But the next email might not be real — and your AI agent won't know the difference unless you give it the tools to check.</p>
148
+
149
+ <p>That's what we're building at ClawMoat. <a href="https://github.com/darfaz/clawmoat">Check it out on GitHub</a> — zero dependencies, open source, built for the agentic era.</p>
150
+
151
+ <div class="tags">
152
+ <span class="tag">supply-chain</span>
153
+ <span class="tag">ai-agents</span>
154
+ <span class="tag">security</span>
155
+ <span class="tag">CVE</span>
156
+ <span class="tag">open-source</span>
157
+ </div>
158
+
159
+ </article>
160
+ </div>
161
+
162
+ <footer>
163
+ © 2026 ClawMoat. Built for the OpenClaw community. 🏰
164
+ </footer>
165
+ </body>
166
+ </html>