@poolzin/pool-bot 2026.3.13 → 2026.3.15

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 (186) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/dist/agents/checkpoint-manager.js +291 -0
  3. package/dist/agents/poolbot-tools.js +5 -0
  4. package/dist/agents/subagent-announce-reliability.js +160 -0
  5. package/dist/agents/tool-result-truncation.js +299 -0
  6. package/dist/agents/tools/nodes-file-tool.js +197 -0
  7. package/dist/build-info.json +3 -3
  8. package/dist/cli/config-cli.js +60 -0
  9. package/dist/cron/cron-improvements.js +195 -0
  10. package/dist/discord/discord-improvements.js +167 -0
  11. package/dist/gateway/auth-rate-limit.js +19 -0
  12. package/dist/gateway/auth.js +41 -0
  13. package/dist/gateway/gateway-improvements.js +294 -0
  14. package/dist/gateway/node-command-policy.js +7 -2
  15. package/dist/infra/net/ssrf.js +15 -2
  16. package/dist/infra/shell-security.js +201 -0
  17. package/dist/memory/memory-improvements.js +239 -0
  18. package/dist/node-host/runner.js +146 -79
  19. package/dist/security/prototype-pollution.js +141 -0
  20. package/dist/security/webhook-security.js +253 -0
  21. package/dist/shared/net/ip.js +52 -1
  22. package/dist/slack/slack-improvements.js +225 -0
  23. package/dist/telegram/telegram-improvements.js +220 -0
  24. package/dist/ui-plugins/ui-plugins-improvements.js +191 -0
  25. package/docs/ANALISE_OPENCLAW_PROFISSIONAL.md +520 -0
  26. package/docs/competitive-analysis.md +421 -0
  27. package/docs/implementation-analysis.md +393 -0
  28. package/docs/plans/2026-03-11-file-operations-security-hardening.md +307 -0
  29. package/docs/plans/2026-03-11-integracao-projetos-poolbot.md +666 -0
  30. package/extensions/agency-agents/README.md +301 -0
  31. package/extensions/agency-agents/agents/CONTRIBUTING.md +353 -0
  32. package/extensions/agency-agents/agents/README.md +602 -0
  33. package/extensions/agency-agents/agents/design/design-brand-guardian.md +320 -0
  34. package/extensions/agency-agents/agents/design/design-image-prompt-engineer.md +234 -0
  35. package/extensions/agency-agents/agents/design/design-ui-designer.md +381 -0
  36. package/extensions/agency-agents/agents/design/design-ux-architect.md +467 -0
  37. package/extensions/agency-agents/agents/design/design-ux-researcher.md +327 -0
  38. package/extensions/agency-agents/agents/design/design-visual-storyteller.md +147 -0
  39. package/extensions/agency-agents/agents/design/design-whimsy-injector.md +436 -0
  40. package/extensions/agency-agents/agents/engineering/engineering-ai-engineer.md +144 -0
  41. package/extensions/agency-agents/agents/engineering/engineering-backend-architect.md +233 -0
  42. package/extensions/agency-agents/agents/engineering/engineering-devops-automator.md +374 -0
  43. package/extensions/agency-agents/agents/engineering/engineering-frontend-developer.md +223 -0
  44. package/extensions/agency-agents/agents/engineering/engineering-mobile-app-builder.md +491 -0
  45. package/extensions/agency-agents/agents/engineering/engineering-rapid-prototyper.md +460 -0
  46. package/extensions/agency-agents/agents/engineering/engineering-security-engineer.md +275 -0
  47. package/extensions/agency-agents/agents/engineering/engineering-senior-developer.md +174 -0
  48. package/extensions/agency-agents/agents/examples/README.md +48 -0
  49. package/extensions/agency-agents/agents/examples/nexus-spatial-discovery.md +852 -0
  50. package/extensions/agency-agents/agents/examples/workflow-landing-page.md +119 -0
  51. package/extensions/agency-agents/agents/examples/workflow-startup-mvp.md +155 -0
  52. package/extensions/agency-agents/agents/integrations/README.md +117 -0
  53. package/extensions/agency-agents/agents/integrations/aider/README.md +38 -0
  54. package/extensions/agency-agents/agents/integrations/antigravity/README.md +49 -0
  55. package/extensions/agency-agents/agents/integrations/claude-code/README.md +31 -0
  56. package/extensions/agency-agents/agents/integrations/cursor/README.md +38 -0
  57. package/extensions/agency-agents/agents/integrations/gemini-cli/README.md +36 -0
  58. package/extensions/agency-agents/agents/integrations/opencode/README.md +58 -0
  59. package/extensions/agency-agents/agents/integrations/windsurf/README.md +26 -0
  60. package/extensions/agency-agents/agents/marketing/marketing-app-store-optimizer.md +319 -0
  61. package/extensions/agency-agents/agents/marketing/marketing-content-creator.md +52 -0
  62. package/extensions/agency-agents/agents/marketing/marketing-growth-hacker.md +52 -0
  63. package/extensions/agency-agents/agents/marketing/marketing-instagram-curator.md +111 -0
  64. package/extensions/agency-agents/agents/marketing/marketing-reddit-community-builder.md +121 -0
  65. package/extensions/agency-agents/agents/marketing/marketing-social-media-strategist.md +123 -0
  66. package/extensions/agency-agents/agents/marketing/marketing-tiktok-strategist.md +123 -0
  67. package/extensions/agency-agents/agents/marketing/marketing-twitter-engager.md +124 -0
  68. package/extensions/agency-agents/agents/marketing/marketing-wechat-official-account.md +143 -0
  69. package/extensions/agency-agents/agents/marketing/marketing-xiaohongshu-specialist.md +136 -0
  70. package/extensions/agency-agents/agents/marketing/marketing-zhihu-strategist.md +160 -0
  71. package/extensions/agency-agents/agents/product/product-feedback-synthesizer.md +117 -0
  72. package/extensions/agency-agents/agents/product/product-sprint-prioritizer.md +152 -0
  73. package/extensions/agency-agents/agents/product/product-trend-researcher.md +157 -0
  74. package/extensions/agency-agents/agents/project-management/project-management-experiment-tracker.md +196 -0
  75. package/extensions/agency-agents/agents/project-management/project-management-project-shepherd.md +192 -0
  76. package/extensions/agency-agents/agents/project-management/project-management-studio-operations.md +198 -0
  77. package/extensions/agency-agents/agents/project-management/project-management-studio-producer.md +201 -0
  78. package/extensions/agency-agents/agents/project-management/project-manager-senior.md +133 -0
  79. package/extensions/agency-agents/agents/scripts/convert.sh +362 -0
  80. package/extensions/agency-agents/agents/scripts/install.sh +465 -0
  81. package/extensions/agency-agents/agents/scripts/lint-agents.sh +115 -0
  82. package/extensions/agency-agents/agents/spatial-computing/macos-spatial-metal-engineer.md +335 -0
  83. package/extensions/agency-agents/agents/spatial-computing/terminal-integration-specialist.md +68 -0
  84. package/extensions/agency-agents/agents/spatial-computing/visionos-spatial-engineer.md +52 -0
  85. package/extensions/agency-agents/agents/spatial-computing/xr-cockpit-interaction-specialist.md +30 -0
  86. package/extensions/agency-agents/agents/spatial-computing/xr-immersive-developer.md +30 -0
  87. package/extensions/agency-agents/agents/spatial-computing/xr-interface-architect.md +30 -0
  88. package/extensions/agency-agents/agents/specialized/agentic-identity-trust.md +367 -0
  89. package/extensions/agency-agents/agents/specialized/agents-orchestrator.md +365 -0
  90. package/extensions/agency-agents/agents/specialized/data-analytics-reporter.md +52 -0
  91. package/extensions/agency-agents/agents/specialized/data-consolidation-agent.md +58 -0
  92. package/extensions/agency-agents/agents/specialized/lsp-index-engineer.md +312 -0
  93. package/extensions/agency-agents/agents/specialized/report-distribution-agent.md +63 -0
  94. package/extensions/agency-agents/agents/specialized/sales-data-extraction-agent.md +65 -0
  95. package/extensions/agency-agents/agents/strategy/EXECUTIVE-BRIEF.md +95 -0
  96. package/extensions/agency-agents/agents/strategy/QUICKSTART.md +194 -0
  97. package/extensions/agency-agents/agents/strategy/coordination/agent-activation-prompts.md +401 -0
  98. package/extensions/agency-agents/agents/strategy/coordination/handoff-templates.md +357 -0
  99. package/extensions/agency-agents/agents/strategy/nexus-strategy.md +1110 -0
  100. package/extensions/agency-agents/agents/strategy/playbooks/phase-0-discovery.md +178 -0
  101. package/extensions/agency-agents/agents/strategy/playbooks/phase-1-strategy.md +238 -0
  102. package/extensions/agency-agents/agents/strategy/playbooks/phase-2-foundation.md +278 -0
  103. package/extensions/agency-agents/agents/strategy/playbooks/phase-3-build.md +286 -0
  104. package/extensions/agency-agents/agents/strategy/playbooks/phase-4-hardening.md +332 -0
  105. package/extensions/agency-agents/agents/strategy/playbooks/phase-5-launch.md +277 -0
  106. package/extensions/agency-agents/agents/strategy/playbooks/phase-6-operate.md +318 -0
  107. package/extensions/agency-agents/agents/strategy/runbooks/scenario-enterprise-feature.md +157 -0
  108. package/extensions/agency-agents/agents/strategy/runbooks/scenario-incident-response.md +217 -0
  109. package/extensions/agency-agents/agents/strategy/runbooks/scenario-marketing-campaign.md +187 -0
  110. package/extensions/agency-agents/agents/strategy/runbooks/scenario-startup-mvp.md +154 -0
  111. package/extensions/agency-agents/agents/support/support-analytics-reporter.md +363 -0
  112. package/extensions/agency-agents/agents/support/support-executive-summary-generator.md +210 -0
  113. package/extensions/agency-agents/agents/support/support-finance-tracker.md +440 -0
  114. package/extensions/agency-agents/agents/support/support-infrastructure-maintainer.md +616 -0
  115. package/extensions/agency-agents/agents/support/support-legal-compliance-checker.md +586 -0
  116. package/extensions/agency-agents/agents/support/support-support-responder.md +583 -0
  117. package/extensions/agency-agents/agents/testing/testing-accessibility-auditor.md +313 -0
  118. package/extensions/agency-agents/agents/testing/testing-api-tester.md +304 -0
  119. package/extensions/agency-agents/agents/testing/testing-evidence-collector.md +208 -0
  120. package/extensions/agency-agents/agents/testing/testing-performance-benchmarker.md +266 -0
  121. package/extensions/agency-agents/agents/testing/testing-reality-checker.md +236 -0
  122. package/extensions/agency-agents/agents/testing/testing-test-results-analyzer.md +303 -0
  123. package/extensions/agency-agents/agents/testing/testing-tool-evaluator.md +392 -0
  124. package/extensions/agency-agents/agents/testing/testing-workflow-optimizer.md +448 -0
  125. package/extensions/agency-agents/index.ts +733 -0
  126. package/extensions/agency-agents/node_modules/.bin/jiti +21 -0
  127. package/extensions/agency-agents/node_modules/.bin/tsc +21 -0
  128. package/extensions/agency-agents/node_modules/.bin/tsserver +21 -0
  129. package/extensions/agency-agents/node_modules/.bin/tsx +21 -0
  130. package/extensions/agency-agents/node_modules/.bin/vite +21 -0
  131. package/extensions/agency-agents/node_modules/.bin/vitest +21 -0
  132. package/extensions/agency-agents/node_modules/.bin/yaml +21 -0
  133. package/extensions/agency-agents/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  134. package/extensions/agency-agents/package.json +25 -0
  135. package/extensions/agency-agents/poolbot.plugin.json +11 -0
  136. package/extensions/agency-agents/src/AgencyAgentsService.test.ts +443 -0
  137. package/extensions/agency-agents/src/AgencyAgentsService.ts +288 -0
  138. package/extensions/agency-agents/src/types.ts +147 -0
  139. package/extensions/agency-agents/vitest.config.ts +8 -0
  140. package/extensions/hexstrike-ai/README.md +98 -0
  141. package/extensions/hexstrike-ai/node_modules/.bin/tsc +21 -0
  142. package/extensions/hexstrike-ai/node_modules/.bin/tsserver +21 -0
  143. package/extensions/hexstrike-ai/package.json +29 -0
  144. package/extensions/hexstrike-ai/poolbot.plugin.json +31 -0
  145. package/extensions/hexstrike-ai/src/client.ts +91 -0
  146. package/extensions/hexstrike-ai/src/index.ts +170 -0
  147. package/extensions/hexstrike-ai/src/server/hexstrike_mcp.py +5470 -0
  148. package/extensions/hexstrike-ai/src/server/hexstrike_server.py +17289 -0
  149. package/extensions/hexstrike-ai/src/server/requirements.txt +84 -0
  150. package/extensions/hexstrike-ai/src/server-manager.ts +83 -0
  151. package/extensions/hexstrike-ai/tsconfig.json +20 -0
  152. package/extensions/page-agent/README.md +159 -0
  153. package/extensions/page-agent/index.ts +595 -0
  154. package/extensions/page-agent/node_modules/.bin/jiti +21 -0
  155. package/extensions/page-agent/node_modules/.bin/playwright +21 -0
  156. package/extensions/page-agent/node_modules/.bin/tsc +21 -0
  157. package/extensions/page-agent/node_modules/.bin/tsserver +21 -0
  158. package/extensions/page-agent/node_modules/.bin/tsx +21 -0
  159. package/extensions/page-agent/node_modules/.bin/vitest +21 -0
  160. package/extensions/page-agent/node_modules/.bin/yaml +21 -0
  161. package/extensions/page-agent/package.json +43 -0
  162. package/extensions/page-agent/poolbot.plugin.json +24 -0
  163. package/extensions/page-agent/src/PageAgentService.test.ts +517 -0
  164. package/extensions/page-agent/src/PageAgentService.ts +636 -0
  165. package/extensions/page-agent/src/PoolBotPageController.test.ts +358 -0
  166. package/extensions/page-agent/src/PoolBotPageController.ts +245 -0
  167. package/extensions/page-agent/src/index.ts +20 -0
  168. package/extensions/page-agent/src/tools.test.ts +231 -0
  169. package/extensions/page-agent/src/tools.ts +167 -0
  170. package/extensions/page-agent/src/types.ts +198 -0
  171. package/extensions/xyops/README.md +227 -0
  172. package/extensions/xyops/index.ts +342 -0
  173. package/extensions/xyops/node_modules/.bin/jiti +21 -0
  174. package/extensions/xyops/node_modules/.bin/tsc +21 -0
  175. package/extensions/xyops/node_modules/.bin/tsserver +21 -0
  176. package/extensions/xyops/node_modules/.bin/tsx +21 -0
  177. package/extensions/xyops/node_modules/.bin/vitest +21 -0
  178. package/extensions/xyops/node_modules/.bin/yaml +21 -0
  179. package/extensions/xyops/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  180. package/extensions/xyops/package.json +39 -0
  181. package/extensions/xyops/poolbot.plugin.json +21 -0
  182. package/extensions/xyops/src/client.test.ts +467 -0
  183. package/extensions/xyops/src/client.ts +157 -0
  184. package/extensions/xyops/src/types.ts +147 -0
  185. package/extensions/xyops/vitest.config.ts +8 -0
  186. package/package.json +1 -1
