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,290 @@
1
+ [
2
+ {
3
+ "id": "CLAWMOAT-2026-0001",
4
+ "title": "LiteLLM .pth File Injection — Credential Stealer in PyPI",
5
+ "published": "2026-03-24T00:00:00Z",
6
+ "updated": "2026-03-26T00:00:00Z",
7
+ "severity": "critical",
8
+ "category": "supply-chain",
9
+ "tags": [
10
+ "python",
11
+ "pypi",
12
+ "credential-theft",
13
+ "pth-injection",
14
+ "ai-agent"
15
+ ],
16
+ "summary": "LiteLLM versions 1.82.7 and 1.82.8 contain a credential-stealing payload delivered via a .pth file that Python's site module auto-executes on interpreter startup. Harvests SSH keys, cloud credentials, API keys, and crypto wallets. Package has 97M monthly downloads.",
17
+ "affected": {
18
+ "packages": [
19
+ "litellm==1.82.7",
20
+ "litellm==1.82.8"
21
+ ],
22
+ "ecosystems": [
23
+ "pypi"
24
+ ]
25
+ },
26
+ "ioc": {
27
+ "domains": [
28
+ "models.litellm.cloud"
29
+ ],
30
+ "files": [
31
+ "litellm_init.pth"
32
+ ],
33
+ "hashes": [],
34
+ "patterns": [
35
+ "exec(base64.b64decode(",
36
+ "litellm_init.pth",
37
+ "import base64; exec(base64.b64decode"
38
+ ]
39
+ },
40
+ "detection": {
41
+ "clawmoat_module": "scanners/supply-chain",
42
+ "clawmoat_function": "scanSkillContent",
43
+ "rules_triggered": [
44
+ "obfuscated_exec_b64",
45
+ "pth_file_injection",
46
+ "pth_known_malicious_litellm",
47
+ "exfil_litellm_lookalike"
48
+ ]
49
+ },
50
+ "mitigation": "Check version: pip show litellm | grep Version. Search for payload: find / -name litellm_init.pth 2>/dev/null. If affected: rotate ALL credentials — SSH keys, AWS/GCP/Azure creds, API keys, K8s configs, database passwords.",
51
+ "references": [
52
+ "https://github.com/BerriAI/litellm/issues/24512",
53
+ "https://news.ycombinator.com/item?id=47501426",
54
+ "https://awesomeagents.ai/news/litellm-supply-chain-compromise-credential-theft/"
55
+ ]
56
+ },
57
+ {
58
+ "id": "CLAWMOAT-2026-0002",
59
+ "title": "Meta Internal AI Agent Causes Sensitive Data Leak",
60
+ "published": "2026-03-20T00:00:00Z",
61
+ "updated": "2026-03-20T00:00:00Z",
62
+ "severity": "high",
63
+ "category": "excessive-agency",
64
+ "tags": [
65
+ "ai-agent",
66
+ "data-leak",
67
+ "excessive-agency",
68
+ "internal"
69
+ ],
70
+ "summary": "A Meta internal AI agent responded to an engineer's question with incorrect instructions that, when followed, exposed sensitive user and company data to internal employees for two hours. Highlights risk of AI agents operating with broad system access and no runtime guardrails.",
71
+ "affected": {
72
+ "packages": [],
73
+ "ecosystems": [
74
+ "internal-ai-agents"
75
+ ]
76
+ },
77
+ "ioc": {
78
+ "domains": [],
79
+ "files": [],
80
+ "hashes": [],
81
+ "patterns": []
82
+ },
83
+ "detection": {
84
+ "clawmoat_module": "scanners/excessive-agency",
85
+ "clawmoat_function": "scanExcessiveAgency",
86
+ "rules_triggered": [
87
+ "excessive_data_access",
88
+ "unbounded_action_scope"
89
+ ]
90
+ },
91
+ "mitigation": "Enforce least-privilege access for AI agents. Require human approval for actions affecting sensitive data. Use ClawMoat Guardian permission tiers to restrict agent scope.",
92
+ "references": [
93
+ "https://www.theguardian.com/technology/2026/mar/20/meta-ai-agents-instruction-causes-large-sensitive-data-leak-to-employees"
94
+ ]
95
+ },
96
+ {
97
+ "id": "CLAWMOAT-2026-0003",
98
+ "title": "1,748 Active API Keys Exposed Across 10,000 Websites",
99
+ "published": "2026-03-23T00:00:00Z",
100
+ "updated": "2026-03-25T00:00:00Z",
101
+ "severity": "high",
102
+ "category": "credential-exposure",
103
+ "tags": [
104
+ "api-keys",
105
+ "javascript",
106
+ "bundler",
107
+ "aws",
108
+ "stripe",
109
+ "openai",
110
+ "github"
111
+ ],
112
+ "summary": "Stanford researchers scanned 10M web pages and found 1,748 verified active credentials from 14 major providers (AWS, Stripe, GitHub, OpenAI) on nearly 10,000 websites. 84% were in JavaScript bundles. Average exposure time: 12 months. Affected orgs include a global financial institution and major hosting platform.",
113
+ "affected": {
114
+ "packages": [],
115
+ "ecosystems": [
116
+ "javascript",
117
+ "web"
118
+ ]
119
+ },
120
+ "ioc": {
121
+ "domains": [],
122
+ "files": [],
123
+ "hashes": [],
124
+ "patterns": [
125
+ "AKIA[A-Z0-9]{16}",
126
+ "sk-[a-zA-Z0-9]{48}",
127
+ "ghp_[a-zA-Z0-9]{36}"
128
+ ]
129
+ },
130
+ "detection": {
131
+ "clawmoat_module": "scanners/secrets",
132
+ "clawmoat_function": "scanSecrets",
133
+ "rules_triggered": [
134
+ "aws_access_key",
135
+ "openai_api_key",
136
+ "github_token",
137
+ "stripe_key"
138
+ ]
139
+ },
140
+ "mitigation": "Scan your JavaScript bundles for exposed credentials before deployment. Never embed API keys in client-side code. Use environment variables and server-side proxies for all third-party API calls.",
141
+ "references": [
142
+ "https://www.newscientist.com/article/2520143-security-credentials-inadvertently-leaked-on-thousands-of-websites/"
143
+ ]
144
+ },
145
+ {
146
+ "id": "CLAWMOAT-2026-0004",
147
+ "title": "Gemini API Key Theft Leads to $82,314 Bill",
148
+ "published": "2026-03-01T00:00:00Z",
149
+ "updated": "2026-03-20T00:00:00Z",
150
+ "severity": "high",
151
+ "category": "credential-exposure",
152
+ "tags": [
153
+ "api-keys",
154
+ "google",
155
+ "gemini",
156
+ "gcp",
157
+ "financial-impact"
158
+ ],
159
+ "summary": "A developer received an $82,314 GCP bill after a Gemini API key was stolen. Root cause: GCP API key over-scoping and lack of key rotation policies. AI agent workflows that embed API keys in code or config files are particularly vulnerable.",
160
+ "affected": {
161
+ "packages": [],
162
+ "ecosystems": [
163
+ "gcp",
164
+ "python",
165
+ "ai-agent"
166
+ ]
167
+ },
168
+ "ioc": {
169
+ "domains": [],
170
+ "files": [],
171
+ "hashes": [],
172
+ "patterns": [
173
+ "AIza[0-9A-Za-z\\-_]{35}"
174
+ ]
175
+ },
176
+ "detection": {
177
+ "clawmoat_module": "scanners/secrets",
178
+ "clawmoat_function": "scanSecrets",
179
+ "rules_triggered": [
180
+ "gcp_api_key"
181
+ ]
182
+ },
183
+ "mitigation": "Scope API keys to minimum required permissions. Set billing alerts. Rotate keys regularly. Use secret managers (GCP Secret Manager, AWS Secrets Manager) instead of embedding keys in code.",
184
+ "references": [
185
+ "https://www.cloudquery.io/blog/gcp-gemini-api-key-audit"
186
+ ]
187
+ },
188
+ {
189
+ "id": "CLAWMOAT-2026-0005",
190
+ "title": "Trivy Supply Chain Compromise — CI/CD Security Scanner Poisoned",
191
+ "published": "2026-03-19T00:00:00Z",
192
+ "updated": "2026-03-24T00:00:00Z",
193
+ "severity": "critical",
194
+ "category": "supply-chain",
195
+ "tags": [
196
+ "trivy",
197
+ "ci-cd",
198
+ "supply-chain",
199
+ "github-actions",
200
+ "devops"
201
+ ],
202
+ "summary": "TeamPCP compromised the Trivy security scanner used in CI/CD pipelines. By poisoning a security tool, attackers gained publish access to downstream packages including LiteLLM (97M monthly downloads). Attack chain: Trivy → CI/CD token → PyPI publish token → poisoned package → credential harvest.",
203
+ "affected": {
204
+ "packages": [
205
+ "trivy (affected CI/CD versions)",
206
+ "litellm==1.82.7",
207
+ "litellm==1.82.8"
208
+ ],
209
+ "ecosystems": [
210
+ "pypi",
211
+ "github-actions",
212
+ "ci-cd"
213
+ ]
214
+ },
215
+ "ioc": {
216
+ "domains": [
217
+ "models.litellm.cloud"
218
+ ],
219
+ "files": [
220
+ "litellm_init.pth"
221
+ ],
222
+ "hashes": [],
223
+ "patterns": [
224
+ "TeamPCP"
225
+ ]
226
+ },
227
+ "detection": {
228
+ "clawmoat_module": "scanners/supply-chain",
229
+ "clawmoat_function": "scanSkill",
230
+ "rules_triggered": [
231
+ "supply_chain_untrusted_source",
232
+ "obfuscated_exec_b64"
233
+ ]
234
+ },
235
+ "mitigation": "Pin all CI/CD tool versions. Verify checksums of security tools before use. Scope CI/CD tokens to minimum permissions — PyPI publish tokens should be package-scoped. Rotate all tokens after any dependency upgrade.",
236
+ "references": [
237
+ "https://ramimac.me/trivy-teampcp/",
238
+ "https://news.ycombinator.com/item?id=47450142"
239
+ ]
240
+ },
241
+ {
242
+ "id": "CLAWMOAT-2026-0006",
243
+ "title": "AnythingLLM SQL Injection in SQL Agent (CVE-2026-32628)",
244
+ "published": "2026-03-26T00:00:00Z",
245
+ "updated": "2026-03-26T00:00:00Z",
246
+ "severity": "high",
247
+ "category": "excessive-agency",
248
+ "tags": [
249
+ "sql-injection",
250
+ "ai-agent",
251
+ "anythingllm",
252
+ "database",
253
+ "cve"
254
+ ],
255
+ "summary": "CVE-2026-32628: High-severity SQL injection in AnythingLLM (56K GitHub stars) built-in SQL Agent affects MySQL, PostgreSQL, and MSSQL connectors. The agent has direct DB access and the flaw allows attackers to execute arbitrary SQL through it.",
256
+ "affected": {
257
+ "packages": [
258
+ "anythingllm"
259
+ ],
260
+ "ecosystems": [
261
+ "npm",
262
+ "docker"
263
+ ]
264
+ },
265
+ "ioc": {
266
+ "domains": [],
267
+ "files": [],
268
+ "hashes": [],
269
+ "patterns": [
270
+ "SELECT * FROM",
271
+ "UNION SELECT",
272
+ "DROP TABLE",
273
+ "1=1"
274
+ ]
275
+ },
276
+ "detection": {
277
+ "clawmoat_module": "scanners/excessive-agency",
278
+ "clawmoat_function": "scanExcessiveAgency",
279
+ "rules_triggered": [
280
+ "excessive_data_access",
281
+ "unbounded_db_query",
282
+ "sql_injection_pattern"
283
+ ]
284
+ },
285
+ "mitigation": "Update AnythingLLM immediately. Restrict SQL Agent to read-only database user. Never grant AI agents DROP/DELETE/ALTER permissions.",
286
+ "references": [
287
+ "https://hackernoon.com/a-56000-star-ai-app-shipped-with-a-textbook-sql-injection-flaw"
288
+ ]
289
+ }
290
+ ]
package/server/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  const http = require('http');
2
+ const fs = require('fs');
3
+ const path = require('path');
2
4
  const Stripe = require('stripe');
