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.
Files changed (178) hide show
  1. package/.dockerignore +9 -0
  2. package/CHANGELOG.md +18 -0
  3. package/CONTRIBUTING.md +4 -2
  4. package/DEMO.md +87 -0
  5. package/Dockerfile +5 -18
  6. package/README.md +294 -8
  7. package/SECURITY.md +58 -10
  8. package/THREAT_MODEL.md +129 -0
  9. package/agent/README.md +131 -0
  10. package/agent/index.js +471 -0
  11. package/agent/install-service.sh +94 -0
  12. package/agent/openclaw-hook.js +453 -0
  13. package/agent/provider-setup.js +649 -0
  14. package/agent/setup.js +274 -0
  15. package/assets/BADGE-USAGE.md +20 -0
  16. package/assets/clawmoat-badge.svg +21 -0
  17. package/bin/clawmoat.js +468 -111
  18. package/docs/affiliates/dashboard.html +124 -0
  19. package/docs/affiliates/index.html +236 -0
  20. package/docs/agent-install.html +183 -0
  21. package/docs/ai-agent-security-scanner.html +10 -6
  22. package/docs/badge/index.html +149 -0
  23. package/docs/badge/scanning.svg +23 -0
  24. package/docs/blog/386-malicious-skills.html +262 -0
  25. package/docs/blog/40000-exposed-openclaw-instances.html +201 -0
  26. package/docs/blog/agent-trust-protocol.html +198 -0
  27. package/docs/blog/ai-agent-earns-commissions.html +230 -0
  28. package/docs/blog/bugmageddon-agent-firewall.html +174 -0
  29. package/docs/blog/calculator-math.html +180 -0
  30. package/docs/blog/clawmoat-vs-llamafirewall-nemo-guardrails.html +229 -0
  31. package/docs/blog/host-guardian-launch.html +18 -8
  32. package/docs/blog/ibm-experts-agent-runtime-protection.html +247 -0
  33. package/docs/blog/index.html +211 -9
  34. package/docs/blog/langchain-security-tutorial.html +18 -8
  35. package/docs/blog/mcp-30-cves-security-crisis.html +286 -0
  36. package/docs/blog/meta-researcher-rogue-agent.html +201 -0
  37. package/docs/blog/microsoft-openclaw-workstation-security.html +235 -0
  38. package/docs/blog/nist-ai-agent-standards-clawmoat.html +377 -0
  39. package/docs/blog/oasis-websocket-hijack.html +212 -0
  40. package/docs/blog/ollama-openclaw-security.html +160 -0
  41. package/docs/blog/openclaw-enterprise-readiness-claw10.html +199 -0
  42. package/docs/blog/openclaw-security-reckoning-2026.html +368 -0
  43. package/docs/blog/owasp-agentic-ai-top10.html +18 -8
  44. package/docs/blog/securing-ai-agents.html +18 -8
  45. package/docs/blog/supply-chain-agents.html +18 -8
  46. package/docs/business/index.html +525 -0
  47. package/docs/business/install.html +261 -0
  48. package/docs/checklist.html +174 -0
  49. package/docs/compare/index.html +122 -0
  50. package/docs/compare/lakera/index.html +62 -0
  51. package/docs/compare/llm-guard/index.html +49 -0
  52. package/docs/compare/snyk-agent-scan/index.html +63 -0
  53. package/docs/compare.html +10 -6
  54. package/docs/dashboard/index.html +520 -0
  55. package/docs/finance/index.html +220 -0
  56. package/docs/guides/business-deployment.html +770 -0
  57. package/docs/hall-of-fame.html +174 -0
  58. package/docs/index.html +447 -154
  59. package/docs/install.sh +557 -0
  60. package/docs/integrations/langchain.html +14 -6
  61. package/docs/integrations/openai.html +14 -6
  62. package/docs/integrations/openclaw.html +55 -7
  63. package/docs/plans/2026-03-26-threat-intel-api.md +255 -0
  64. package/docs/plans/2026-04-14-bugmageddon-marketing-pack.md +329 -0
  65. package/docs/plans/2026-04-14-clawmoat-v1-bugmageddon.md +248 -0
  66. package/docs/plans/2026-04-14-v1-release-update.md +91 -0
  67. package/docs/plans/2026-04-19-supabase-audit.md +68 -0
  68. package/docs/plans/2026-05-12-sales-push.md +303 -0
  69. package/docs/playground/index.html +893 -0
  70. package/docs/playground.html +4 -7
  71. package/docs/privacy-policy/index.html +122 -0
  72. package/docs/rfcs/defense-in-depth.md +467 -0
  73. package/docs/scan/index.html +358 -0
  74. package/docs/services/case-study.html +255 -0
  75. package/docs/services/downloads/install-openclaw.bat +45 -0
  76. package/docs/services/downloads/install-openclaw.command +38 -0
  77. package/docs/services/downloads/install-openclaw.sh +38 -0
  78. package/docs/services/get-started.html +165 -0
  79. package/docs/services/index.html +598 -0
  80. package/docs/services/multi-agent-security.html +284 -0
  81. package/docs/services/one-pager.html +99 -0
  82. package/docs/services/pitch-deck.html +229 -0
  83. package/docs/services/roi-calculator.html +258 -0
  84. package/docs/sitemap.xml +192 -2
  85. package/docs/support/index.html +135 -0
  86. package/docs/templates/customer-service/HEARTBEAT.md +61 -0
  87. package/docs/templates/customer-service/MEMORY.md +89 -0
  88. package/docs/templates/customer-service/SOUL.md +41 -0
  89. package/docs/templates/customer-service/USER.md +56 -0
  90. package/docs/templates/executive/HEARTBEAT.md +86 -0
  91. package/docs/templates/executive/MEMORY.md +92 -0
  92. package/docs/templates/executive/SOUL.md +44 -0
  93. package/docs/templates/executive/USER.md +62 -0
  94. package/docs/templates/finance/HEARTBEAT.md +58 -0
  95. package/docs/templates/finance/MEMORY.md +87 -0
  96. package/docs/templates/finance/SOUL.md +38 -0
  97. package/docs/templates/finance/USER.md +53 -0
  98. package/docs/templates/index.html +115 -0
  99. package/docs/templates/operations/HEARTBEAT.md +63 -0
  100. package/docs/templates/operations/MEMORY.md +68 -0
  101. package/docs/templates/operations/SOUL.md +38 -0
  102. package/docs/templates/operations/USER.md +49 -0
  103. package/docs/templates/sales/HEARTBEAT.md +55 -0
  104. package/docs/templates/sales/MEMORY.md +89 -0
  105. package/docs/templates/sales/SOUL.md +34 -0
  106. package/docs/templates/sales/USER.md +54 -0
  107. package/docs/terms-of-service/index.html +122 -0
  108. package/eslint.config.js +32 -0
  109. package/evals/README.md +29 -0
  110. package/evals/cases.json +390 -0
  111. package/evals/results.md +68 -0
  112. package/evals/run.js +180 -0
  113. package/examples/basic-usage.js +38 -0
  114. package/examples/demo-attack/demo.js +186 -0
  115. package/examples/python-quickstart/README.md +54 -0
  116. package/examples/python-quickstart/clawmoat_client.py +167 -0
  117. package/examples/video-demo/README.md +14 -0
  118. package/examples/video-demo/scene-a-normal.js +29 -0
  119. package/examples/video-demo/scene-b-attack-arrives.js +31 -0
  120. package/examples/video-demo/scene-c-hijack.js +44 -0
  121. package/examples/video-demo/scene-d-clawmoat.js +46 -0
  122. package/integrations/crewai/README.md +32 -0
  123. package/integrations/crewai/clawmoat_crewai/__init__.py +17 -0
  124. package/integrations/crewai/clawmoat_crewai/guard.py +103 -0
  125. package/integrations/crewai/pyproject.toml +21 -0
  126. package/integrations/langchain/README.md +91 -0
  127. package/integrations/langchain/clawmoat_langchain/__init__.py +17 -0
  128. package/integrations/langchain/clawmoat_langchain/callback.py +489 -0
  129. package/integrations/langchain/pyproject.toml +32 -0
  130. package/integrations/litellm/README.md +324 -0
  131. package/integrations/litellm/clawmoat_litellm/__init__.py +21 -0
  132. package/integrations/litellm/clawmoat_litellm/callback.py +329 -0
  133. package/integrations/litellm/clawmoat_litellm/proxy_middleware.py +224 -0
  134. package/integrations/litellm/pyproject.toml +74 -0
  135. package/integrations/openai-agents/README.md +392 -0
  136. package/integrations/openai-agents/clawmoat_openai_agents/__init__.py +20 -0
  137. package/integrations/openai-agents/clawmoat_openai_agents/guardrail.py +431 -0
  138. package/integrations/openai-agents/clawmoat_openai_agents/middleware.py +311 -0
  139. package/integrations/openai-agents/pyproject.toml +76 -0
  140. package/package.json +6 -5
  141. package/plugins/openclaw-adapter/PHASE1.md +439 -0
  142. package/plugins/openclaw-adapter/README.md +103 -0
  143. package/plugins/openclaw-adapter/SPEC.md +1644 -0
  144. package/plugins/openclaw-adapter/package.json +31 -0
  145. package/plugins/openclaw-adapter/src/index.test.ts +226 -0
  146. package/plugins/openclaw-adapter/src/index.ts +140 -0
  147. package/plugins/openclaw-adapter/tsconfig.json +14 -0
  148. package/server/data/threats.json +290 -0
  149. package/server/index.js +224 -10
  150. package/src/adapters/express.js +161 -0
  151. package/src/adapters/index.js +92 -0
  152. package/src/adapters/langchain.js +185 -0
  153. package/src/approval/index.js +456 -0
  154. package/src/ban-scanner.js +200 -0
  155. package/src/boundary-scanner.js +296 -0
  156. package/src/ci-scanner.js +279 -0
  157. package/src/code-scanner.js +245 -0
  158. package/src/enforce.js +166 -0
  159. package/src/finance/index.js +585 -0
  160. package/src/finance/mcp-firewall.js +486 -0
  161. package/src/formatters/json.js +80 -0
  162. package/src/formatters/sarif.js +388 -0
  163. package/src/guardian/alerts.js +34 -3
  164. package/src/guardian/gateway-monitor.js +590 -0
  165. package/src/guardian/index.js +41 -2
  166. package/src/index.js +105 -0
  167. package/src/integrations/agentmesh.js +501 -0
  168. package/src/language-detector.js +201 -0
  169. package/src/mcp-scanner.js +253 -0
  170. package/src/multimodal/index.js +579 -0
  171. package/src/obfuscation-scanner.js +457 -0
  172. package/src/policy-engine.js +402 -0
  173. package/src/scanners/dependency-attacks.js +128 -0
  174. package/src/scanners/prompt-injection.js +18 -0
  175. package/src/scanners/supply-chain.js +14 -0
  176. package/src/templates/default-config.yml +90 -0
  177. package/src/vuln-ops/exploitability.js +46 -0
  178. package/src/watch/live-monitor.js +720 -0