@@ -0,0 +1,294 @@
1
+ /**
2
+ * Gateway & Auth Upgrades
3
+ *
4
+ * Implements OpenClaw improvements:
5
+ * - Device-auth v2 with nonce-based signing
6
+ * - Trusted-proxy mode with loopback allowance
7
+ * - Control UI CORS with wildcard support
8
+ * - Security headers (HSTS, Permissions-Policy)
9
+ * - Rate limiting for sensitive endpoints
10
+ *
11
+ * OpenClaw #32384, #30951
12
+ */
13
+ import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
14
+ export const DEFAULT_DEVICE_AUTH_V2_CONFIG = {
15
+ enabled: true,
16
+ nonceLength: 32,
17
+ nonceTtlMs: 5 * 60 * 1000, // 5 minutes
18
+ signatureAlgorithm: "sha256",
19
+ requireNonce: false, // Allow v1 for backward compatibility
20
+ allowV1Fallback: true,
21
+ };
22
+ const nonceStore = new Map();
23
+ /**
24
+ * Cleanup expired nonces every 10 minutes
25
+ */
26
+ setInterval(() => {
27
+ const now = Date.now();
28
+ const maxAge = 10 * 60 * 1000; // 10 minutes
29
+ for (const [deviceId, entry] of nonceStore.entries()) {
30
+ if (now - entry.createdAt > maxAge) {
31
+ nonceStore.delete(deviceId);
32
+ }
33
+ }
34
+ }, 10 * 60 * 1000).unref();
35
+ /**
36
+ * Generate a new nonce for device-auth v2
37
+ */
38
+ export function generateNonce(config = DEFAULT_DEVICE_AUTH_V2_CONFIG) {
39
+ return randomBytes(config.nonceLength).toString("hex");
40
+ }
41
+ /**
42
+ * Store nonce for device-auth v2
43
+ */
44
+ export function storeNonce(params) {
45
+ const { deviceId, nonce, config } = params;
46
+ nonceStore.set(deviceId, {
47
+ nonce,
48
+ createdAt: Date.now(),
49
+ used: false,
50
+ });
51
+ // Auto-expire after TTL
52
+ setTimeout(() => {
53
+ const entry = nonceStore.get(deviceId);
54
+ if (entry && entry.nonce === nonce) {
55
+ nonceStore.delete(deviceId);
56
+ }
57
+ }, config.nonceTtlMs).unref();
58
+ }
59
+ /**
60
+ * Verify nonce for device-auth v2
61
+ */
62
+ export function verifyNonce(params) {
63
+ const { deviceId, nonce, config } = params;
64
+ const entry = nonceStore.get(deviceId);
65
+ if (!entry) {
66
+ return { valid: false, reason: "Nonce not found or expired" };
67
+ }
68
+ if (entry.used) {
69
+ return { valid: false, reason: "Nonce already used (replay attempt)" };
70
+ }
71
+ if (Date.now() - entry.createdAt > config.nonceTtlMs) {
72
+ nonceStore.delete(deviceId);
73
+ return { valid: false, reason: "Nonce expired" };
74
+ }
75
+ if (!timingSafeEqual(Buffer.from(entry.nonce), Buffer.from(nonce))) {
76
+ return { valid: false, reason: "Nonce mismatch" };
77
+ }
78
+ // Mark as used
79
+ entry.used = true;
80
+ return { valid: true };
81
+ }
82
+ /**
83
+ * Build device-auth v2 payload with nonce
84
+ */
85
+ export function buildDeviceAuthV2Payload(params) {
86
+ const { deviceId, clientId, clientMode, role, scopes, signedAtMs, token, nonce } = params;
87
+ const scopesStr = scopes.join(",");
88
+ return `v2|${deviceId}|${clientId}|${clientMode}|${role}|${scopesStr}|${signedAtMs}|${token}|${nonce}`;
89
+ }
90
+ /**
91
+ * Sign device-auth v2 payload
92
+ */
93
+ export function signDeviceAuthV2Payload(params) {
94
+ const { payload, secret, algorithm } = params;
95
+ return createHmac(algorithm, secret).update(payload).digest("hex");
96
+ }
97
+ /**
98
+ * Verify device-auth v2 signature
99
+ */
100
+ export function verifyDeviceAuthV2Signature(params) {
101
+ const { payload, signature, secret, algorithm } = params;
102
+ try {
103
+ const expectedSignature = signDeviceAuthV2Payload({ payload, secret, algorithm });
104
+ const signatureBuffer = Buffer.from(signature, "hex");
105
+ const expectedBuffer = Buffer.from(expectedSignature, "hex");
106
+ if (signatureBuffer.length !== expectedBuffer.length) {
107
+ return { valid: false, reason: "Signature length mismatch" };
108
+ }
109
+ if (!timingSafeEqual(signatureBuffer, expectedBuffer)) {
110
+ return { valid: false, reason: "Signature mismatch" };
111
+ }
112
+ return { valid: true };
113
+ }
114
+ catch {
115
+ return { valid: false, reason: "Invalid signature format" };
116
+ }
117
+ }
118
+ export const DEFAULT_TRUSTED_PROXY_CONFIG = {
119
+ enabled: true,
120
+ trustedProxies: ["127.0.0.1", "::1"],
121
+ userHeader: "X-Forwarded-User",
122
+ requiredHeaders: [],
123
+ allowUsers: [],
124
+ allowLoopback: true,
125
+ autoApproveLoopbackScopes: true,
126
+ safeLoopbackScopes: ["read", "read:config", "read:status", "write", "execute", "execute:tool"],
127
+ };
128
+ /**
129
+ * Check if IP is a trusted proxy
130
+ */
131
+ export function isTrustedProxy(ip, config = DEFAULT_TRUSTED_PROXY_CONFIG) {
132
+ if (!config.enabled) {
133
+ return false;
134
+ }
135
+ return config.trustedProxies.includes(ip);
136
+ }
137
+ /**
138
+ * Check if IP is loopback
139
+ */
140
+ export function isLoopbackIp(ip) {
141
+ return ip === "127.0.0.1" || ip === "::1" || ip === "localhost";
142
+ }
143
+ /**
144
+ * Validate scope upgrade for loopback
145
+ */
146
+ export function validateLoopbackScopeUpgrade(params) {
147
+ const { currentScopes, requestedScopes, config } = params;
148
+ if (!config.autoApproveLoopbackScopes) {
149
+ return { allowed: false, reason: "Auto-approve for loopback is disabled" };
150
+ }
151
+ const currentSet = new Set(currentScopes.map((s) => s.trim().toLowerCase()));
152
+ const newScopes = requestedScopes.filter((s) => !currentSet.has(s.trim().toLowerCase()));
153
+ const safeScopes = new Set(config.safeLoopbackScopes.map((s) => s.trim().toLowerCase()));
154
+ for (const scope of newScopes) {
155
+ const normalized = scope.trim().toLowerCase();
156
+ if (!safeScopes.has(normalized) &&
157
+ !normalized.startsWith("read:") &&
158
+ !normalized.startsWith("execute:")) {
159
+ return { allowed: false, reason: `Scope "${scope}" requires explicit approval` };
160
+ }
161
+ }
162
+ return { allowed: true };
163
+ }
164
+ export const DEFAULT_CONTROL_UI_CORS_CONFIG = {
165
+ enabled: true,
166
+ allowedOrigins: ["*"], // Wildcard by default for local dev
167
+ allowedMethods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
168
+ allowedHeaders: ["Content-Type", "Authorization", "X-Request-ID"],
169
+ exposedHeaders: ["X-Request-ID", "X-RateLimit-Remaining"],
170
+ maxAgeSeconds: 86400, // 24 hours
171
+ allowCredentials: false,
172
+ wildcardSubdomains: true,
173
+ };
174
+ /**
175
+ * Check if origin is allowed
176
+ */
177
+ export function isOriginAllowed(origin, config = DEFAULT_CONTROL_UI_CORS_CONFIG) {
178
+ if (!config.enabled || !origin) {
179
+ return false;
180
+ }
181
+ // Wildcard allows all
182
+ if (config.allowedOrigins.includes("*")) {
183
+ return true;
184
+ }
185
+ // Check exact match
186
+ if (config.allowedOrigins.includes(origin)) {
187
+ return true;
188
+ }
189
+ // Check wildcard subdomains
190
+ if (config.wildcardSubdomains) {
191
+ for (const allowed of config.allowedOrigins) {
192
+ if (allowed.startsWith("*.")) {
193
+ const suffix = allowed.slice(1);
194
+ if (origin.endsWith(suffix)) {
195
+ return true;
196
+ }
197
+ }
198
+ }
199
+ }
200
+ return false;
201
+ }
202
+ /**
203
+ * Generate CORS headers
204
+ */
205
+ export function generateCorsHeaders(params) {
206
+ const { origin, config } = params;
207
+ const headers = {};
208
+ if (!config.enabled) {
209
+ return headers;
210
+ }
211
+ const allowedOrigin = isOriginAllowed(origin, config) ? (origin ?? "*") : "";
212
+ if (allowedOrigin) {
213
+ headers["Access-Control-Allow-Origin"] = allowedOrigin;
214
+ headers["Access-Control-Allow-Methods"] = config.allowedMethods.join(", ");
215
+ headers["Access-Control-Allow-Headers"] = config.allowedHeaders.join(", ");
216
+ headers["Access-Control-Expose-Headers"] = config.exposedHeaders.join(", ");
217
+ headers["Access-Control-Max-Age"] = String(config.maxAgeSeconds);
218
+ if (config.allowCredentials) {
219
+ headers["Access-Control-Allow-Credentials"] = "true";
220
+ }
221
+ }
222
+ return headers;
223
+ }
224
+ export const DEFAULT_SECURITY_HEADERS_CONFIG = {
225
+ enabled: true,
226
+ hstsMaxAgeSeconds: 31536000, // 1 year
227
+ hstsIncludeSubdomains: true,
228
+ hstsPreload: true,
229
+ permissionsPolicy: [
230
+ "accelerometer=()",
231
+ "camera=()",
232
+ "geolocation=()",
233
+ "gyroscope=()",
234
+ "magnetometer=()",
235
+ "microphone=()",
236
+ "payment=()",
237
+ "usb=()",
238
+ ],
239
+ xContentTypeOptions: true,
240
+ xFrameOptions: true,
241
+ xXssProtection: true,
242
+ referrerPolicy: "strict-origin-when-cross-origin",
243
+ };
244
+ /**
245
+ * Generate security headers
246
+ */
247
+ export function generateSecurityHeaders(config = DEFAULT_SECURITY_HEADERS_CONFIG) {
248
+ if (!config.enabled) {
249
+ return {};
250
+ }
251
+ const headers = {};
252
+ // HSTS
253
+ if (config.hstsMaxAgeSeconds > 0) {
254
+ let hsts = `max-age=${config.hstsMaxAgeSeconds}`;
255
+ if (config.hstsIncludeSubdomains) {
256
+ hsts += "; includeSubDomains";
257
+ }
258
+ if (config.hstsPreload) {
259
+ hsts += "; preload";
260
+ }
261
+ headers["Strict-Transport-Security"] = hsts;
262
+ }
263
+ // Permissions-Policy
264
+ if (config.permissionsPolicy.length > 0) {
265
+ headers["Permissions-Policy"] = config.permissionsPolicy.join(", ");
266
+ }
267
+ // X-Content-Type-Options
268
+ if (config.xContentTypeOptions) {
269
+ headers["X-Content-Type-Options"] = "nosniff";
270
+ }
271
+ // X-Frame-Options
272
+ if (config.xFrameOptions) {
273
+ headers["X-Frame-Options"] = "DENY";
274
+ }
275
+ // X-XSS-Protection
276
+ if (config.xXssProtection) {
277
+ headers["X-XSS-Protection"] = "1; mode=block";
278
+ }
279
+ // Content-Security-Policy
280
+ if (config.contentSecurityPolicy) {
281
+ headers["Content-Security-Policy"] = config.contentSecurityPolicy;
282
+ }
283
+ // Referrer-Policy
284
+ headers["Referrer-Policy"] = config.referrerPolicy;
285
+ return headers;
286
+ }
287
+ /**
288
+ * Combine CORS and security headers
289
+ */
290
+ export function generateGatewayHeaders(params) {
291
+ const corsHeaders = generateCorsHeaders({ origin: params.origin, config: params.corsConfig });
292
+ const securityHeaders = generateSecurityHeaders(params.securityConfig);
293
+ return { ...corsHeaders, ...securityHeaders };
294
+ }
@@ -32,6 +32,7 @@ const SYSTEM_COMMANDS = [
32
32
  "system.execApprovals.set",
33
33
  "browser.proxy",
34
34
  ];