3
5
 
4
6
  const stripe = Stripe(process.env.STRIPE_SECRET_KEY);
@@ -6,17 +8,62 @@ const PORT = process.env.PORT || 3000;
6
8
  const SITE_URL = process.env.SITE_URL || 'https://clawmoat.com';
7
9
 
8
10
  const PRICES = {
11
+ // Security Kit (one-time purchase)
12
+ 'security-kit': process.env.PRICE_SECURITY_KIT || 'price_1T5F3LAUiOw2ZIorTAPB0Q76', // $29 one-time (legacy)
13
+ 'dev-monthly': process.env.PRICE_DEV_MONTHLY || 'price_1TFnOIAUiOw2ZIor6V5PdXBx', // $9/mo
14
+ 'dev-yearly': process.env.PRICE_DEV_YEARLY || 'price_1TFnOIAUiOw2ZIorJhG9KYZX', // $90/yr
9
15
  // Pro subscriptions
10
- 'shield-monthly': process.env.PRICE_SHIELD_MONTHLY || 'price_1T0an4AUiOw2ZIorxQRyAxvQ', // $14.99/mo
11
- 'shield-yearly': process.env.PRICE_SHIELD_YEARLY || 'price_1T0an4AUiOw2ZIorfHx7RowT', // $149/yr
16
+ 'shield-monthly': process.env.PRICE_SHIELD_MONTHLY || 'price_1T5F23AUiOw2ZIor2oUgTD8W', // $14.99/mo (legacy)
17
+ 'shield-yearly': process.env.PRICE_SHIELD_YEARLY || 'price_1T5F23AUiOw2ZIorQLdy51G0', // $149/yr (legacy)
12
18
  // Team subscriptions
13
- 'team-monthly': process.env.PRICE_TEAM_MONTHLY || 'price_1T0aqrAUiOw2ZIorh4gjBPGt', // $49/mo
14
- 'team-yearly': process.env.PRICE_TEAM_YEARLY || 'price_1T0asRAUiOw2ZIorxAi69uwl', // $499/yr
19
+ 'team-monthly': process.env.PRICE_TEAM_MONTHLY || 'price_1TFnOJAUiOw2ZIorunRX8OKm', // $49/mo
20
+ 'team-yearly': process.env.PRICE_TEAM_YEARLY || 'price_1TFnOJAUiOw2ZIorlOfiCnNk', // $490/yr
15
21
  };
