clawmoat 0.8.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/DEMO.md +87 -0
- package/Dockerfile +5 -18
- package/README.md +232 -8
- 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 +11 -4
- package/docs/blog/40000-exposed-openclaw-instances.html +11 -4
- package/docs/blog/agent-trust-protocol.html +5 -4
- 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 +10 -4
- package/docs/blog/host-guardian-launch.html +18 -8
- package/docs/blog/ibm-experts-agent-runtime-protection.html +15 -6
- package/docs/blog/index.html +67 -9
- package/docs/blog/langchain-security-tutorial.html +18 -8
- package/docs/blog/mcp-30-cves-security-crisis.html +11 -4
- package/docs/blog/meta-researcher-rogue-agent.html +201 -0
- package/docs/blog/microsoft-openclaw-workstation-security.html +5 -4
- package/docs/blog/nist-ai-agent-standards-clawmoat.html +16 -8
- package/docs/blog/oasis-websocket-hijack.html +11 -4
- package/docs/blog/ollama-openclaw-security.html +10 -4
- package/docs/blog/openclaw-enterprise-readiness-claw10.html +5 -4
- package/docs/blog/openclaw-security-reckoning-2026.html +11 -4
- 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 +11 -16
- package/docs/business/install.html +21 -7
- package/docs/checklist.html +10 -4
- 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 +9 -6
- package/docs/guides/business-deployment.html +770 -0
- package/docs/hall-of-fame.html +11 -5
- package/docs/index.html +266 -137
- 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/rfcs/defense-in-depth.md +467 -0
- package/docs/scan/index.html +156 -12
- 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 +62 -2
- package/docs/support/index.html +12 -1
- 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/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/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 +142 -7
- 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/formatters/json.js +80 -0
- package/src/formatters/sarif.js +388 -0
- package/src/guardian/alerts.js +34 -3
- package/src/guardian/index.js +41 -2
- package/src/index.js +102 -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
- package/clawmoat-0.8.0.tgz +0 -0
- package/server/index.js.patch +0 -1
package/bin/clawmoat.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* clawmoat scan <text> Scan text for threats
|
|
8
8
|
* clawmoat scan --file <path> Scan file contents
|
|
9
9
|
* clawmoat audit <session-dir> Audit OpenClaw session logs
|
|
10
|
+
* clawmoat providers [cmd] Configure AI provider connections (Claude/ChatGPT/Kimi)
|
|
10
11
|
* clawmoat test Run built-in test suite against detection engines
|
|
11
12
|
* clawmoat version Show version
|
|
12
13
|
*/
|
|
@@ -21,6 +22,8 @@ const { NetworkEgressLogger } = require('../src/guardian/network-log');
|
|
|
21
22
|
const { AlertManager } = require('../src/guardian/alerts');
|
|
22
23
|
const { CredentialMonitor, CVEVerifier } = require('../src/guardian/index');
|
|
23
24
|
const { InsiderThreatDetector } = require('../src/guardian/insider-threat');
|
|
25
|
+
const { formatReport, formatScanResult, formatAuditResult } = require('../src/formatters/json');
|
|
26
|
+
const { formatScanResultAsSarif, formatAuditResultAsSarif } = require('../src/formatters/sarif');
|
|
24
27
|
|
|
25
28
|
const VERSION = require('../package.json').version;
|
|
26
29
|
const BOLD = '\x1b[1m';
|
|
@@ -58,6 +61,12 @@ switch (command) {
|
|
|
58
61
|
case 'verify-cve':
|
|
59
62
|
cmdVerifyCve(args.slice(1));
|
|
60
63
|
break;
|
|
64
|
+
case 'init':
|
|
65
|
+
cmdInit(args.slice(1));
|
|
66
|
+
break;
|
|
67
|
+
case 'ci':
|
|
68
|
+
cmdCI(args.slice(1));
|
|
69
|
+
break;
|
|
61
70
|
case 'test':
|
|
62
71
|
cmdTest();
|
|
63
72
|
break;
|
|
@@ -68,6 +77,13 @@ switch (command) {
|
|
|
68
77
|
case 'pro':
|
|
69
78
|
printUpgrade();
|
|
70
79
|
break;
|
|
80
|
+
case 'providers':
|
|
81
|
+
case 'provider':
|
|
82
|
+
cmdProviders(args.slice(1));
|
|
83
|
+
break;
|
|
84
|
+
case 'scan-mcp':
|
|
85
|
+
cmdScanMCP(args.slice(1));
|
|
86
|
+
break;
|
|
71
87
|
case 'version':
|
|
72
88
|
case '--version':
|
|
73
89
|
case '-v':
|
|
@@ -81,6 +97,23 @@ switch (command) {
|
|
|
81
97
|
break;
|
|
82
98
|
}
|
|
83
99
|
|
|
100
|
+
async function cmdProviders(args) {
|
|
101
|
+
const { cmdSetup, cmdList, cmdTest, cmdOpenClaw } = require('../agent/provider-setup');
|
|
102
|
+
const sub = args[0] || 'setup';
|
|
103
|
+
switch (sub) {
|
|
104
|
+
case 'setup': return cmdSetup();
|
|
105
|
+
case 'list': return cmdList();
|
|
106
|
+
case 'test': return cmdTest();
|
|
107
|
+
case 'openclaw': return cmdOpenClaw();
|
|
108
|
+
default:
|
|
109
|
+
console.log('Usage: clawmoat providers [setup|list|test|openclaw]');
|
|
110
|
+
console.log(' setup Configure AI providers (Claude, ChatGPT, Kimi)');
|
|
111
|
+
console.log(' list Show configured providers');
|
|
112
|
+
console.log(' test Test all connections');
|
|
113
|
+
console.log(' openclaw Generate OpenClaw config snippet');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
84
117
|
async function cmdVerifyCve(args) {
|
|
85
118
|
const cveId = args[0];
|
|
86
119
|
const suspiciousUrl = args[1] || null;
|
|
@@ -161,14 +194,38 @@ function colorSeverity(severity) {
|
|
|
161
194
|
}
|
|
162
195
|
|
|
163
196
|
function cmdScan(args) {
|
|
197
|
+
// Parse arguments
|
|
164
198
|
let text;
|
|
199
|
+
let sourceFile = 'stdin';
|
|
200
|
+
let format = 'text';
|
|
201
|
+
|
|
202
|
+
// Extract format flag
|
|
203
|
+
const formatIndex = args.indexOf('--format');
|
|
204
|
+
if (formatIndex >= 0 && formatIndex + 1 < args.length) {
|
|
205
|
+
format = args[formatIndex + 1];
|
|
206
|
+
args = args.filter((arg, i) => arg !== '--format' && i !== formatIndex + 1);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (!['text', 'json', 'sarif'].includes(format)) {
|
|
210
|
+
console.error(`${RED}Error: Invalid format "${format}". Supported: text, json, sarif${RESET}`);
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
165
213
|
|
|
166
214
|
if (args[0] === '--file' && args[1]) {
|
|
167
215
|
try {
|
|
168
|
-
|
|
169
|
-
|
|
216
|
+
sourceFile = args[1];
|
|
217
|
+
text = fs.readFileSync(sourceFile, 'utf8');
|
|
218
|
+
if (format === 'text') {
|
|
219
|
+
console.log(`${DIM}Scanning file: ${sourceFile} (${text.length} chars)${RESET}\n`);
|
|
220
|
+
}
|
|
170
221
|
} catch (err) {
|
|
171
|
-
|
|
222
|
+
if (format === 'json') {
|
|
223
|
+
console.log(JSON.stringify({ error: `Error reading file: ${err.message}` }));
|
|
224
|
+
} else if (format === 'sarif') {
|
|
225
|
+
console.log(JSON.stringify({ error: `Error reading file: ${err.message}` }));
|
|
226
|
+
} else {
|
|
227
|
+
console.error(`Error reading file: ${err.message}`);
|
|
228
|
+
}
|
|
172
229
|
process.exit(1);
|
|
173
230
|
}
|
|
174
231
|
} else if (args.length > 0) {
|
|
@@ -179,59 +236,94 @@ function cmdScan(args) {
|
|
|
179
236
|
}
|
|
180
237
|
|
|
181
238
|
if (!text) {
|
|
182
|
-
|
|
239
|
+
const errorMsg = 'No text to scan. Usage: clawmoat scan "text to scan"';
|
|
240
|
+
if (format === 'json') {
|
|
241
|
+
console.log(JSON.stringify({ error: errorMsg }));
|
|
242
|
+
} else if (format === 'sarif') {
|
|
243
|
+
console.log(JSON.stringify({ error: errorMsg }));
|
|
244
|
+
} else {
|
|
245
|
+
console.error(errorMsg);
|
|
246
|
+
}
|
|
183
247
|
process.exit(1);
|
|
184
248
|
}
|
|
185
249
|
|
|
186
250
|
const result = moat.scan(text, { context: 'cli' });
|
|
187
251
|
|
|
188
|
-
|
|
252
|
+
// Output based on format
|
|
253
|
+
if (format === 'json') {
|
|
254
|
+
const jsonResult = formatScanResult(result);
|
|
255
|
+
console.log(JSON.stringify(jsonResult, null, 2));
|
|
256
|
+
} else if (format === 'sarif') {
|
|
257
|
+
const sarifResult = formatScanResultAsSarif(result, sourceFile);
|
|
258
|
+
console.log(JSON.stringify(sarifResult, null, 2));
|
|
259
|
+
} else {
|
|
260
|
+
// Original text output
|
|
261
|
+
console.log(`${BOLD}🏰 ClawMoat Scan Results${RESET}\n`);
|
|
189
262
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
263
|
+
if (result.safe) {
|
|
264
|
+
console.log(`${GREEN}✅ CLEAN${RESET} — No threats detected\n`);
|
|
265
|
+
process.exit(0);
|
|
266
|
+
}
|
|
194
267
|
|
|
195
|
-
|
|
196
|
-
|
|
268
|
+
const icon = { critical: '🚨', high: '⚠️', medium: '⚡', low: 'ℹ️' };
|
|
269
|
+
const color = { critical: RED, high: RED, medium: YELLOW, low: CYAN };
|
|
197
270
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
271
|
+
for (const finding of result.findings) {
|
|
272
|
+
const sev = finding.severity || 'medium';
|
|
273
|
+
console.log(
|
|
274
|
+
`${icon[sev] || '•'} ${color[sev] || ''}${sev.toUpperCase()}${RESET} ` +
|
|
275
|
+
`${BOLD}${finding.type}${RESET}` +
|
|
276
|
+
(finding.subtype ? ` (${finding.subtype})` : '') +
|
|
277
|
+
(finding.matched ? `\n ${DIM}Matched: "${finding.matched}"${RESET}` : '') +
|
|
278
|
+
(finding.reason ? `\n ${DIM}${finding.reason}${RESET}` : '')
|
|
279
|
+
);
|
|
280
|
+
console.log();
|
|
281
|
+
}
|
|
209
282
|
|
|
210
|
-
|
|
283
|
+
console.log(`${DIM}Total findings: ${result.findings.length}${RESET}`);
|
|
211
284
|
|
|
212
|
-
|
|
213
|
-
|
|
285
|
+
if (!getLicense()) {
|
|
286
|
+
console.log(`\n${DIM}💡 Upgrade to Pro for real-time alerts, dashboard & threat intel → clawmoat upgrade${RESET}`);
|
|
287
|
+
}
|
|
214
288
|
}
|
|
215
289
|
|
|
216
290
|
process.exit(result.findings.some(f => f.severity === 'critical') ? 2 : 1);
|
|
217
291
|
}
|
|
218
292
|
|
|
219
293
|
function cmdAudit(args) {
|
|
294
|
+
// Parse arguments
|
|
220
295
|
const badgeFlag = args.includes('--badge');
|
|
221
|
-
const
|
|
296
|
+
const formatIndex = args.indexOf('--format');
|
|
297
|
+
const format = formatIndex >= 0 ? args[formatIndex + 1] : 'text';
|
|
298
|
+
const filteredArgs = args.filter((arg, i) => arg !== '--badge' && arg !== '--format' && i !== formatIndex + 1);
|
|
222
299
|
const sessionDir = filteredArgs[0] || path.join(process.env.HOME, '.openclaw/agents/main/sessions');
|
|
223
300
|
|
|
301
|
+
if (format !== 'text' && format !== 'json' && format !== 'sarif') {
|
|
302
|
+
console.error(`${RED}Error: Invalid format "${format}". Supported: text, json, sarif${RESET}`);
|
|
303
|
+
process.exit(1);
|
|
304
|
+
}
|
|
305
|
+
|
|
224
306
|
if (!fs.existsSync(sessionDir)) {
|
|
225
|
-
|
|
307
|
+
if (format === 'json') {
|
|
308
|
+
console.log(JSON.stringify({ error: 'Session directory not found', directory: sessionDir }));
|
|
309
|
+
} else if (format === 'sarif') {
|
|
310
|
+
console.log(JSON.stringify({ error: 'Session directory not found', directory: sessionDir }));
|
|
311
|
+
} else {
|
|
312
|
+
console.error(`Session directory not found: ${sessionDir}`);
|
|
313
|
+
}
|
|
226
314
|
process.exit(1);
|
|
227
315
|
}
|
|
228
316
|
|
|
229
|
-
|
|
230
|
-
|
|
317
|
+
if (format === 'text') {
|
|
318
|
+
console.log(`${BOLD}🏰 ClawMoat Session Audit${RESET}`);
|
|
319
|
+
console.log(`${DIM}Directory: ${sessionDir}${RESET}\n`);
|
|
320
|
+
}
|
|
231
321
|
|
|
232
322
|
const files = fs.readdirSync(sessionDir).filter(f => f.endsWith('.jsonl'));
|
|
233
323
|
let totalFindings = 0;
|
|
234
324
|
let filesScanned = 0;
|
|
325
|
+
const findingsByFile = {};
|
|
326
|
+
const allFindings = [];
|
|
235
327
|
|
|
236
328
|
for (const file of files) {
|
|
237
329
|
const filePath = path.join(sessionDir, file);
|
|
@@ -246,6 +338,14 @@ function cmdAudit(args) {
|
|
|
246
338
|
const result = moat.scan(content, { context: 'session_log' });
|
|
247
339
|
if (!result.safe) {
|
|
248
340
|
fileFindings += result.findings.length;
|
|
341
|
+
for (const finding of result.findings) {
|
|
342
|
+
allFindings.push({
|
|
343
|
+
...finding,
|
|
344
|
+
source: file,
|
|
345
|
+
timestamp: entry.timestamp || new Date().toISOString(),
|
|
346
|
+
entry_id: entry.id || null
|
|
347
|
+
});
|
|
348
|
+
}
|
|
249
349
|
}
|
|
250
350
|
}
|
|
251
351
|
|
|
@@ -258,6 +358,14 @@ function cmdAudit(args) {
|
|
|
258
358
|
const evalResult = moat.evaluateTool(tc.name, tc.arguments || {});
|
|
259
359
|
if (evalResult.decision !== 'allow') {
|
|
260
360
|
fileFindings++;
|
|
361
|
+
allFindings.push({
|
|
362
|
+
type: 'tool_policy_violation',
|
|
363
|
+
severity: 'medium',
|
|
364
|
+
reason: `Tool ${tc.name} blocked by policy: ${evalResult.reason}`,
|
|
365
|
+
source: file,
|
|
366
|
+
timestamp: entry.timestamp || new Date().toISOString(),
|
|
367
|
+
entry_id: entry.id || null
|
|
368
|
+
});
|
|
261
369
|
}
|
|
262
370
|
}
|
|
263
371
|
}
|
|
@@ -266,34 +374,56 @@ function cmdAudit(args) {
|
|
|
266
374
|
|
|
267
375
|
filesScanned++;
|
|
268
376
|
totalFindings += fileFindings;
|
|
377
|
+
findingsByFile[file] = fileFindings;
|
|
269
378
|
|
|
270
|
-
if (
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
379
|
+
if (format === 'text') {
|
|
380
|
+
if (fileFindings > 0) {
|
|
381
|
+
console.log(`${RED}⚠ ${file}${RESET}: ${fileFindings} finding(s)`);
|
|
382
|
+
} else {
|
|
383
|
+
console.log(`${GREEN}✓ ${file}${RESET}: clean`);
|
|
384
|
+
}
|
|
274
385
|
}
|
|
275
386
|
}
|
|
276
387
|
|
|
277
|
-
|
|
388
|
+
// Prepare audit data
|
|
389
|
+
const auditData = {
|
|
390
|
+
filesScanned,
|
|
391
|
+
totalFindings,
|
|
392
|
+
sessionDir,
|
|
393
|
+
findingsByFile,
|
|
394
|
+
findings: allFindings
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
// Output based on format
|
|
398
|
+
if (format === 'json') {
|
|
399
|
+
const jsonResult = formatAuditResult(auditData);
|
|
400
|
+
console.log(JSON.stringify(jsonResult, null, 2));
|
|
401
|
+
} else if (format === 'sarif') {
|
|
402
|
+
const sarifResult = formatAuditResultAsSarif(auditData);
|
|
403
|
+
console.log(JSON.stringify(sarifResult, null, 2));
|
|
404
|
+
} else {
|
|
405
|
+
// Original text output
|
|
406
|
+
console.log(`\n${BOLD}Summary:${RESET} ${filesScanned} sessions scanned, ${totalFindings} total findings`);
|
|
278
407
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
408
|
+
const summary = moat.getSummary();
|
|
409
|
+
if (summary.events.byType) {
|
|
410
|
+
console.log(`${DIM}Breakdown: ${JSON.stringify(summary.events.byType)}${RESET}`);
|
|
411
|
+
}
|
|
283
412
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
413
|
+
// Badge generation
|
|
414
|
+
if (badgeFlag) {
|
|
415
|
+
const criticalFindings = allFindings.filter(f => f.severity === 'critical').length;
|
|
416
|
+
const grade = calculateGrade({ totalFindings, criticalFindings, filesScanned });
|
|
417
|
+
const svg = generateBadgeSVG(grade);
|
|
418
|
+
const badgePath = path.join(process.cwd(), 'clawmoat-badge.svg');
|
|
419
|
+
fs.writeFileSync(badgePath, svg);
|
|
420
|
+
console.log(`\n${BOLD}🏷️ Security Badge${RESET}`);
|
|
421
|
+
console.log(` Grade: ${grade}`);
|
|
422
|
+
console.log(` SVG saved: ${badgePath}`);
|
|
423
|
+
console.log(` Shields.io: ${getShieldsURL(grade)}`);
|
|
424
|
+
console.log(`\n ${DIM}Add to README:${RESET}`);
|
|
425
|
+
console.log(` })`);
|
|
426
|
+
}
|
|
297
427
|
}
|
|
298
428
|
|
|
299
429
|
process.exit(totalFindings > 0 ? 1 : 0);
|
|
@@ -446,13 +576,13 @@ function cmdTest() {
|
|
|
446
576
|
process.exit(failed > 0 ? 1 : 0);
|
|
447
577
|
}
|
|
448
578
|
|
|
449
|
-
function cmdWatch(args) {
|
|
579
|
+
async function cmdWatch(args) {
|
|
450
580
|
const isDaemon = args.includes('--daemon');
|
|
451
581
|
const webhookArg = args.find(a => a.startsWith('--alert-webhook='));
|
|
452
582
|
const webhookUrl = webhookArg ? webhookArg.split('=').slice(1).join('=') : null;
|
|
453
583
|
const filteredArgs = args.filter(a => a !== '--daemon' && !a.startsWith('--alert-webhook='));
|
|
454
|
-
const
|
|
455
|
-
const {
|
|
584
|
+
const watchDir = filteredArgs[0] || path.join(process.env.HOME, '.openclaw');
|
|
585
|
+
const { LiveMonitor } = require('../src/watch/live-monitor');
|
|
456
586
|
|
|
457
587
|
// Daemon mode: fork to background
|
|
458
588
|
if (isDaemon) {
|
|
@@ -475,33 +605,45 @@ function cmdWatch(args) {
|
|
|
475
605
|
if (webhookUrl) alertChannels.push('webhook');
|
|
476
606
|
const alertMgr = new AlertManager({ channels: alertChannels, webhookUrl });
|
|
477
607
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
608
|
+
// Create and start live monitor
|
|
609
|
+
const monitor = new LiveMonitor({
|
|
610
|
+
watchDir,
|
|
611
|
+
refreshRate: 1000,
|
|
612
|
+
showNetworkGraph: true,
|
|
613
|
+
showThreatMap: true,
|
|
614
|
+
maxHistoryItems: 100,
|
|
615
|
+
animateCharts: true
|
|
616
|
+
});
|
|
482
617
|
|
|
483
|
-
|
|
484
|
-
|
|
618
|
+
// Connect alert manager to monitor events
|
|
619
|
+
monitor.on('threat-detected', (threat) => {
|
|
620
|
+
alertMgr.send({
|
|
621
|
+
type: 'threat',
|
|
622
|
+
severity: threat.severity,
|
|
623
|
+
message: `Threat detected: ${threat.type}/${threat.subtype}`
|
|
624
|
+
});
|
|
625
|
+
});
|
|
485
626
|
|
|
486
627
|
// Also start credential monitor
|
|
487
|
-
const credMon = new CredentialMonitor({ quiet:
|
|
628
|
+
const credMon = new CredentialMonitor({ quiet: true, onAlert: (a) => alertMgr.send(a) });
|
|
488
629
|
credMon.start();
|
|
489
630
|
|
|
490
|
-
//
|
|
491
|
-
setInterval(() => {
|
|
492
|
-
const summary = monitor.getSummary();
|
|
493
|
-
if (summary.scanned > 0) {
|
|
494
|
-
console.log(`${DIM}[ClawMoat] Stats: ${summary.scanned} scanned, ${summary.blocked} blocked, ${summary.warnings} warnings${RESET}`);
|
|
495
|
-
}
|
|
496
|
-
}, 60000);
|
|
497
|
-
|
|
631
|
+
// Handle shutdown gracefully
|
|
498
632
|
process.on('SIGINT', () => {
|
|
499
633
|
monitor.stop();
|
|
500
634
|
credMon.stop();
|
|
501
|
-
|
|
502
|
-
|
|
635
|
+
console.log(`\n${GREEN}ClawMoat Live Monitor stopped.${RESET}`);
|
|
636
|
+
process.exit(0);
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
process.on('SIGTERM', () => {
|
|
640
|
+
monitor.stop();
|
|
641
|
+
credMon.stop();
|
|
503
642
|
process.exit(0);
|
|
504
643
|
});
|
|
644
|
+
|
|
645
|
+
// Start the live monitor
|
|
646
|
+
await monitor.start();
|
|
505
647
|
}
|
|
506
648
|
|
|
507
649
|
function cmdSkillAudit(args) {
|
|
@@ -546,16 +688,27 @@ function cmdSkillAudit(args) {
|
|
|
546
688
|
}
|
|
547
689
|
|
|
548
690
|
function cmdReport(args) {
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
691
|
+
// Parse arguments
|
|
692
|
+
const formatIndex = args.indexOf('--format');
|
|
693
|
+
const format = formatIndex >= 0 ? args[formatIndex + 1] : 'text';
|
|
694
|
+
const otherArgs = args.filter((arg, i) => arg !== '--format' && i !== formatIndex + 1);
|
|
695
|
+
const sessionsDir = otherArgs[0] || path.join(process.env.HOME, '.openclaw/agents/main/sessions');
|
|
696
|
+
|
|
697
|
+
if (format !== 'text' && format !== 'json') {
|
|
698
|
+
console.error(`${RED}Error: Invalid format "${format}". Supported: text, json${RESET}`);
|
|
699
|
+
process.exit(1);
|
|
700
|
+
}
|
|
553
701
|
|
|
554
702
|
if (!fs.existsSync(sessionsDir)) {
|
|
555
|
-
|
|
703
|
+
if (format === 'json') {
|
|
704
|
+
console.log(JSON.stringify({ error: 'Sessions directory not found', directory: sessionsDir }));
|
|
705
|
+
} else {
|
|
706
|
+
console.log(`${YELLOW}Sessions directory not found: ${sessionsDir}${RESET}`);
|
|
707
|
+
}
|
|
556
708
|
process.exit(0);
|
|
557
709
|
}
|
|
558
710
|
|
|
711
|
+
// Collect all data
|
|
559
712
|
const oneDayAgo = Date.now() - 86400000;
|
|
560
713
|
const files = fs.readdirSync(sessionsDir).filter(f => f.endsWith('.jsonl'));
|
|
561
714
|
let recentFiles = 0;
|
|
@@ -563,6 +716,7 @@ function cmdReport(args) {
|
|
|
563
716
|
let toolCalls = 0;
|
|
564
717
|
let threats = 0;
|
|
565
718
|
const toolUsage = {};
|
|
719
|
+
const findings = [];
|
|
566
720
|
|
|
567
721
|
for (const file of files) {
|
|
568
722
|
const filePath = path.join(sessionsDir, file);
|
|
@@ -592,7 +746,17 @@ function cmdReport(args) {
|
|
|
592
746
|
const text = extractContent(entry);
|
|
593
747
|
if (text) {
|
|
594
748
|
const result = moat.scan(text, { context: 'report' });
|
|
595
|
-
if (!result.safe)
|
|
749
|
+
if (!result.safe) {
|
|
750
|
+
threats++;
|
|
751
|
+
for (const finding of result.findings) {
|
|
752
|
+
findings.push({
|
|
753
|
+
...finding,
|
|
754
|
+
timestamp: entry.timestamp || new Date().toISOString(),
|
|
755
|
+
source: file,
|
|
756
|
+
entry_id: entry.id || null
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
}
|
|
596
760
|
}
|
|
597
761
|
} catch {}
|
|
598
762
|
}
|
|
@@ -602,22 +766,6 @@ function cmdReport(args) {
|
|
|
602
766
|
const netLogger = new NetworkEgressLogger();
|
|
603
767
|
const netResult = netLogger.scanSessions(sessionsDir, { maxAge: 86400000 });
|
|
604
768
|
|
|
605
|
-
console.log(`${BOLD}Activity:${RESET}`);
|
|
606
|
-
console.log(` Sessions active: ${recentFiles}`);
|
|
607
|
-
console.log(` Total entries: ${totalEntries}`);
|
|
608
|
-
console.log(` Tool calls: ${toolCalls}`);
|
|
609
|
-
console.log(` Threats detected: ${threats}`);
|
|
610
|
-
console.log();
|
|
611
|
-
|
|
612
|
-
if (Object.keys(toolUsage).length > 0) {
|
|
613
|
-
console.log(`${BOLD}Tool Usage:${RESET}`);
|
|
614
|
-
const sorted = Object.entries(toolUsage).sort((a, b) => b[1] - a[1]);
|
|
615
|
-
for (const [tool, count] of sorted.slice(0, 15)) {
|
|
616
|
-
console.log(` ${tool}: ${count}`);
|
|
617
|
-
}
|
|
618
|
-
console.log();
|
|
619
|
-
}
|
|
620
|
-
|
|
621
769
|
// Insider threat scan on recent sessions
|
|
622
770
|
const insiderDetector = new InsiderThreatDetector();
|
|
623
771
|
let insiderThreats = 0;
|
|
@@ -636,28 +784,68 @@ function cmdReport(args) {
|
|
|
636
784
|
if (insiderResult.riskScore > insiderHighScore) insiderHighScore = insiderResult.riskScore;
|
|
637
785
|
}
|
|
638
786
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
787
|
+
// Prepare report data
|
|
788
|
+
const reportData = {
|
|
789
|
+
recentFiles,
|
|
790
|
+
totalEntries,
|
|
791
|
+
toolCalls,
|
|
792
|
+
threats,
|
|
793
|
+
toolUsage,
|
|
794
|
+
netResult,
|
|
795
|
+
insiderThreats,
|
|
796
|
+
insiderHighScore,
|
|
797
|
+
findings,
|
|
798
|
+
sessionDir: sessionsDir
|
|
799
|
+
};
|
|
800
|
+
|
|
801
|
+
// Output based on format
|
|
802
|
+
if (format === 'json') {
|
|
803
|
+
const jsonReport = formatReport(reportData);
|
|
804
|
+
console.log(JSON.stringify(jsonReport, null, 2));
|
|
805
|
+
} else {
|
|
806
|
+
// Original text output
|
|
807
|
+
console.log(`${BOLD}🏰 ClawMoat Activity Report (Last 24h)${RESET}`);
|
|
808
|
+
console.log(`${DIM}Sessions: ${sessionsDir}${RESET}\n`);
|
|
809
|
+
|
|
810
|
+
console.log(`${BOLD}Activity:${RESET}`);
|
|
811
|
+
console.log(` Sessions active: ${recentFiles}`);
|
|
812
|
+
console.log(` Total entries: ${totalEntries}`);
|
|
813
|
+
console.log(` Tool calls: ${toolCalls}`);
|
|
814
|
+
console.log(` Threats detected: ${threats}`);
|
|
815
|
+
console.log();
|
|
816
|
+
|
|
817
|
+
if (Object.keys(toolUsage).length > 0) {
|
|
818
|
+
console.log(`${BOLD}Tool Usage:${RESET}`);
|
|
819
|
+
const sorted = Object.entries(toolUsage).sort((a, b) => b[1] - a[1]);
|
|
820
|
+
for (const [tool, count] of sorted.slice(0, 15)) {
|
|
821
|
+
console.log(` ${tool}: ${count}`);
|
|
822
|
+
}
|
|
823
|
+
console.log();
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
console.log(`${BOLD}Insider Threats:${RESET}`);
|
|
827
|
+
console.log(` Threats detected: ${insiderThreats}`);
|
|
828
|
+
console.log(` Highest risk score: ${insiderHighScore}/100`);
|
|
829
|
+
console.log();
|
|
643
830
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
831
|
+
console.log(`${BOLD}Network Egress:${RESET}`);
|
|
832
|
+
console.log(` URLs contacted: ${netResult.totalUrls}`);
|
|
833
|
+
console.log(` Unique domains: ${netResult.domains.length}`);
|
|
834
|
+
console.log(` Flagged (not in allowlist): ${netResult.flagged.length}`);
|
|
835
|
+
console.log(` Known-bad domains: ${netResult.badDomains.length}`);
|
|
649
836
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
837
|
+
if (netResult.flagged.length > 0) {
|
|
838
|
+
console.log(`\n ${YELLOW}Flagged domains:${RESET}`);
|
|
839
|
+
for (const d of netResult.flagged.slice(0, 20)) {
|
|
840
|
+
console.log(` • ${d}`);
|
|
841
|
+
}
|
|
654
842
|
}
|
|
655
|
-
}
|
|
656
843
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
844
|
+
if (netResult.badDomains.length > 0) {
|
|
845
|
+
console.log(`\n ${RED}Bad domains:${RESET}`);
|
|
846
|
+
for (const b of netResult.badDomains) {
|
|
847
|
+
console.log(` 🚨 ${b.domain} (in ${b.file})`);
|
|
848
|
+
}
|
|
661
849
|
}
|
|
662
850
|
}
|
|
663
851
|
|
|
@@ -837,6 +1025,160 @@ function cmdActivate(args) {
|
|
|
837
1025
|
req.end();
|
|
838
1026
|
}
|
|
839
1027
|
|
|
1028
|
+
function cmdScanMCP(args) {
|
|
1029
|
+
const { scanMCP, discoverMCPConfigs } = require('../src/mcp-scanner');
|
|
1030
|
+
const extraPaths = args.filter(a => !a.startsWith('-'));
|
|
1031
|
+
const verbose = args.includes('--verbose') || args.includes('-v');
|
|
1032
|
+
const jsonOut = args.includes('--json');
|
|
1033
|
+
|
|
1034
|
+
console.log('\n🏰 ClawMoat MCP Scanner\n');
|
|
1035
|
+
|
|
1036
|
+
// Run scan
|
|
1037
|
+
const report = scanMCP({ extraPaths, verbose });
|
|
1038
|
+
|
|
1039
|
+
if (jsonOut) {
|
|
1040
|
+
console.log(JSON.stringify(report, null, 2));
|
|
1041
|
+
return;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
// Discovery
|
|
1045
|
+
console.log(`📁 Configs discovered: ${report.configsFound.length}`);
|
|
1046
|
+
for (const c of report.configsFound) {
|
|
1047
|
+
console.log(` ✓ ${c.name}: ${c.path}`);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
if (report.configsFound.length === 0) {
|
|
1051
|
+
// Show what we looked for
|
|
1052
|
+
console.log('\n No MCP configs found. Searched:');
|
|
1053
|
+
const all = discoverMCPConfigs();
|
|
1054
|
+
for (const c of all.slice(0, 6)) {
|
|
1055
|
+
console.log(` · ${c.name}: ${c.path}`);
|
|
1056
|
+
}
|
|
1057
|
+
console.log(` ... and ${all.length - 6} more locations`);
|
|
1058
|
+
console.log('\n Tip: pass a config path directly: clawmoat scan-mcp ~/.cursor/mcp.json\n');
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// Servers
|
|
1063
|
+
console.log(`\n🔌 MCP servers found: ${report.servers.length}`);
|
|
1064
|
+
for (const s of report.servers) {
|
|
1065
|
+
console.log(` · ${s.name} (${s.config})`);
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
// Findings
|
|
1069
|
+
if (report.findings.length === 0) {
|
|
1070
|
+
console.log('\n✅ No security issues found.\n');
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
console.log(`\n⚠️ Findings: ${report.summary.total}`);
|
|
1075
|
+
if (report.summary.critical) console.log(` 🔴 ${report.summary.critical} CRITICAL`);
|
|
1076
|
+
if (report.summary.high) console.log(` 🟠 ${report.summary.high} HIGH`);
|
|
1077
|
+
if (report.summary.medium) console.log(` 🟡 ${report.summary.medium} MEDIUM`);
|
|
1078
|
+
if (report.summary.low) console.log(` 🔵 ${report.summary.low} LOW`);
|
|
1079
|
+
|
|
1080
|
+
console.log('');
|
|
1081
|
+
for (const f of report.findings) {
|
|
1082
|
+
const icon = f.severity === 'critical' ? '🔴' : f.severity === 'high' ? '🟠' : f.severity === 'medium' ? '🟡' : '🔵';
|
|
1083
|
+
console.log(`${icon} [${f.severity.toUpperCase()}] ${f.label}`);
|
|
1084
|
+
console.log(` Server: ${f.server} (${f.configName})`);
|
|
1085
|
+
console.log(` Fix: ${f.fix}`);
|
|
1086
|
+
console.log('');
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
function cmdInit(args) {
|
|
1091
|
+
const force = args.includes('--force') || args.includes('-f');
|
|
1092
|
+
const configPath = path.join(process.cwd(), 'clawmoat.yml');
|
|
1093
|
+
const templatePath = path.join(__dirname, '../src/templates/default-config.yml');
|
|
1094
|
+
|
|
1095
|
+
// Check if config already exists
|
|
1096
|
+
if (fs.existsSync(configPath) && !force) {
|
|
1097
|
+
console.log(`${YELLOW}⚠️ Configuration file already exists: ${configPath}${RESET}`);
|
|
1098
|
+
console.log(`${DIM}Use --force to overwrite${RESET}`);
|
|
1099
|
+
process.exit(1);
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// Read template
|
|
1103
|
+
let template;
|
|
1104
|
+
try {
|
|
1105
|
+
template = fs.readFileSync(templatePath, 'utf8');
|
|
1106
|
+
} catch (err) {
|
|
1107
|
+
console.error(`${RED}Error reading config template: ${err.message}${RESET}`);
|
|
1108
|
+
process.exit(1);
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
// Write config file
|
|
1112
|
+
try {
|
|
1113
|
+
fs.writeFileSync(configPath, template);
|
|
1114
|
+
console.log(`${GREEN}✅ Created ${configPath}${RESET}`);
|
|
1115
|
+
console.log(`${DIM}Edit the file to customize your security policies.${RESET}`);
|
|
1116
|
+
console.log(`${DIM}Documentation: https://github.com/darfaz/clawmoat${RESET}`);
|
|
1117
|
+
} catch (err) {
|
|
1118
|
+
console.error(`${RED}Error writing config file: ${err.message}${RESET}`);
|
|
1119
|
+
process.exit(1);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
function cmdCI(args) {
|
|
1124
|
+
const { scanRepo } = require('../src/ci-scanner');
|
|
1125
|
+
const jsonOut = args.includes('--json');
|
|
1126
|
+
const dir = args.find(a => !a.startsWith('-')) || '.';
|
|
1127
|
+
const failOn = (args.find(a => a.startsWith('--fail-on=')) || '--fail-on=high').split('=')[1];
|
|
1128
|
+
|
|
1129
|
+
if (!jsonOut) {
|
|
1130
|
+
console.log(`\n${BOLD}🏰 ClawMoat CI Scanner${RESET}`);
|
|
1131
|
+
console.log(`${DIM}Scanning: ${require('path').resolve(dir)}${RESET}\n`);
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
const result = scanRepo({ rootDir: dir, failOn });
|
|
1135
|
+
|
|
1136
|
+
if (jsonOut) {
|
|
1137
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1138
|
+
process.exit(result.passed ? 0 : 1);
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
if (result.findings.length === 0) {
|
|
1143
|
+
console.log(`${GREEN}✅ No security issues found.${RESET}\n`);
|
|
1144
|
+
process.exit(0);
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
// Group by type
|
|
1149
|
+
const byType = {};
|
|
1150
|
+
for (const f of result.findings) {
|
|
1151
|
+
if (!byType[f.type]) byType[f.type] = [];
|
|
1152
|
+
byType[f.type].push(f);
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
for (const [type, findings] of Object.entries(byType)) {
|
|
1156
|
+
const label = type.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
|
|
1157
|
+
console.log(`${BOLD}${label}${RESET}`);
|
|
1158
|
+
for (const f of findings) {
|
|
1159
|
+
const icon = f.severity === 'critical' ? `${RED}🔴` : f.severity === 'high' ? `${YELLOW}🟠` : '🟡';
|
|
1160
|
+
console.log(` ${icon} [${f.severity.toUpperCase()}] ${f.evidence}${RESET}`);
|
|
1161
|
+
console.log(`${DIM} File: ${f.file}${RESET}`);
|
|
1162
|
+
console.log(`${DIM} Fix: ${f.fix}${RESET}`);
|
|
1163
|
+
}
|
|
1164
|
+
console.log('');
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
console.log(`${BOLD}Summary:${RESET} ${result.summary.total} issue(s) found`);
|
|
1168
|
+
if (result.summary.critical) console.log(` ${RED}${result.summary.critical} critical${RESET}`);
|
|
1169
|
+
if (result.summary.high) console.log(` ${YELLOW}${result.summary.high} high${RESET}`);
|
|
1170
|
+
if (result.summary.medium) console.log(` ${DIM}${result.summary.medium} medium${RESET}`);
|
|
1171
|
+
console.log('');
|
|
1172
|
+
|
|
1173
|
+
if (!result.passed) {
|
|
1174
|
+
console.log(`${RED}❌ CI check FAILED (found ${failOn}+ severity issues)${RESET}\n`);
|
|
1175
|
+
process.exit(1);
|
|
1176
|
+
} else {
|
|
1177
|
+
console.log(`${YELLOW}⚠️ Issues found but none at fail-on severity (${failOn})${RESET}\n`);
|
|
1178
|
+
process.exit(0);
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
|
|
840
1182
|
function getLicense() {
|
|
841
1183
|
try {
|
|
842
1184
|
const licPath = path.join(process.env.HOME, '.clawmoat', 'license.json');
|
|
@@ -852,30 +1194,45 @@ ${BOLD}🏰 ClawMoat v${VERSION}${RESET} — Security moat for AI agents
|
|
|
852
1194
|
Plan: ${planLabel}
|
|
853
1195
|
|
|
854
1196
|
${BOLD}USAGE${RESET}
|
|
1197
|
+
clawmoat ci [dir] Scan repo for secrets, compromised deps, CI risks, MCP issues
|
|
1198
|
+
clawmoat ci --json Output JSON for CI/CD integration
|
|
1199
|
+
clawmoat ci --fail-on=critical Only fail on critical severity (default: high)
|
|
1200
|
+
clawmoat init Generate a starter config file (clawmoat.yml)
|
|
855
1201
|
clawmoat scan <text> Scan text for threats
|
|
856
1202
|
clawmoat scan --file <path> Scan file contents
|
|
1203
|
+
clawmoat scan --format sarif Output SARIF format for CI/CD integration
|
|
857
1204
|
cat file.txt | clawmoat scan Scan from stdin
|
|
858
1205
|
clawmoat audit [session-dir] Audit OpenClaw session logs
|
|
859
1206
|
clawmoat audit --badge Audit + generate security score badge SVG
|
|
1207
|
+
clawmoat audit --format sarif Generate SARIF report for security platforms
|
|
860
1208
|
clawmoat watch [agent-dir] Live monitor OpenClaw sessions
|
|
861
1209
|
clawmoat watch --daemon Daemonize watch mode (background, PID file)
|
|
862
1210
|
clawmoat watch --alert-webhook=URL Send alerts to webhook
|
|
863
1211
|
clawmoat skill-audit [skills-dir] Verify skill file integrity & scan for suspicious patterns
|
|
864
1212
|
clawmoat insider-scan [session-file] Scan sessions for insider threats (self-preservation, blackmail, deception)
|
|
865
1213
|
clawmoat report [sessions-dir] 24-hour activity summary report
|
|
1214
|
+
clawmoat report --format json Generate JSON report for programmatic use
|
|
866
1215
|
clawmoat verify-cve <CVE-ID> [url] Verify a CVE against GitHub Advisory DB
|
|
867
1216
|
clawmoat test Run detection test suite
|
|
1217
|
+
clawmoat providers Configure AI providers (Claude/ChatGPT/Kimi)
|
|
1218
|
+
clawmoat providers list Show configured providers
|
|
1219
|
+
clawmoat providers test Test all provider connections
|
|
1220
|
+
clawmoat providers openclaw Generate OpenClaw config snippet
|
|
868
1221
|
clawmoat activate <KEY> Activate a Pro/Team license key
|
|
869
1222
|
clawmoat upgrade Show upgrade options & pricing
|
|
870
1223
|
clawmoat version Show version
|
|
871
1224
|
|
|
872
1225
|
${BOLD}EXAMPLES${RESET}
|
|
1226
|
+
clawmoat init # Create clawmoat.yml config file
|
|
873
1227
|
clawmoat scan "Ignore all previous instructions"
|
|
874
1228
|
clawmoat scan --file suspicious-email.txt
|
|
1229
|
+
clawmoat scan --format sarif --file agent-input.txt # For GitHub Code Scanning
|
|
875
1230
|
clawmoat audit ~/.openclaw/agents/main/sessions/
|
|
1231
|
+
clawmoat audit --format sarif # SARIF output for CI/CD
|
|
876
1232
|
clawmoat watch --daemon --alert-webhook=https://hooks.example.com/alerts
|
|
877
1233
|
clawmoat skill-audit ~/.openclaw/workspace/skills
|
|
878
1234
|
clawmoat report
|
|
1235
|
+
clawmoat report --format json # For dashboards/automation
|
|
879
1236
|
clawmoat test
|
|
880
1237
|
|
|
881
1238
|
${BOLD}CONFIG${RESET}
|