35
+ const FILE_DANGEROUS_COMMANDS = ["file.write", "file.delete"];
35
36
  // "High risk" node commands. These can be enabled by explicitly adding them to
36
37
  // `gateway.nodes.allowCommands` (and ensuring they're not blocked by denyCommands).
37
38
  export const DEFAULT_DANGEROUS_NODE_COMMANDS = [
@@ -41,6 +42,7 @@ export const DEFAULT_DANGEROUS_NODE_COMMANDS = [
41
42
  ...CALENDAR_DANGEROUS_COMMANDS,
42
43
  ...REMINDERS_DANGEROUS_COMMANDS,
43
44
  ...SMS_DANGEROUS_COMMANDS,
45
+ ...FILE_DANGEROUS_COMMANDS,
44
46
  ];
45
47
  const PLATFORM_DEFAULTS = {
46
48
  ios: [
@@ -77,9 +79,12 @@ const PLATFORM_DEFAULTS = {
77
79
  ...PHOTOS_COMMANDS,
78
80
  ...MOTION_COMMANDS,
79
81
  ...SYSTEM_COMMANDS,
82
+ "file.read",
83
+ "file.exists",
84
+ "file.list",
80
85
  ],
81
- linux: [...SYSTEM_COMMANDS],
82
- windows: [...SYSTEM_COMMANDS],
86
+ linux: [...SYSTEM_COMMANDS, "file.read", "file.exists", "file.list"],
87
+ windows: [...SYSTEM_COMMANDS, "file.read", "file.exists", "file.list"],
83
88
  unknown: [...CANVAS_COMMANDS, ...CAMERA_COMMANDS, ...LOCATION_COMMANDS, ...SYSTEM_COMMANDS],
84
89
  };
85
90
  function normalizePlatformId(platform, deviceFamily) {
@@ -20,6 +20,16 @@ function normalizeHostnameSet(values) {
20
20
  }
21
21
  return new Set(values.map((value) => normalizeHostname(value)).filter(Boolean));
22
22
  }
23
+ /**
24
+ * Normalize hostname with Unicode NFKC folding to prevent homoglyph bypass attacks.
25
+ * This catches lookalike characters that could be used to bypass allowlist checks.
26
+ */
27
+ export function normalizeHostnameWithUnicode(value) {
28
+ // Apply NFKC normalization to fold homoglyphs and lookalike characters
29
+ const normalized = value.normalize("NFKC");
30
+ // Then apply standard hostname normalization
31
+ return normalizeHostname(normalized) || normalized.toLowerCase();
32
+ }
23
33
  function normalizeHostnameAllowlist(values) {
24
34
  if (!values || values.length === 0) {
25
35
  return [];
@@ -89,8 +99,11 @@ export function isPrivateIpAddress(address, policy) {
89
99
  return false;
90
100
  }
91
101
  // Security-critical parse failures should fail closed for any malformed IPv6 literal.
92
- if (normalized.includes(":") && !parseLooseIpAddress(normalized)) {
93
- return true;
102
+ // This includes IPv6 transition mechanism literals and malformed addresses.
103
+ if (normalized.includes(":")) {
104
+ if (!parseLooseIpAddress(normalized)) {
105
+ return true;
106
+ }
94
107
  }
95
108
  if (!isCanonicalDottedDecimalIPv4(normalized) && isLegacyIpv4Literal(normalized)) {
96
109
  return true;
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Shell Command Security Hardening
3
+ *
4
+ * Prevents shell injection attacks via:
5
+ * - Line continuation blocking (\\\n)
6
+ * - Unicode normalization for homoglyph attacks
7
+ * - Fail-closed sandbox unavailable handling
8
+ * - Docker namespace join blocking
9
+ *
10
+ * OpenClaw #32384, #30951
11
+ */
12
+ import { isSafeExecutableValue } from "./exec-safety.js";
13
+ /**
14
+ * Shell metacharacters that enable command injection or chaining.
15
+ * Extended set includes backslash for line continuation detection.
16
+ */
17
+ const SHELL_INJECTION_CHARS = /[;&|`$(){}<>\\]/;
18
+ const SHELL_LINE_CONTINUATION = /\\\r?\n/g;
19
+ const SHELL_COMMENT_INJECTION = /#[^\n]*$/gm;
20
+ /**
21
+ * Unicode characters that can be used to bypass shell filters.
22
+ * Includes zero-width chars, homoglyphs, and special whitespace.
23
+ */
24
+ const UNICODE_BYPASS_CHARS = /[\u200B-\u200D\uFEFF\u00A0\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]/;
25
+ /**
26
+ * Check if a command contains shell line continuations (\\\n).
27
+ * OpenClaw #32384 - blocks shell wrapper bypass attempts.
28
+ */
29
+ export function hasShellLineContinuation(command) {
30
+ return SHELL_LINE_CONTINUATION.test(command);
31
+ }
32
+ /**
33
+ * Check if a command contains unicode bypass characters.
34
+ */
35
+ export function hasUnicodeBypassChars(command) {
36
+ return UNICODE_BYPASS_CHARS.test(command);
37
+ }
38
+ /**
39
+ * Normalize command string to prevent unicode bypass attacks.
40
+ * Removes zero-width characters and normalizes whitespace.
41
+ */
42
+ export function normalizeShellCommand(command) {
43
+ // Remove zero-width and special unicode chars
44
+ let normalized = command.replace(UNICODE_BYPASS_CHARS, "");
45
+ // Normalize line endings
46
+ normalized = normalized.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
47
+ // Remove shell line continuations (security-critical)
48
+ normalized = normalized.replace(SHELL_LINE_CONTINUATION, "");
49
+ // Remove trailing comments that could bypass filters
50
+ normalized = normalized.replace(SHELL_COMMENT_INJECTION, "");
51
+ // Trim whitespace
52
+ return normalized.trim();
53
+ }
54
+ /**
55
+ * Enhanced shell command validation with continuation blocking.
56
+ * OpenClaw #32384
57
+ */
58
+ export function isSafeShellCommand(command) {
59
+ if (!command || !command.trim()) {
60
+ return false;
61
+ }
62
+ const normalized = normalizeShellCommand(command);
63
+ // Block if normalization removed anything (indicates bypass attempt)
64
+ if (normalized !== command.trim()) {
65
+ return false;
66
+ }
67
+ // Check for shell injection chars
68
+ if (SHELL_INJECTION_CHARS.test(normalized)) {
69
+ return false;
70
+ }
71
+ // Check for unicode bypass attempts
72
+ if (hasUnicodeBypassChars(command)) {
73
+ return false;
74
+ }
75
+ // Check for line continuations
76
+ if (hasShellLineContinuation(command)) {
77
+ return false;
78
+ }
79
+ // Use existing safety check
80
+ return isSafeExecutableValue(normalized);
81
+ }
82
+ /**
83
+ * Docker namespace types that should be blocked by default.
84
+ * OpenClaw #32384 - prevents container escape via namespace joins.
85
+ */
86
+ const BLOCKED_DOCKER_NAMESPACES = new Set(["pid", "ipc", "uts", "mount", "network"]);
87
+ /**
88
+ * Check if a Docker run command attempts to join host namespaces.
89
+ * Blocks --pid=host, --ipc=host, --network=host, etc.
90
+ */
91
+ export function hasDangerousDockerNamespace(command) {
92
+ const normalized = command.toLowerCase();
93
+ for (const ns of BLOCKED_DOCKER_NAMESPACES) {
94
+ // Match patterns like --pid=host, --pid host, --pidhost
95
+ const patterns = [
96
+ new RegExp(`--${ns}\\s*=\\s*host`, "i"),
97
+ new RegExp(`--${ns}\\s+host`, "i"),
98
+ new RegExp(`--${ns}host`, "i"),
99
+ ];
100
+ for (const pattern of patterns) {
101
+ if (pattern.test(normalized)) {
102
+ return true;
103
+ }
104
+ }
105
+ }
106
+ return false;
107
+ }
108
+ /**
109
+ * Validate Docker command for security issues.
110
+ * Returns { valid: true } or { valid: false, reason: string }
111
+ */
112
+ export function validateDockerCommand(command) {
113
+ // Check for namespace joins
114
+ if (hasDangerousDockerNamespace(command)) {
115
+ return {
116
+ valid: false,
117
+ reason: "Docker namespace join detected (pid/ipc/uts/mount/network=host). Container escape vector.",
118
+ };
119
+ }
120
+ // Check for privileged mode
121
+ if (/--privileged/.test(command.toLowerCase())) {
122
+ return {
123
+ valid: false,
124
+ reason: "Privileged Docker container detected. Full host access bypass.",
125
+ };
126
+ }
127
+ // Check for dangerous volume mounts
128
+ const dangerousMounts = [
129
+ "/:/host",
130
+ "/:/root",
131
+ "--volume=/:",
132
+ "-v /:/",
133
+ "--volume=/:/",
134
+ "-v /:/",
135
+ "--volume=/var/run/docker.sock",
136
+ "-v /var/run/docker.sock",
137
+ "--volume=/run/docker.sock",
138
+ "-v /run/docker.sock",
139
+ ];
140
+ const normalized = command.toLowerCase();
141
+ for (const mount of dangerousMounts) {
142
+ if (normalized.includes(mount.toLowerCase())) {
143
+ return {
144
+ valid: false,
145
+ reason: `Dangerous volume mount detected: ${mount}. Host filesystem access bypass.`,
146
+ };
147
+ }
148
+ }
149
+ // Check for capability additions
150
+ if (/--cap-add=ALL|--cap-add\s+ALL|--cap-add=SYS_ADMIN|--cap-add\s+SYS_ADMIN/i.test(command)) {
151
+ return {
152
+ valid: false,
153
+ reason: "Dangerous capability addition detected (ALL or SYS_ADMIN). Privilege escalation vector.",
154
+ };
155
+ }
156
+ return { valid: true };
157
+ }
158
+ /**
159
+ * Fail-closed error for when sandbox is unavailable.
160
+ * OpenClaw #32384 - ensures commands are rejected when safety checks can't run.
161
+ */
162
+ export class SandboxUnavailableError extends Error {
163
+ constructor(message = "Sandbox unavailable. Failing closed for security.") {
164
+ super(message);
165
+ this.name = "SandboxUnavailableError";
166
+ }
167
+ }
168
+ /**
169
+ * Wrap sandbox execution with fail-closed behavior.
170
+ * If sandbox checks cannot be performed, reject the command.
171
+ */
172
+ export function withFailClosedSandbox(operation, context = "sandbox-execution") {
173
+ return operation().catch((error) => {
174
+ // Always fail closed - if we can't verify safety, reject
175
+ if (error instanceof SandboxUnavailableError) {
176
+ throw error;
177
+ }
178
+ // Wrap other errors as sandbox unavailable
179
+ throw new SandboxUnavailableError(`Sandbox check failed (${context}): ${error instanceof Error ? error.message : String(error)}. Failing closed.`);
180
+ });
181
+ }
182
+ /**
183
+ * Validate that a shell wrapper's inner executable matches approval.
184
+ */
185
+ export function validateShellWrapper(params) {
186
+ const { command, approval } = params;
187
+ // Extract the actual executable from the command
188
+ const parts = command.trim().split(/\s+/);
189
+ if (parts.length === 0) {
190
+ return { valid: false, reason: "Empty command" };
191
+ }
192
+ const actualExecutable = parts[0];
193
+ // Check if executable matches approved inner executable
194
+ if (actualExecutable !== approval.innerExecutable) {
195
+ return {
196
+ valid: false,
197
+ reason: `Executable mismatch: approved "${approval.innerExecutable}", got "${actualExecutable}"`,
198
+ };
199
+ }
200
+ return { valid: true };
201
+ }