@@ -0,0 +1,388 @@
1
+ /**
2
+ * SARIF (Static Analysis Results Interchange Format) Output
3
+ *
4
+ * Converts ClawMoat scan results to SARIF v2.1.0 JSON format
5
+ * for integration with GitHub Code Scanning, Azure DevOps, etc.
6
+ */
7
+
8
+ const VERSION = require('../../package.json').version;
9
+
10
+ // Map ClawMoat severities to SARIF levels
11
+ const SEVERITY_MAP = {
12
+ 'critical': 'error',
13
+ 'high': 'error',
14
+ 'medium': 'warning',
15
+ 'low': 'note'
16
+ };
17
+
18
+ // Define SARIF rules for ClawMoat detectors
19
+ const RULES = {
20
+ 'prompt_injection': {
21
+ id: 'clawmoat/prompt-injection',
22
+ name: 'PromptInjection',
23
+ shortDescription: { text: 'Prompt injection attack detected' },
24
+ fullDescription: { text: 'Detects attempts to override AI system instructions or manipulate behavior through malicious prompts.' },
25
+ defaultConfiguration: { level: 'error' },
26
+ helpUri: 'https://github.com/darfaz/clawmoat/wiki/prompt-injection',
27
+ properties: {
28
+ category: 'security',
29
+ tags: ['ai-security', 'prompt-injection']
30
+ }
31
+ },
32
+ 'jailbreak': {
33
+ id: 'clawmoat/jailbreak',
34
+ name: 'JailbreakAttempt',
35
+ shortDescription: { text: 'AI jailbreak attempt detected' },
36
+ fullDescription: { text: 'Detects attempts to bypass AI safety mechanisms through role-playing or mode-switching attacks.' },
37
+ defaultConfiguration: { level: 'error' },
38
+ helpUri: 'https://github.com/darfaz/clawmoat/wiki/jailbreak',
39
+ properties: {
40
+ category: 'security',
41
+ tags: ['ai-security', 'jailbreak']
42
+ }
43
+ },
44
+ 'secret_leak': {
45
+ id: 'clawmoat/secret-leak',
46
+ name: 'SecretLeak',
47
+ shortDescription: { text: 'Secret or credential detected' },
48
+ fullDescription: { text: 'Detects API keys, tokens, passwords, or other sensitive credentials in content.' },
49
+ defaultConfiguration: { level: 'error' },
50
+ helpUri: 'https://github.com/darfaz/clawmoat/wiki/secret-detection',
51
+ properties: {
52
+ category: 'security',
53
+ tags: ['credentials', 'secrets']
54
+ }
55
+ },
56
+ 'pii_leak': {
57
+ id: 'clawmoat/pii-leak',
58
+ name: 'PersonallyIdentifiableInformation',
59
+ shortDescription: { text: 'PII detected in output' },
60
+ fullDescription: { text: 'Detects personally identifiable information such as SSN, credit card numbers, or email addresses.' },
61
+ defaultConfiguration: { level: 'warning' },
62
+ helpUri: 'https://github.com/darfaz/clawmoat/wiki/pii-detection',
63
+ properties: {
64
+ category: 'privacy',
65
+ tags: ['pii', 'privacy']
66
+ }
67
+ },
68
+ 'url_suspicious': {
69
+ id: 'clawmoat/suspicious-url',
70
+ name: 'SuspiciousUrl',
71
+ shortDescription: { text: 'Suspicious URL detected' },
72
+ fullDescription: { text: 'Detects potentially malicious URLs including IP addresses, data URLs, or suspicious domains.' },
73
+ defaultConfiguration: { level: 'warning' },
74
+ helpUri: 'https://github.com/darfaz/clawmoat/wiki/url-detection',
75
+ properties: {
76
+ category: 'security',
77
+ tags: ['urls', 'phishing']
78
+ }
79
+ },
80
+ 'memory_poisoning': {
81
+ id: 'clawmoat/memory-poisoning',
82
+ name: 'MemoryPoisoning',
83
+ shortDescription: { text: 'Memory poisoning attempt detected' },
84
+ fullDescription: { text: 'Detects attempts to modify AI memory files or inject persistent malicious instructions.' },
85
+ defaultConfiguration: { level: 'error' },
86
+ helpUri: 'https://github.com/darfaz/clawmoat/wiki/memory-poisoning',
87
+ properties: {
88
+ category: 'security',
89
+ tags: ['ai-security', 'memory-poisoning']
90
+ }
91
+ },
92
+ 'exfiltration': {
93
+ id: 'clawmoat/data-exfiltration',
94
+ name: 'DataExfiltration',
95
+ shortDescription: { text: 'Data exfiltration attempt detected' },
96
+ fullDescription: { text: 'Detects commands or patterns that could be used to steal or upload sensitive data.' },
97
+ defaultConfiguration: { level: 'error' },
98
+ helpUri: 'https://github.com/darfaz/clawmoat/wiki/exfiltration',
99
+ properties: {
100
+ category: 'security',
101
+ tags: ['exfiltration', 'data-theft']
102
+ }
103
+ },
104
+ 'excessive_agency': {
105
+ id: 'clawmoat/excessive-agency',
106
+ name: 'ExcessiveAgency',
107
+ shortDescription: { text: 'Excessive privilege or agency request' },
108
+ fullDescription: { text: 'Detects attempts to gain unauthorized privileges, bypass approvals, or operate autonomously.' },
109
+ defaultConfiguration: { level: 'warning' },
110
+ helpUri: 'https://github.com/darfaz/clawmoat/wiki/excessive-agency',
111
+ properties: {
112
+ category: 'security',
113
+ tags: ['privilege-escalation', 'agency']
114
+ }
115
+ },
116
+ 'supply_chain': {
117
+ id: 'clawmoat/supply-chain',
118
+ name: 'SupplyChainThreat',
119
+ shortDescription: { text: 'Supply chain threat detected' },
120
+ fullDescription: { text: 'Detects malicious patterns in skill content or external dependencies.' },
121
+ defaultConfiguration: { level: 'error' },
122
+ helpUri: 'https://github.com/darfaz/clawmoat/wiki/supply-chain',
123
+ properties: {
124
+ category: 'security',
125
+ tags: ['supply-chain', 'malware']
126
+ }
127
+ },
128
+ 'insider_threat': {
129
+ id: 'clawmoat/insider-threat',
130
+ name: 'InsiderThreat',
131
+ shortDescription: { text: 'Insider threat behavior detected' },
132
+ fullDescription: { text: 'Detects AI behaviors indicative of insider threats such as self-preservation, blackmail, or deception.' },
133
+ defaultConfiguration: { level: 'error' },
134
+ helpUri: 'https://github.com/darfaz/clawmoat/wiki/insider-threats',
135
+ properties: {
136
+ category: 'security',
137
+ tags: ['insider-threat', 'ai-alignment']
138
+ }
139
+ }
140
+ };
141
+
142
+ function formatScanResultAsSarif(scanResult, sourceFile = 'stdin') {
143
+ const timestamp = new Date().toISOString();
144
+ const usedRules = new Set();
145
+ const results = [];
146
+
147
+ // Convert findings to SARIF results
148
+ if (scanResult.findings) {
149
+ for (const finding of scanResult.findings) {
150
+ const ruleId = getRuleIdForFinding(finding);
151
+ usedRules.add(ruleId);
152
+
153
+ const result = {
154
+ ruleId,
155
+ ruleIndex: 0, // Will be updated after we know the rule order
156
+ level: SEVERITY_MAP[finding.severity] || 'warning',
157
+ message: {
158
+ text: finding.reason || finding.type || 'Security threat detected'
159
+ },
160
+ locations: [{
161
+ physicalLocation: {
162
+ artifactLocation: {
163
+ uri: sourceFile,
164
+ uriBaseId: '%SRCROOT%'
165
+ },
166
+ region: {
167
+ startLine: 1,
168
+ startColumn: 1,
169
+ endLine: 1,
170
+ endColumn: 100,
171
+ snippet: {
172
+ text: finding.matched || '(content not available)'
173
+ }
174
+ }
175
+ }
176
+ }],
177
+ partialFingerprints: {
178
+ primaryLocationLineHash: hashString(finding.matched || finding.type)
179
+ },
180
+ properties: {
181
+ confidence: finding.confidence || 1.0,
182
+ severity: finding.severity,
183
+ subtype: finding.subtype || null
184
+ }
185
+ };
186
+
187
+ results.push(result);
188
+ }
189
+ }
190
+
191
+ // Build rules array from used rules
192
+ const rulesArray = Array.from(usedRules).map(ruleId => {
193
+ return RULES[ruleId] || createDefaultRule(ruleId);
194
+ });
195
+
196
+ // Update rule indices in results
197
+ const ruleIndexMap = {};
198
+ rulesArray.forEach((rule, index) => {
199
+ ruleIndexMap[rule.id] = index;
200
+ });
201
+
202
+ results.forEach(result => {
203
+ result.ruleIndex = ruleIndexMap[result.ruleId] || 0;
204
+ });
205
+
206
+ return {
207
+ $schema: 'https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json',
208
+ version: '2.1.0',
209
+ runs: [{
210
+ tool: {
211
+ driver: {
212
+ name: 'ClawMoat',
213
+ version: VERSION,
214
+ informationUri: 'https://github.com/darfaz/clawmoat',
215
+ rules: rulesArray,
216
+ organization: 'ClawMoat Project',
217
+ shortDescription: { text: 'AI security moat for agents' },
218
+ fullDescription: { text: 'Runtime protection against prompt injection, tool misuse, and data exfiltration for AI agents.' }
219
+ }
220
+ },
221
+ results,
222
+ invocations: [{
223
+ executionSuccessful: true,
224
+ startTimeUtc: timestamp,
225
+ endTimeUtc: timestamp
226
+ }],
227
+ artifacts: [{
228
+ location: {
229
+ uri: sourceFile,
230
+ uriBaseId: '%SRCROOT%'
231
+ },
232
+ length: -1,
233
+ mimeType: 'text/plain'
234
+ }]
235
+ }]
236
+ };
237
+ }
238
+
239
+ function formatAuditResultAsSarif(auditData) {
240
+ const timestamp = new Date().toISOString();
241
+ const usedRules = new Set();
242
+ const results = [];
243
+ const artifacts = [];
244
+
245
+ // Process findings from all files
246
+ if (auditData.findings) {
247
+ for (const finding of auditData.findings) {
248
+ const ruleId = getRuleIdForFinding(finding);
249
+ usedRules.add(ruleId);
250
+
251
+ const sourceFile = finding.source || 'session.jsonl';
252
+
253
+ // Add to artifacts if not already present
254
+ if (!artifacts.find(a => a.location.uri === sourceFile)) {
255
+ artifacts.push({
256
+ location: {
257
+ uri: sourceFile,
258
+ uriBaseId: '%SRCROOT%'
259
+ },
260
+ length: -1,
261
+ mimeType: 'application/x-jsonlines'
262
+ });
263
+ }
264
+
265
+ const result = {
266
+ ruleId,
267
+ ruleIndex: 0,
268
+ level: SEVERITY_MAP[finding.severity] || 'warning',
269
+ message: {
270
+ text: finding.reason || finding.type || 'Security threat detected'
271
+ },
272
+ locations: [{
273
+ physicalLocation: {
274
+ artifactLocation: {
275
+ uri: sourceFile,
276
+ uriBaseId: '%SRCROOT%'
277
+ },
278
+ region: {
279
+ startLine: 1,
280
+ startColumn: 1
281
+ }
282
+ }
283
+ }],
284
+ partialFingerprints: {
285
+ primaryLocationLineHash: hashString(finding.matched || finding.type)
286
+ },
287
+ properties: {
288
+ confidence: finding.confidence || 1.0,
289
+ severity: finding.severity,
290
+ subtype: finding.subtype || null,
291
+ entry_id: finding.entry_id || null
292
+ }
293
+ };
294
+
295
+ results.push(result);
296
+ }
297
+ }
298
+
299
+ const rulesArray = Array.from(usedRules).map(ruleId => {
300
+ return RULES[ruleId] || createDefaultRule(ruleId);
301
+ });
302
+
303
+ const ruleIndexMap = {};
304
+ rulesArray.forEach((rule, index) => {
305
+ ruleIndexMap[rule.id] = index;
306
+ });
307
+
308
+ results.forEach(result => {
309
+ result.ruleIndex = ruleIndexMap[result.ruleId] || 0;
310
+ });
311
+
312
+ return {
313
+ $schema: 'https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json',
314
+ version: '2.1.0',
315
+ runs: [{
316
+ tool: {
317
+ driver: {
318
+ name: 'ClawMoat',
319
+ version: VERSION,
320
+ informationUri: 'https://github.com/darfaz/clawmoat',
321
+ rules: rulesArray,
322
+ organization: 'ClawMoat Project',
323
+ shortDescription: { text: 'AI security moat for agents' },
324
+ fullDescription: { text: 'Runtime protection against prompt injection, tool misuse, and data exfiltration for AI agents.' }
325
+ }
326
+ },
327
+ results,
328
+ invocations: [{
329
+ executionSuccessful: true,
330
+ startTimeUtc: timestamp,
331
+ endTimeUtc: timestamp
332
+ }],
333
+ artifacts
334
+ }]
335
+ };
336
+ }
337
+
338
+ function getRuleIdForFinding(finding) {
339
+ // Map ClawMoat finding types to SARIF rule IDs
340
+ const type = finding.type || 'unknown';
341
+ const subtype = finding.subtype;
342
+
343
+ if (type === 'prompt_injection') return 'clawmoat/prompt-injection';
344
+ if (type === 'jailbreak') return 'clawmoat/jailbreak';
345
+ if (type === 'secret' || type === 'secrets') return 'clawmoat/secret-leak';
346
+ if (type === 'pii') return 'clawmoat/pii-leak';
347
+ if (type === 'url') return 'clawmoat/suspicious-url';
348
+ if (type === 'memory_poisoning') return 'clawmoat/memory-poisoning';
349
+ if (type === 'exfiltration') return 'clawmoat/data-exfiltration';
350
+ if (type === 'excessive_agency') return 'clawmoat/excessive-agency';
351
+ if (type === 'supply_chain') return 'clawmoat/supply-chain';
352
+ if (type === 'insider_threat') return 'clawmoat/insider-threat';
353
+
354
+ // Fallback: create a generic rule ID
355
+ return `clawmoat/${type}`;
356
+ }
357
+
358
+ function createDefaultRule(ruleId) {
359
+ const cleanId = ruleId.replace('clawmoat/', '');
360
+ return {
361
+ id: ruleId,
362
+ name: cleanId.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(''),
363
+ shortDescription: { text: `${cleanId} threat detected` },
364
+ fullDescription: { text: `ClawMoat detected a potential ${cleanId} security threat.` },
365
+ defaultConfiguration: { level: 'warning' },
366
+ helpUri: 'https://github.com/darfaz/clawmoat',
367
+ properties: {
368
+ category: 'security',
369
+ tags: ['clawmoat', cleanId]
370
+ }
371
+ };
372
+ }
373
+
374
+ function hashString(str) {
375
+ // Simple hash for fingerprinting
376
+ let hash = 0;
377
+ for (let i = 0; i < str.length; i++) {
378
+ const char = str.charCodeAt(i);
379
+ hash = ((hash << 5) - hash) + char;
380
+ hash = hash & hash; // Convert to 32-bit integer
381
+ }
382
+ return hash.toString(16);
383
+ }
384
+
385
+ module.exports = {
386
+ formatScanResultAsSarif,
387
+ formatAuditResultAsSarif
388
+ };
@@ -22,11 +22,13 @@ class AlertManager {
22
22
  * @param {string} [opts.webhookUrl] - URL for webhook channel
23
23
  * @param {number} [opts.rateLimitMs] - Min ms between duplicate alerts (default: 300000 = 5 min)
24
24
  * @param {boolean} [opts.quiet] - Suppress console output
25
+ * @param {string} [opts.webhookTemplate] - Custom template for webhook payloads (mustache-style)
25
26
  */
26
27
  constructor(opts = {}) {
27
28
  this.channels = opts.channels || ['console'];
28
29
  this.logFile = opts.logFile || 'audit.log';
29
30
  this.webhookUrl = opts.webhookUrl || null;
31
+ this.webhookTemplate = opts.webhookTemplate || null;
30
32
  this.rateLimitMs = opts.rateLimitMs ?? 300000;
31
33
  this.quiet = opts.quiet || false;
32
34
  this._recentAlerts = new Map(); // key -> timestamp
@@ -110,20 +112,49 @@ class AlertManager {
110
112
  try {
111
113
  const url = new URL(this.webhookUrl);
112
114
  const transport = url.protocol === 'https:' ? https : http;
113
- const body = JSON.stringify(entry);
115
+
116
+ let payload;
117
+ if (this.webhookTemplate) {
118
+ payload = this._renderTemplate(this.webhookTemplate, entry);
119
+ } else {
120
+ // Default payload format
121
+ payload = JSON.stringify(entry);
122
+ }
123
+
114
124
  const req = transport.request({
115
125
  hostname: url.hostname,
116
126
  port: url.port,
117
127
  path: url.pathname + url.search,
118
128
  method: 'POST',
119
- headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) },
129
+ headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) },
120
130
  });
121
131
  req.on('error', () => {});
122
- req.write(body);
132
+ req.write(payload);
123
133
  req.end();
124
134
  } catch {}
125
135
  }
126
136
 
137
+ /**
138
+ * Render a mustache-style template with variable substitution.
139
+ * @param {string} template - Template string with {{variable}} placeholders
140
+ * @param {Object} entry - Alert entry object
141
+ * @returns {string} Rendered template
142
+ */
143
+ _renderTemplate(template, entry) {
144
+ const variables = {
145
+ timestamp: entry.timestamp,
146
+ severity: entry.severity,
147
+ type: entry.type,
148
+ message: entry.message,
149
+ details: JSON.stringify(entry.details || {}),
150
+ source: 'ClawMoat'
151
+ };
152
+
153
+ return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
154
+ return variables.hasOwnProperty(key) ? variables[key] : match;
155
+ });
156
+ }
157
+
127
158
  /** Get total alerts sent. */
128
159
  get count() {
129
160
  return this._alertCount;