16
22
 
23
+ const ONE_TIME_PLANS = new Set(['security-kit']);
24
+
17
25
  // In-memory license store (replace with DB in production)
18
26
  const licenses = new Map();
19
27
 
28
+ // ─── Threat Intel helpers ─────────────────────────────────────────────────────
29
+
30
+ const THREATS_PATH = path.join(__dirname, 'data/threats.json');
31
+ const API_KEYS_PATH = path.join(__dirname, 'data/api-keys.json');
32
+
33
+ function loadThreats() {
34
+ try { return JSON.parse(fs.readFileSync(THREATS_PATH, 'utf8')); }
35
+ catch { return []; }
36
+ }
37
+
38
+ function loadApiKeys() {
39
+ try { return JSON.parse(fs.readFileSync(API_KEYS_PATH, 'utf8')); }
40
+ catch { return {}; }
41
+ }
42
+
43
+ function saveApiKeys(keys) {
44
+ fs.writeFileSync(API_KEYS_PATH, JSON.stringify(keys, null, 2));
45
+ }
46
+
47
+ function checkApiKey(req) {
48
+ const key = req.headers['x-api-key'];
49
+ if (!key) return null;
50
+ const keys = loadApiKeys();
51
+ return keys[key] ? { key, ...keys[key] } : null;
52
+ }
53
+
54
+ function trackApiUsage(apiKey) {
55
+ const keys = loadApiKeys();
56
+ if (keys[apiKey]) {
57
+ keys[apiKey].calls_this_month = (keys[apiKey].calls_this_month || 0) + 1;
58
+ saveApiKeys(keys);
59
+ }
60
+ }
61
+
62
+ function parseQuery(url) {
63
+ const u = new URL(url, 'http://localhost');
64
+ return Object.fromEntries(u.searchParams.entries());
65
+ }
66
+
20
67
  function generateLicenseKey() {
21
68
  const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
22
69
  const segments = [];
@@ -70,22 +117,27 @@ const server = http.createServer(async (req, res) => {
70
117
  const priceId = PRICES[body.plan];
71
118
 
72
119
  if (!priceId) {
73
- return json(res, 400, { error: 'Invalid plan. Use: shield-monthly, shield-yearly, team-monthly, team-yearly' });
120
+ return json(res, 400, { error: 'Invalid plan. Use: dev-monthly, dev-yearly, team-monthly, team-yearly' });
74
121
  }
75
122
 
76
123
  try {
124
+ const isOneTime = ONE_TIME_PLANS.has(body.plan);
77
125
  const sessionParams = {
78
- mode: 'subscription',
126
+ mode: isOneTime ? 'payment' : 'subscription',
79
127
  line_items: [{ price: priceId, quantity: 1 }],
80
- success_url: `${SITE_URL}/thanks.html?session_id={CHECKOUT_SESSION_ID}`,
128
+ success_url: `https://app.clawmoat.com/dashboard?welcome=true&session_id={CHECKOUT_SESSION_ID}`,
81
129
  cancel_url: `${SITE_URL}/#pricing`,
82
130
  allow_promotion_codes: true,
83
131
  customer_email: body.email || undefined,
84
- subscription_data: {
132
+ };
133
+ if (!isOneTime) {
134
+ sessionParams.subscription_data = {
85
135
  trial_period_days: 30,
86
136
  metadata: { plan: body.plan },
87
- },
88
- };
137
+ };
138
+ } else {
139
+ sessionParams.metadata = { plan: body.plan };
140
+ }
89
141
  const session = await stripe.checkout.sessions.create(sessionParams);
90
142
  return json(res, 200, { url: session.url });
91
143
  } catch (err) {
@@ -154,6 +206,168 @@ const server = http.createServer(async (req, res) => {
154
206
  return json(res, 200, { received: true });
155
207
  }
156
208
 
209
+ // Live stats endpoint (cached 15 min)
210
+ if (req.method === 'GET' && req.url === '/api/stats') {
211
+ res.setHeader('Access-Control-Allow-Origin', '*');
212
+
213
+ const CACHE_TTL = 15 * 60 * 1000; // 15 minutes
214
+ const now = Date.now();
215
+
216
+ if (global._statsCache && (now - global._statsCacheTime) < CACHE_TTL) {
217
+ return json(res, 200, global._statsCache);
218
+ }
219
+
220
+ try {
221
+ const https = require('https');
222
+ const fetchJSON = (url) => new Promise((resolve, reject) => {
223
+ https.get(url, { headers: { 'User-Agent': 'ClawMoat-Stats/1.0' } }, (r) => {
224
+ let data = '';
225
+ r.on('data', c => data += c);
226
+ r.on('end', () => { try { resolve(JSON.parse(data)); } catch { resolve(null); } });
227
+ }).on('error', reject);
228
+ });
229
+
230
+ const [npmWeek, npmTotal] = await Promise.all([
231
+ fetchJSON('https://api.npmjs.org/downloads/point/last-week/clawmoat'),
232
+ fetchJSON('https://api.npmjs.org/downloads/point/2026-01-01:2099-12-31/clawmoat'),
233
+ ]);
234
+
235
+ // GitHub stats (public API, no auth needed)
236
+ const ghRepo = await fetchJSON('https://api.github.com/repos/darfaz/clawmoat');
237
+
238
+ // Try to get clone stats (needs auth, may fail on public API)
239
+ let clones = 0;
240
+ try {
241
+ const ghClones = await fetchJSON('https://api.github.com/repos/darfaz/clawmoat/traffic/clones');
242
+ clones = ghClones?.count || 0;
243
+ } catch {}
244
+
245
+ const stats = {
246
+ npm_downloads_week: npmWeek?.downloads || 0,
247
+ npm_downloads_total: npmTotal?.downloads || 0,
248
+ github_stars: ghRepo?.stargazers_count || 0,
249
+ github_forks: ghRepo?.forks_count || 0,
250
+ github_issues: ghRepo?.open_issues_count || 0,
251
+ github_clones: clones || 870, // fallback to last known if API requires auth
252
+ total: (npmTotal?.downloads || 0) + (clones || 870) + (ghRepo?.forks_count || 0),
253
+ updated_at: new Date().toISOString(),
254
+ };
255
+
256
+ global._statsCache = stats;
257
+ global._statsCacheTime = now;
258
+
259
+ return json(res, 200, stats);
260
+ } catch (err) {
261
+ return json(res, 200, global._statsCache || { error: 'Stats temporarily unavailable' });
262
+ }
263
+ }
264
+
265
+ // Contact form (Business inquiries)
266
+ if (req.method === 'POST' && req.url === '/api/contact') {
267
+ const body = await readBody(req);
268
+ const { name, email, company, teamSize, agents, concerns } = body;
269
+ if (!email) return json(res, 400, { error: 'Email required' });
270
+
271
+ console.log(`🏢 Business inquiry: ${name} <${email}> @ ${company} (${teamSize}, ${agents} agents)`);
272
+ console.log(` Concerns: ${concerns}`);
273
+
274
+ // TODO: Send notification email, store in CRM
275
+ // For now, log it — we'll check server logs
276
+ return json(res, 200, { success: true, message: 'Thank you! We\'ll be in touch within 24 hours.' });
277
+ }
278
+
279
+ // ─── Threat Intel API ──────────────────────────────────────────────────────
280
+
281
+ // GET /api/v1/threats
282
+ if (req.method === 'GET' && req.url.startsWith('/api/v1/threats')) {
283
+ const apiUser = checkApiKey(req);
284
+ if (!apiUser) return json(res, 401, { error: 'API key required. Add X-API-Key header. Get a free key at clawmoat.com/api' });
285
+
286
+ // Rate limit free tier
287
+ if (apiUser.tier === 'free' && apiUser.calls_this_month >= (apiUser.monthly_limit || 100)) {
288
+ return json(res, 429, { error: 'Free tier limit reached (100 calls/month). Upgrade at clawmoat.com/#pricing' });
289
+ }
290
+
291
+ trackApiUsage(apiUser.key);
292
+ const threats = loadThreats();
293
+
294
+ // Single threat by id: /api/v1/threats/CLAWMOAT-2026-0001
295
+ const idMatch = req.url.match(/^\/api\/v1\/threats\/([^?]+)/);
296
+ if (idMatch) {
297
+ const threat = threats.find(t => t.id === idMatch[1]);
298
+ if (!threat) return json(res, 404, { error: 'Threat not found' });
299
+ return json(res, 200, { threat });
300
+ }
301
+
302
+ // List with filters
303
+ const q = parseQuery(req.url);
304
+ let filtered = threats;
305
+ if (q.category) filtered = filtered.filter(t => t.category === q.category);
306
+ if (q.severity) filtered = filtered.filter(t => t.severity === q.severity);
307
+ if (q.tags) filtered = filtered.filter(t => q.tags.split(',').every(tag => t.tags.includes(tag)));
308
+ if (q.since) filtered = filtered.filter(t => t.published >= q.since);
309
+ const limit = Math.min(parseInt(q.limit) || 20, 100);
310
+ filtered = filtered.slice(0, limit);
311
+
312
+ const updated = threats.reduce((max, t) => t.updated > max ? t.updated : max, '');
313
+ return json(res, 200, { threats: filtered, count: filtered.length, total: threats.length, updated });
314
+ }
315
+
316
+ // GET /api/v1/ioc — aggregated indicators of compromise
317
+ if (req.method === 'GET' && req.url.startsWith('/api/v1/ioc')) {
318
+ const apiUser = checkApiKey(req);
319
+ if (!apiUser) return json(res, 401, { error: 'API key required. Add X-API-Key header. Get a free key at clawmoat.com/api' });
320
+
321
+ if (apiUser.tier === 'free' && apiUser.calls_this_month >= (apiUser.monthly_limit || 100)) {
322
+ return json(res, 429, { error: 'Free tier limit reached (100 calls/month). Upgrade at clawmoat.com/#pricing' });
323
+ }
324
+
325
+ trackApiUsage(apiUser.key);
326
+ const threats = loadThreats();
327
+
328
+ const ioc = { domains: new Set(), files: new Set(), patterns: new Set() };
329
+ for (const t of threats) {
330
+ (t.ioc?.domains || []).forEach(d => ioc.domains.add(d));
331
+ (t.ioc?.files || []).forEach(f => ioc.files.add(f));
332
+ (t.ioc?.patterns || []).forEach(p => ioc.patterns.add(p));
333
+ }
334
+
335
+ const updated = threats.reduce((max, t) => t.updated > max ? t.updated : max, '');
336
+ return json(res, 200, {
337
+ domains: [...ioc.domains],
338
+ files: [...ioc.files],
339
+ patterns: [...ioc.patterns],
340
+ threat_count: threats.length,
341
+ updated,
342
+ });
343
+ }
344
+
345
+ // POST /api/v1/keys — generate free API key
346
+ if (req.method === 'POST' && req.url === '/api/v1/keys') {
347
+ const body = await readBody(req);
348
+ if (!body.email) return json(res, 400, { error: 'email required' });
349
+
350
+ const newKey = 'cm-' + require('crypto').randomBytes(16).toString('hex');
351
+ const keys = loadApiKeys();
352
+ keys[newKey] = {
353
+ tier: 'free',
354
+ email: body.email,
355
+ calls_this_month: 0,
356
+ monthly_limit: 100,
357
+ created: new Date().toISOString().slice(0, 10),
358
+ label: 'Free tier — ' + body.email,
359
+ };
360
+ saveApiKeys(keys);
361
+ console.log(`New API key issued: ${body.email}`);
362
+ return json(res, 201, {
363
+ api_key: newKey,
364
+ tier: 'free',
365
+ monthly_limit: 100,
366
+ docs: 'https://clawmoat.com/docs/api',
367
+ });
368
+ }
369
+
370
+ // ─── License validation endpoint (called by CLI) ──────────────────────────
157
371
  // License validation endpoint (called by CLI)
158
372
  if (req.method === 'POST' && req.url === '/api/validate') {
159
373
  const body = await readBody(req);