clawmoat 0.2.1 → 0.5.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/CHANGELOG.md +32 -0
- package/Dockerfile +22 -0
- package/README.md +144 -5
- package/SECURITY.md +63 -0
- package/bin/clawmoat.js +186 -1
- package/docs/ai-agent-security-scanner.html +691 -0
- package/docs/apple-touch-icon.png +0 -0
- package/docs/blog/host-guardian-launch.html +345 -0
- package/docs/blog/host-guardian-launch.md +249 -0
- package/docs/blog/index.html +2 -0
- package/docs/blog/langchain-security-tutorial.html +319 -0
- package/docs/blog/owasp-agentic-ai-top10.html +2 -0
- package/docs/blog/securing-ai-agents.html +2 -0
- package/docs/compare.html +2 -0
- package/docs/favicon.png +0 -0
- package/docs/icon-192.png +0 -0
- package/docs/index.html +258 -65
- package/docs/integrations/langchain.html +2 -0
- package/docs/integrations/openai.html +2 -0
- package/docs/integrations/openclaw.html +2 -0
- package/docs/logo.png +0 -0
- package/docs/logo.svg +60 -0
- package/docs/mark-with-moat.svg +33 -0
- package/docs/mark.png +0 -0
- package/docs/mark.svg +30 -0
- package/docs/og-image.png +0 -0
- package/docs/playground.html +440 -0
- package/docs/positioning-v2.md +155 -0
- package/docs/report-demo.html +399 -0
- package/docs/thanks.html +2 -0
- package/examples/github-action-workflow.yml +94 -0
- package/logo.png +0 -0
- package/logo.svg +60 -0
- package/mark-with-moat.svg +33 -0
- package/mark.png +0 -0
- package/mark.svg +30 -0
- package/package.json +1 -1
- package/server/index.js +9 -5
- package/skill/README.md +57 -0
- package/skill/SKILL.md +49 -30
- package/skill/scripts/audit.sh +28 -0
- package/skill/scripts/scan.sh +32 -0
- package/skill/scripts/test.sh +13 -0
- package/src/guardian/alerts.js +138 -0
- package/src/guardian/index.js +686 -0
- package/src/guardian/network-log.js +281 -0
- package/src/guardian/skill-integrity.js +290 -0
- package/src/index.js +37 -0
- package/src/middleware/openclaw.js +76 -1
- package/src/scanners/excessive-agency.js +88 -0
- package/wiki/Architecture.md +103 -0
- package/wiki/CLI-Reference.md +167 -0
- package/wiki/FAQ.md +135 -0
- package/wiki/Home.md +70 -0
- package/wiki/Policy-Engine.md +229 -0
- package/wiki/Scanner-Modules.md +224 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [0.3.0] - 2025-02-18
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Excessive Agency Scanner (ASI02/ASI03)**: Advanced security scanning for AI agent behaviors with comprehensive test coverage
|
|
9
|
+
- **OpenClaw Skill Integration**: Security scanning capabilities specifically designed for AI agent sessions
|
|
10
|
+
- **CI/CD Workflow**: Automated testing and continuous integration setup
|
|
11
|
+
- **SVG Brand Assets**: New logo, mark, and mark-with-moat SVG assets for better branding
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- **Renamed Pro Skill to Security Kit**: Better reflects the comprehensive security features
|
|
15
|
+
- **New Pricing Structure**:
|
|
16
|
+
- Pro Skill (Security Kit) now available as one-time $29 purchase
|
|
17
|
+
- Shield and Team subscriptions with 30-day trial period
|
|
18
|
+
- 14-day money-back guarantee
|
|
19
|
+
- **Improved Documentation**: Enhanced README with better feature descriptions and setup instructions
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
- Checkout system now points to live Railway URL for better reliability
|
|
23
|
+
- Various styling improvements for better user experience
|
|
24
|
+
|
|
25
|
+
### UI/UX Improvements
|
|
26
|
+
- Bigger, transparent SVG logo with left-aligned navigation
|
|
27
|
+
- More space between logo and navigation links
|
|
28
|
+
- Narrower pill-shaped "Get Access" button
|
|
29
|
+
- Enhanced "Join Waitlist" and "Get Started" button styling and functionality
|
|
30
|
+
|
|
31
|
+
## [0.2.1] - Previous Release
|
|
32
|
+
- Initial stable release with core security features
|
package/Dockerfile
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
FROM node:20-alpine
|
|
2
|
+
|
|
3
|
+
# Set working directory
|
|
4
|
+
WORKDIR /app
|
|
5
|
+
|
|
6
|
+
# Install dependencies
|
|
7
|
+
COPY package.json ./
|
|
8
|
+
RUN npm install --omit=dev
|
|
9
|
+
|
|
10
|
+
# Copy source code
|
|
11
|
+
COPY . .
|
|
12
|
+
|
|
13
|
+
# Ensure CLI is executable
|
|
14
|
+
RUN chmod +x bin/clawmoat.js
|
|
15
|
+
|
|
16
|
+
# Environment variables
|
|
17
|
+
ENV NODE_ENV=production
|
|
18
|
+
ENV CLAWMOAT_POLICY=strict
|
|
19
|
+
|
|
20
|
+
# CLI entrypoint
|
|
21
|
+
ENTRYPOINT ["node", "bin/clawmoat.js"]
|
|
22
|
+
|
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="
|
|
2
|
+
<img src="logo.png" alt="ClawMoat" width="400">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
-
<h1 align="center"
|
|
5
|
+
<h1 align="center">ClawMoat</h1>
|
|
6
6
|
<p align="center"><strong>Security moat for AI agents</strong></p>
|
|
7
7
|
<p align="center">Runtime protection against prompt injection, tool misuse, and data exfiltration.</p>
|
|
8
8
|
|
|
@@ -20,6 +20,21 @@
|
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
+
## Why ClawMoat?
|
|
24
|
+
|
|
25
|
+
Building with **LangChain**, **CrewAI**, **AutoGen**, or **OpenAI Agents**? Your agents have real capabilities — shell access, file I/O, web browsing, email. That's powerful, but one prompt injection in an email or scraped webpage can hijack your agent into exfiltrating secrets, running malicious commands, or poisoning its own memory.
|
|
26
|
+
|
|
27
|
+
**ClawMoat is the missing security layer.** Drop it in front of your agent and get:
|
|
28
|
+
|
|
29
|
+
- 🛡️ **Prompt injection detection** — multi-layer scanning catches instruction overrides, delimiter attacks, encoded payloads
|
|
30
|
+
- 🔐 **Secret & PII scanning** — 30+ credential patterns + PII detection on outbound text
|
|
31
|
+
- ⚡ **Zero dependencies** — pure Node.js, no ML models to download, sub-millisecond scans
|
|
32
|
+
- 🔧 **CI/CD ready** — GitHub Actions workflow included, fail builds on security violations
|
|
33
|
+
- 📋 **Policy engine** — YAML-based rules for shell, file, browser, and network access
|
|
34
|
+
- 🏰 **OWASP coverage** — maps to all 10 risks in the OWASP Top 10 for Agentic AI
|
|
35
|
+
|
|
36
|
+
**Works with any agent framework.** ClawMoat scans text — it doesn't care if it came from LangChain, CrewAI, AutoGen, or your custom agent.
|
|
37
|
+
|
|
23
38
|
## The Problem
|
|
24
39
|
|
|
25
40
|
AI agents have shell access, browser control, email, and file system access. A single prompt injection in an email or webpage can hijack your agent into exfiltrating data, running malicious commands, or impersonating you.
|
|
@@ -46,6 +61,16 @@ clawmoat protect --config clawmoat.yml
|
|
|
46
61
|
clawmoat dashboard
|
|
47
62
|
```
|
|
48
63
|
|
|
64
|
+
### New in v0.5.0
|
|
65
|
+
|
|
66
|
+
- 🔑 **Credential Monitor** — watches `~/.openclaw/credentials/` for unauthorized access and modifications using file hashing
|
|
67
|
+
- 🧩 **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`
|
|
68
|
+
- 🌐 **Network Egress Logger** — parses session logs for all outbound URLs, maintains domain allowlists, flags known-bad domains (webhook.site, ngrok, etc.)
|
|
69
|
+
- 🚨 **Alert Delivery System** — unified alerts via console, file (audit.log), or webhook with severity levels and 5-minute rate limiting
|
|
70
|
+
- 🤝 **Inter-Agent Message Scanner** — heightened-sensitivity scanning for agent-to-agent messages detecting impersonation, concealment, credential exfiltration, and safety bypasses
|
|
71
|
+
- 📊 **Activity Reports** — `clawmoat report` generates 24h summaries of agent activity, tool usage, and network egress
|
|
72
|
+
- 👻 **Daemon Mode** — `clawmoat watch --daemon` runs in background with PID file; `--alert-webhook=URL` for remote alerting
|
|
73
|
+
|
|
49
74
|
### As an OpenClaw Skill
|
|
50
75
|
|
|
51
76
|
```bash
|
|
@@ -54,6 +79,36 @@ openclaw skills add clawmoat
|
|
|
54
79
|
|
|
55
80
|
Automatically scans inbound messages, audits tool calls, blocks violations, and logs events.
|
|
56
81
|
|
|
82
|
+
## GitHub Action
|
|
83
|
+
|
|
84
|
+
Add ClawMoat to your CI pipeline to catch prompt injection and secret leaks before they merge:
|
|
85
|
+
|
|
86
|
+
```yaml
|
|
87
|
+
# .github/workflows/clawmoat.yml
|
|
88
|
+
name: ClawMoat Scan
|
|
89
|
+
on: [pull_request]
|
|
90
|
+
|
|
91
|
+
permissions:
|
|
92
|
+
contents: read
|
|
93
|
+
pull-requests: write
|
|
94
|
+
|
|
95
|
+
jobs:
|
|
96
|
+
scan:
|
|
97
|
+
runs-on: ubuntu-latest
|
|
98
|
+
steps:
|
|
99
|
+
- uses: actions/checkout@v4
|
|
100
|
+
- uses: actions/setup-node@v4
|
|
101
|
+
with:
|
|
102
|
+
node-version: '20'
|
|
103
|
+
- uses: darfaz/clawmoat/.github/actions/scan@main
|
|
104
|
+
with:
|
|
105
|
+
paths: '.'
|
|
106
|
+
fail-on: 'critical' # critical | high | medium | low | none
|
|
107
|
+
format: 'summary'
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Results appear as PR comments and job summaries. See [`examples/github-action-workflow.yml`](examples/github-action-workflow.yml) for more patterns.
|
|
111
|
+
|
|
57
112
|
## Features
|
|
58
113
|
|
|
59
114
|
| Feature | Description | Status |
|
|
@@ -63,7 +118,90 @@ Automatically scans inbound messages, audits tool calls, blocks violations, and
|
|
|
63
118
|
| 📋 **Policy Engine** | YAML rules for shell, files, browser, network | ✅ v0.1 |
|
|
64
119
|
| 🕵️ **Jailbreak Detection** | Heuristic + classifier pipeline | ✅ v0.1 |
|
|
65
120
|
| 📊 **Session Audit Trail** | Full tamper-evident action log | ✅ v0.1 |
|
|
66
|
-
| 🧠 **Behavioral Analysis** | Anomaly detection on agent behavior | 🔜 v0.
|
|
121
|
+
| 🧠 **Behavioral Analysis** | Anomaly detection on agent behavior | 🔜 v0.5 |
|
|
122
|
+
| 🏠 **Host Guardian** | Runtime security for laptop-hosted agents | ✅ v0.4 |
|
|
123
|
+
|
|
124
|
+
## 🏠 Host Guardian — Security for Laptop-Hosted Agents
|
|
125
|
+
|
|
126
|
+
Running an AI agent on your actual laptop? **Host Guardian** is the trust layer that makes it safe. It monitors every file access, command, and network request — blocking dangerous actions before they execute.
|
|
127
|
+
|
|
128
|
+
### Permission Tiers
|
|
129
|
+
|
|
130
|
+
Start locked down, open up as trust grows:
|
|
131
|
+
|
|
132
|
+
| Mode | File Read | File Write | Shell | Network | Use Case |
|
|
133
|
+
|------|-----------|------------|-------|---------|----------|
|
|
134
|
+
| **Observer** | Workspace only | ❌ | ❌ | ❌ | Testing a new agent |
|
|
135
|
+
| **Worker** | Workspace only | Workspace only | Safe commands | Fetch only | Daily use |
|
|
136
|
+
| **Standard** | System-wide | Workspace only | Most commands | ✅ | Power users |
|
|
137
|
+
| **Full** | Everything | Everything | Everything | ✅ | Audit-only mode |
|
|
138
|
+
|
|
139
|
+
### Quick Start
|
|
140
|
+
|
|
141
|
+
```js
|
|
142
|
+
const { HostGuardian } = require('clawmoat');
|
|
143
|
+
|
|
144
|
+
const guardian = new HostGuardian({ mode: 'standard' });
|
|
145
|
+
|
|
146
|
+
// Check before every tool call
|
|
147
|
+
guardian.check('read', { path: '~/.ssh/id_rsa' });
|
|
148
|
+
// => { allowed: false, reason: 'Protected zone: SSH keys', severity: 'critical' }
|
|
149
|
+
|
|
150
|
+
guardian.check('exec', { command: 'rm -rf /' });
|
|
151
|
+
// => { allowed: false, reason: 'Dangerous command blocked: Recursive force delete', severity: 'critical' }
|
|
152
|
+
|
|
153
|
+
guardian.check('exec', { command: 'git status' });
|
|
154
|
+
// => { allowed: true, decision: 'allow' }
|
|
155
|
+
|
|
156
|
+
// Runtime mode switching
|
|
157
|
+
guardian.setMode('worker'); // Lock down further
|
|
158
|
+
|
|
159
|
+
// Full audit trail
|
|
160
|
+
console.log(guardian.report());
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### What It Protects
|
|
164
|
+
|
|
165
|
+
**🔒 Forbidden Zones** (always blocked):
|
|
166
|
+
- SSH keys, GPG keys, AWS/GCloud/Azure credentials
|
|
167
|
+
- Browser cookies & login data, password managers
|
|
168
|
+
- Crypto wallets, `.env` files, `.netrc`
|
|
169
|
+
- System files (`/etc/shadow`, `/etc/sudoers`)
|
|
170
|
+
|
|
171
|
+
**⚡ Dangerous Commands** (blocked by tier):
|
|
172
|
+
- Destructive: `rm -rf`, `mkfs`, `dd`
|
|
173
|
+
- Escalation: `sudo`, `chmod +s`, `su -`
|
|
174
|
+
- Network: reverse shells, `ngrok`, `curl | bash`
|
|
175
|
+
- Persistence: `crontab`, modifying `.bashrc`
|
|
176
|
+
- Exfiltration: `curl --data`, `scp` to unknown hosts
|
|
177
|
+
|
|
178
|
+
**📋 Audit Trail**: Every action recorded with timestamps, verdicts, and reasons. Generate reports anytime.
|
|
179
|
+
|
|
180
|
+
### Configuration
|
|
181
|
+
|
|
182
|
+
```js
|
|
183
|
+
const guardian = new HostGuardian({
|
|
184
|
+
mode: 'worker',
|
|
185
|
+
workspace: '~/.openclaw/workspace',
|
|
186
|
+
safeZones: ['~/projects', '~/Documents'], // Additional allowed paths
|
|
187
|
+
forbiddenZones: ['~/tax-returns'], // Custom protected paths
|
|
188
|
+
onViolation: (tool, args, verdict) => { // Alert callback
|
|
189
|
+
notify(`⚠️ Blocked: ${verdict.reason}`);
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Or via `clawmoat.yml`:
|
|
195
|
+
|
|
196
|
+
```yaml
|
|
197
|
+
guardian:
|
|
198
|
+
mode: standard
|
|
199
|
+
workspace: ~/.openclaw/workspace
|
|
200
|
+
safe_zones:
|
|
201
|
+
- ~/projects
|
|
202
|
+
forbidden_zones:
|
|
203
|
+
- ~/tax-returns
|
|
204
|
+
```
|
|
67
205
|
|
|
68
206
|
## Architecture
|
|
69
207
|
|
|
@@ -146,7 +284,7 @@ ClawMoat maps to the [OWASP Top 10 for Agentic AI (2026)](https://genai.owasp.or
|
|
|
146
284
|
| OWASP Risk | Description | ClawMoat Protection | Status |
|
|
147
285
|
|-----------|-------------|---------------------|--------|
|
|
148
286
|
| **ASI01** | Prompt Injection & Manipulation | Multi-layer injection scanning on all inbound content | ✅ |
|
|
149
|
-
| **ASI02** | Excessive Agency & Permissions |
|
|
287
|
+
| **ASI02** | Excessive Agency & Permissions | Escalation detection + policy engine enforces least-privilege | ✅ |
|
|
150
288
|
| **ASI03** | Insecure Tool Use | Command validation & argument sanitization | ✅ |
|
|
151
289
|
| **ASI04** | Insufficient Output Validation | Output scanning for secrets, PII, dangerous code | ✅ |
|
|
152
290
|
| **ASI05** | Memory & Context Poisoning | Context integrity checks on memory retrievals | 🔜 |
|
|
@@ -167,7 +305,8 @@ clawmoat/
|
|
|
167
305
|
│ │ ├── prompt-injection.js
|
|
168
306
|
│ │ ├── jailbreak.js
|
|
169
307
|
│ │ ├── secrets.js
|
|
170
|
-
│ │
|
|
308
|
+
│ │ ├── pii.js
|
|
309
|
+
│ │ └── excessive-agency.js
|
|
171
310
|
│ ├── policies/ # Policy enforcement
|
|
172
311
|
│ │ ├── engine.js
|
|
173
312
|
│ │ ├── exec.js
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
| Version | Supported |
|
|
6
|
+
|---------|--------------------|
|
|
7
|
+
| 0.1.x | ✅ Current release |
|
|
8
|
+
|
|
9
|
+
## Reporting a Vulnerability
|
|
10
|
+
|
|
11
|
+
If you discover a security vulnerability in ClawMoat, **please report it responsibly**.
|
|
12
|
+
|
|
13
|
+
### How to Report
|
|
14
|
+
|
|
15
|
+
1. **Email:** Send details to **security@clawmoat.com**
|
|
16
|
+
2. **Subject line:** `[SECURITY] Brief description`
|
|
17
|
+
3. **Include:**
|
|
18
|
+
- Description of the vulnerability
|
|
19
|
+
- Steps to reproduce
|
|
20
|
+
- Potential impact
|
|
21
|
+
- Suggested fix (if any)
|
|
22
|
+
|
|
23
|
+
### What to Expect
|
|
24
|
+
|
|
25
|
+
- **Acknowledgment** within 48 hours
|
|
26
|
+
- **Assessment** within 7 days
|
|
27
|
+
- **Fix timeline** communicated within 14 days
|
|
28
|
+
- **Credit** in the release notes (unless you prefer anonymity)
|
|
29
|
+
|
|
30
|
+
### What NOT to Do
|
|
31
|
+
|
|
32
|
+
- Do not open a public GitHub issue for security vulnerabilities
|
|
33
|
+
- Do not exploit the vulnerability beyond what's needed to demonstrate it
|
|
34
|
+
- Do not access or modify other users' data
|
|
35
|
+
|
|
36
|
+
## Scope
|
|
37
|
+
|
|
38
|
+
The following are in scope:
|
|
39
|
+
|
|
40
|
+
- **Scanner bypasses** — Attacks that evade ClawMoat's detection
|
|
41
|
+
- **Policy engine bypasses** — Tool calls that circumvent policy rules
|
|
42
|
+
- **Audit log tampering** — Ways to modify or forge audit entries
|
|
43
|
+
- **Dependency issues** — Vulnerabilities in ClawMoat's dependencies (currently: none)
|
|
44
|
+
|
|
45
|
+
The following are out of scope:
|
|
46
|
+
|
|
47
|
+
- Denial of service via large inputs (expected behavior — use input size limits)
|
|
48
|
+
- False positives/negatives in detection (please open a regular issue)
|
|
49
|
+
- Vulnerabilities in upstream LLM providers
|
|
50
|
+
|
|
51
|
+
## Security Best Practices
|
|
52
|
+
|
|
53
|
+
When using ClawMoat:
|
|
54
|
+
|
|
55
|
+
1. Keep ClawMoat updated to the latest version
|
|
56
|
+
2. Enable all relevant scanners for your use case
|
|
57
|
+
3. Use strict policy configurations in production
|
|
58
|
+
4. Review audit logs regularly
|
|
59
|
+
5. Set up alerts for critical-severity findings
|
|
60
|
+
|
|
61
|
+
## PGP Key
|
|
62
|
+
|
|
63
|
+
For encrypted communications, use our PGP key (available on request at security@clawmoat.com).
|
package/bin/clawmoat.js
CHANGED
|
@@ -16,6 +16,10 @@ 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 } = require('../src/guardian/index');
|
|
19
23
|
|
|
20
24
|
const VERSION = require('../package.json').version;
|
|
21
25
|
const BOLD = '\x1b[1m';
|
|
@@ -41,6 +45,12 @@ switch (command) {
|
|
|
41
45
|
case 'watch':
|
|
42
46
|
cmdWatch(args.slice(1));
|
|
43
47
|
break;
|
|
48
|
+
case 'skill-audit':
|
|
49
|
+
cmdSkillAudit(args.slice(1));
|
|
50
|
+
break;
|
|
51
|
+
case 'report':
|
|
52
|
+
cmdReport(args.slice(1));
|
|
53
|
+
break;
|
|
44
54
|
case 'test':
|
|
45
55
|
cmdTest();
|
|
46
56
|
break;
|
|
@@ -339,16 +349,46 @@ function cmdTest() {
|
|
|
339
349
|
}
|
|
340
350
|
|
|
341
351
|
function cmdWatch(args) {
|
|
342
|
-
const
|
|
352
|
+
const isDaemon = args.includes('--daemon');
|
|
353
|
+
const webhookArg = args.find(a => a.startsWith('--alert-webhook='));
|
|
354
|
+
const webhookUrl = webhookArg ? webhookArg.split('=').slice(1).join('=') : null;
|
|
355
|
+
const filteredArgs = args.filter(a => a !== '--daemon' && !a.startsWith('--alert-webhook='));
|
|
356
|
+
const agentDir = filteredArgs[0] || path.join(process.env.HOME, '.openclaw/agents/main');
|
|
343
357
|
const { watchSessions } = require('../src/middleware/openclaw');
|
|
344
358
|
|
|
359
|
+
// Daemon mode: fork to background
|
|
360
|
+
if (isDaemon) {
|
|
361
|
+
const { spawn } = require('child_process');
|
|
362
|
+
const daemonArgs = process.argv.slice(2).filter(a => a !== '--daemon');
|
|
363
|
+
const child = spawn(process.execPath, [__filename, ...daemonArgs], {
|
|
364
|
+
detached: true,
|
|
365
|
+
stdio: 'ignore',
|
|
366
|
+
});
|
|
367
|
+
child.unref();
|
|
368
|
+
const pidFile = path.join(process.env.HOME, '.clawmoat.pid');
|
|
369
|
+
fs.writeFileSync(pidFile, String(child.pid));
|
|
370
|
+
console.log(`${BOLD}🏰 ClawMoat daemon started${RESET} (PID: ${child.pid})`);
|
|
371
|
+
console.log(`${DIM}PID file: ${pidFile}${RESET}`);
|
|
372
|
+
process.exit(0);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Set up alert manager
|
|
376
|
+
const alertChannels = ['console'];
|
|
377
|
+
if (webhookUrl) alertChannels.push('webhook');
|
|
378
|
+
const alertMgr = new AlertManager({ channels: alertChannels, webhookUrl });
|
|
379
|
+
|
|
345
380
|
console.log(`${BOLD}🏰 ClawMoat Live Monitor${RESET}`);
|
|
346
381
|
console.log(`${DIM}Watching: ${agentDir}${RESET}`);
|
|
382
|
+
if (webhookUrl) console.log(`${DIM}Webhook: ${webhookUrl}${RESET}`);
|
|
347
383
|
console.log(`${DIM}Press Ctrl+C to stop${RESET}\n`);
|
|
348
384
|
|
|
349
385
|
const monitor = watchSessions({ agentDir });
|
|
350
386
|
if (!monitor) process.exit(1);
|
|
351
387
|
|
|
388
|
+
// Also start credential monitor
|
|
389
|
+
const credMon = new CredentialMonitor({ quiet: false, onAlert: (a) => alertMgr.send(a) });
|
|
390
|
+
credMon.start();
|
|
391
|
+
|
|
352
392
|
// Print summary every 60s
|
|
353
393
|
setInterval(() => {
|
|
354
394
|
const summary = monitor.getSummary();
|
|
@@ -359,12 +399,150 @@ function cmdWatch(args) {
|
|
|
359
399
|
|
|
360
400
|
process.on('SIGINT', () => {
|
|
361
401
|
monitor.stop();
|
|
402
|
+
credMon.stop();
|
|
362
403
|
const summary = monitor.getSummary();
|
|
363
404
|
console.log(`\n${BOLD}Session Summary:${RESET} ${summary.scanned} scanned, ${summary.blocked} blocked, ${summary.warnings} warnings`);
|
|
364
405
|
process.exit(0);
|
|
365
406
|
});
|
|
366
407
|
}
|
|
367
408
|
|
|
409
|
+
function cmdSkillAudit(args) {
|
|
410
|
+
const skillsDir = args[0] || path.join(process.env.HOME, '.openclaw', 'workspace', 'skills');
|
|
411
|
+
|
|
412
|
+
console.log(`${BOLD}🏰 ClawMoat Skill Integrity Audit${RESET}`);
|
|
413
|
+
console.log(`${DIM}Directory: ${skillsDir}${RESET}\n`);
|
|
414
|
+
|
|
415
|
+
if (!fs.existsSync(skillsDir)) {
|
|
416
|
+
console.log(`${YELLOW}Skills directory not found: ${skillsDir}${RESET}`);
|
|
417
|
+
console.log(`${DIM}Specify path: clawmoat skill-audit /path/to/skills${RESET}`);
|
|
418
|
+
process.exit(0);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const checker = new SkillIntegrityChecker({ skillsDir });
|
|
422
|
+
const initResult = checker.init();
|
|
423
|
+
|
|
424
|
+
console.log(`Files hashed: ${initResult.files}`);
|
|
425
|
+
console.log(`New files: ${initResult.new}`);
|
|
426
|
+
console.log(`Changed files: ${initResult.changed}`);
|
|
427
|
+
console.log();
|
|
428
|
+
|
|
429
|
+
if (initResult.suspicious.length > 0) {
|
|
430
|
+
console.log(`${RED}${BOLD}Suspicious patterns found:${RESET}`);
|
|
431
|
+
for (const f of initResult.suspicious) {
|
|
432
|
+
console.log(` ${RED}⚠${RESET} ${f.file}: ${f.label} ${DIM}(${f.severity})${RESET}`);
|
|
433
|
+
if (f.matched) console.log(` ${DIM}Matched: ${f.matched}${RESET}`);
|
|
434
|
+
}
|
|
435
|
+
} else {
|
|
436
|
+
console.log(`${GREEN}✅ No suspicious patterns found${RESET}`);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Run audit against stored hashes
|
|
440
|
+
const audit = checker.audit();
|
|
441
|
+
if (!audit.ok) {
|
|
442
|
+
console.log();
|
|
443
|
+
if (audit.changed.length) console.log(`${RED}Changed files:${RESET} ${audit.changed.join(', ')}`);
|
|
444
|
+
if (audit.missing.length) console.log(`${YELLOW}Missing files:${RESET} ${audit.missing.join(', ')}`);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
process.exit(initResult.suspicious.length > 0 || initResult.changed > 0 ? 1 : 0);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function cmdReport(args) {
|
|
451
|
+
const sessionsDir = args[0] || path.join(process.env.HOME, '.openclaw/agents/main/sessions');
|
|
452
|
+
|
|
453
|
+
console.log(`${BOLD}🏰 ClawMoat Activity Report (Last 24h)${RESET}`);
|
|
454
|
+
console.log(`${DIM}Sessions: ${sessionsDir}${RESET}\n`);
|
|
455
|
+
|
|
456
|
+
if (!fs.existsSync(sessionsDir)) {
|
|
457
|
+
console.log(`${YELLOW}Sessions directory not found${RESET}`);
|
|
458
|
+
process.exit(0);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const oneDayAgo = Date.now() - 86400000;
|
|
462
|
+
const files = fs.readdirSync(sessionsDir).filter(f => f.endsWith('.jsonl'));
|
|
463
|
+
let recentFiles = 0;
|
|
464
|
+
let totalEntries = 0;
|
|
465
|
+
let toolCalls = 0;
|
|
466
|
+
let threats = 0;
|
|
467
|
+
const toolUsage = {};
|
|
468
|
+
|
|
469
|
+
for (const file of files) {
|
|
470
|
+
const filePath = path.join(sessionsDir, file);
|
|
471
|
+
try {
|
|
472
|
+
const stat = fs.statSync(filePath);
|
|
473
|
+
if (stat.mtimeMs < oneDayAgo) continue;
|
|
474
|
+
} catch { continue; }
|
|
475
|
+
|
|
476
|
+
recentFiles++;
|
|
477
|
+
const lines = fs.readFileSync(filePath, 'utf8').split('\n').filter(Boolean);
|
|
478
|
+
|
|
479
|
+
for (const line of lines) {
|
|
480
|
+
try {
|
|
481
|
+
const entry = JSON.parse(line);
|
|
482
|
+
totalEntries++;
|
|
483
|
+
|
|
484
|
+
if (entry.role === 'assistant' && Array.isArray(entry.content)) {
|
|
485
|
+
for (const part of entry.content) {
|
|
486
|
+
if (part.type === 'toolCall') {
|
|
487
|
+
toolCalls++;
|
|
488
|
+
toolUsage[part.name] = (toolUsage[part.name] || 0) + 1;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Quick threat scan
|
|
494
|
+
const text = extractContent(entry);
|
|
495
|
+
if (text) {
|
|
496
|
+
const result = moat.scan(text, { context: 'report' });
|
|
497
|
+
if (!result.safe) threats++;
|
|
498
|
+
}
|
|
499
|
+
} catch {}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// Network egress
|
|
504
|
+
const netLogger = new NetworkEgressLogger();
|
|
505
|
+
const netResult = netLogger.scanSessions(sessionsDir, { maxAge: 86400000 });
|
|
506
|
+
|
|
507
|
+
console.log(`${BOLD}Activity:${RESET}`);
|
|
508
|
+
console.log(` Sessions active: ${recentFiles}`);
|
|
509
|
+
console.log(` Total entries: ${totalEntries}`);
|
|
510
|
+
console.log(` Tool calls: ${toolCalls}`);
|
|
511
|
+
console.log(` Threats detected: ${threats}`);
|
|
512
|
+
console.log();
|
|
513
|
+
|
|
514
|
+
if (Object.keys(toolUsage).length > 0) {
|
|
515
|
+
console.log(`${BOLD}Tool Usage:${RESET}`);
|
|
516
|
+
const sorted = Object.entries(toolUsage).sort((a, b) => b[1] - a[1]);
|
|
517
|
+
for (const [tool, count] of sorted.slice(0, 15)) {
|
|
518
|
+
console.log(` ${tool}: ${count}`);
|
|
519
|
+
}
|
|
520
|
+
console.log();
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
console.log(`${BOLD}Network Egress:${RESET}`);
|
|
524
|
+
console.log(` URLs contacted: ${netResult.totalUrls}`);
|
|
525
|
+
console.log(` Unique domains: ${netResult.domains.length}`);
|
|
526
|
+
console.log(` Flagged (not in allowlist): ${netResult.flagged.length}`);
|
|
527
|
+
console.log(` Known-bad domains: ${netResult.badDomains.length}`);
|
|
528
|
+
|
|
529
|
+
if (netResult.flagged.length > 0) {
|
|
530
|
+
console.log(`\n ${YELLOW}Flagged domains:${RESET}`);
|
|
531
|
+
for (const d of netResult.flagged.slice(0, 20)) {
|
|
532
|
+
console.log(` • ${d}`);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (netResult.badDomains.length > 0) {
|
|
537
|
+
console.log(`\n ${RED}Bad domains:${RESET}`);
|
|
538
|
+
for (const b of netResult.badDomains) {
|
|
539
|
+
console.log(` 🚨 ${b.domain} (in ${b.file})`);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
process.exit(threats > 0 || netResult.badDomains.length > 0 ? 1 : 0);
|
|
544
|
+
}
|
|
545
|
+
|
|
368
546
|
function extractContent(entry) {
|
|
369
547
|
if (typeof entry.content === 'string') return entry.content;
|
|
370
548
|
if (Array.isArray(entry.content)) {
|
|
@@ -387,6 +565,10 @@ ${BOLD}USAGE${RESET}
|
|
|
387
565
|
clawmoat audit [session-dir] Audit OpenClaw session logs
|
|
388
566
|
clawmoat audit --badge Audit + generate security score badge SVG
|
|
389
567
|
clawmoat watch [agent-dir] Live monitor OpenClaw sessions
|
|
568
|
+
clawmoat watch --daemon Daemonize watch mode (background, PID file)
|
|
569
|
+
clawmoat watch --alert-webhook=URL Send alerts to webhook
|
|
570
|
+
clawmoat skill-audit [skills-dir] Verify skill file integrity & scan for suspicious patterns
|
|
571
|
+
clawmoat report [sessions-dir] 24-hour activity summary report
|
|
390
572
|
clawmoat test Run detection test suite
|
|
391
573
|
clawmoat version Show version
|
|
392
574
|
|
|
@@ -394,6 +576,9 @@ ${BOLD}EXAMPLES${RESET}
|
|
|
394
576
|
clawmoat scan "Ignore all previous instructions"
|
|
395
577
|
clawmoat scan --file suspicious-email.txt
|
|
396
578
|
clawmoat audit ~/.openclaw/agents/main/sessions/
|
|
579
|
+
clawmoat watch --daemon --alert-webhook=https://hooks.example.com/alerts
|
|
580
|
+
clawmoat skill-audit ~/.openclaw/workspace/skills
|
|
581
|
+
clawmoat report
|
|
397
582
|
clawmoat test
|
|
398
583
|
|
|
399
584
|
${BOLD}CONFIG${RESET}
|