hyperclaw 4.0.2 → 5.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 (134) hide show
  1. package/README.md +54 -3
  2. package/dist/a2ui-protocol-CfBI44-Q.js +75 -0
  3. package/dist/agents-routing-ChHiZp36.js +327 -0
  4. package/dist/agents-routing-ChqZ6l2S.js +4 -0
  5. package/dist/api-keys-guide-BCcOl0Q7.js +149 -0
  6. package/dist/audit-BaIiyWFu.js +441 -0
  7. package/dist/bounty-tools-DWudyZie.js +211 -0
  8. package/dist/browser-tools-BsTeGMnX.js +5 -0
  9. package/dist/browser-tools-D8_rLe2p.js +179 -0
  10. package/dist/claw-tasks-CgTsiNE8.js +80 -0
  11. package/dist/connector-5N0-X_xs.js +194 -0
  12. package/dist/connector-B3v0qcXg.js +425 -0
  13. package/dist/connector-B8R3iBY1.js +280 -0
  14. package/dist/connector-BAM-08NN.js +189 -0
  15. package/dist/connector-BC8FIVu4.js +181 -0
  16. package/dist/connector-BDmwwaVc.js +213 -0
  17. package/dist/connector-BGjbBy69.js +225 -0
  18. package/dist/connector-BO2SRzfG.js +218 -0
  19. package/dist/connector-BfXky0L3.js +167 -0
  20. package/dist/connector-BiiSJpx3.js +192 -0
  21. package/dist/connector-BnDmIhIu.js +85 -0
  22. package/dist/connector-C1HSoUyk.js +189 -0
  23. package/dist/connector-CKQHZOXg.js +568 -0
  24. package/dist/connector-CRl-iidy.js +239 -0
  25. package/dist/connector-Ci9glMD-.js +340 -0
  26. package/dist/connector-CjtZIEDj.js +181 -0
  27. package/dist/connector-Ck6JtOsX.js +531 -0
  28. package/dist/connector-D8Kelee0.js +286 -0
  29. package/dist/connector-DAnRJ0oP.js +162 -0
  30. package/dist/connector-DXTp5PE8.js +508 -0
  31. package/dist/connector-Dih6dUPP.js +173 -0
  32. package/dist/connector-DqTH_tPX.js +182 -0
  33. package/dist/connector-DrnEiiyP.js +419 -0
  34. package/dist/connector-DtR5GGTX.js +167 -0
  35. package/dist/connector-Tky_qS_K.js +350 -0
  36. package/dist/connector-ZSc3oTTy.js +305 -0
  37. package/dist/connector-sW5yhU1m.js +498 -0
  38. package/dist/connector-u3ICd3Ic.js +552 -0
  39. package/dist/cost-tracker-DD9wtWsr.js +103 -0
  40. package/dist/credentials-store-C6ir0Dae.js +4 -0
  41. package/dist/credentials-store-H13LqOwJ.js +77 -0
  42. package/dist/cron-tasks-Bli7Kzd2.js +82 -0
  43. package/dist/daemon-Bg4GtCmc.js +318 -0
  44. package/dist/daemon-DhmwY8k4.js +5 -0
  45. package/dist/delivery-BmIYy9VQ.js +4 -0
  46. package/dist/delivery-pWUPBp1F.js +95 -0
  47. package/dist/destructive-gate-D6vWOdEl.js +101 -0
  48. package/dist/developer-keys-CPWT7Q6S.js +8 -0
  49. package/dist/developer-keys-DrrcUqFa.js +127 -0
  50. package/dist/doctor-BvCe8BBk.js +230 -0
  51. package/dist/doctor-CxyPLYsJ.js +6 -0
  52. package/dist/engine-CEDSqXfw.js +256 -0
  53. package/dist/engine-Da4JMNpI.js +7 -0
  54. package/dist/env-resolve-CiXbWYwe.js +10 -0
  55. package/dist/env-resolve-CmGWhWXJ.js +115 -0
  56. package/dist/extraction-tools-HOZstZ0y.js +91 -0
  57. package/dist/extraction-tools-m4lmAv7l.js +5 -0
  58. package/dist/form_data-Cz040rio.js +8657 -0
  59. package/dist/gmail-watch-setup-Du7DVV7S.js +40 -0
  60. package/dist/health-B-asI__D.js +6 -0
  61. package/dist/health-Ds2YlpTB.js +152 -0
  62. package/dist/heartbeat-engine-BYT5ayQH.js +83 -0
  63. package/dist/hub-D0XwdjM-.js +515 -0
  64. package/dist/hub-LiD5Iztb.js +6 -0
  65. package/dist/hyperclawbot-zvczQgKx.js +505 -0
  66. package/dist/inference-BKVkBREb.js +6 -0
  67. package/dist/inference-DCXH4Q3x.js +922 -0
  68. package/dist/knowledge-graph-iBG76fvm.js +131 -0
  69. package/dist/loader-CC45xGpC.js +4 -0
  70. package/dist/loader-CnEdOyjT.js +400 -0
  71. package/dist/logger-ybOp7VOC.js +83 -0
  72. package/dist/manager-03ipO9R0.js +105 -0
  73. package/dist/manager-BpDfbDjg.js +117 -0
  74. package/dist/manager-Bxl0sqlh.js +4 -0
  75. package/dist/manager-CrVDn6eN.js +6 -0
  76. package/dist/manager-FCgF1plu.js +218 -0
  77. package/dist/manager-rgCsaWT1.js +40 -0
  78. package/dist/mcp-CfoSU4Uz.js +139 -0
  79. package/dist/mcp-loader-DkRBsLpk.js +94 -0
  80. package/dist/memory-BlHL7JCO.js +4 -0
  81. package/dist/memory-DsS_eFvJ.js +270 -0
  82. package/dist/memory-auto-BkvtSFUw.js +5 -0
  83. package/dist/memory-auto-Bnz_-1wP.js +306 -0
  84. package/dist/memory-integration-cSYkZyEo.js +91 -0
  85. package/dist/moltbook-BtLDZTfM.js +81 -0
  86. package/dist/node-Dw2Gi-cP.js +222 -0
  87. package/dist/nodes-registry-B8dmrlLv.js +52 -0
  88. package/dist/oauth-flow-DQPvMHRH.js +150 -0
  89. package/dist/oauth-provider-Uo4Nib_c.js +110 -0
  90. package/dist/observability-BV-Yx0V9.js +89 -0
  91. package/dist/onboard-0WoDxbv_.js +10 -0
  92. package/dist/onboard-BXNXCQp4.js +4070 -0
  93. package/dist/orchestrator-DmnEvMaL.js +189 -0
  94. package/dist/orchestrator-RI3bpqqc.js +6 -0
  95. package/dist/pairing-6iM27aD8.js +196 -0
  96. package/dist/pairing-dGoiGepK.js +4 -0
  97. package/dist/pc-access-CgCsYrpt.js +8 -0
  98. package/dist/pc-access-_iH2aorG.js +819 -0
  99. package/dist/pending-approval-CUXjysAo.js +22 -0
  100. package/dist/reminders-store-Drjed_-h.js +58 -0
  101. package/dist/renderer-BVQrd0_g.js +225 -0
  102. package/dist/rules-BE4GV6cV.js +103 -0
  103. package/dist/run-main.js +1607 -443
  104. package/dist/runner-DatMMYYE.js +1271 -0
  105. package/dist/sdk/index.js +2 -2
  106. package/dist/sdk/index.mjs +2 -2
  107. package/dist/security-BqNyT4ID.js +4 -0
  108. package/dist/security-tpgqPWWH.js +73 -0
  109. package/dist/server-D4wVHiX9.js +4 -0
  110. package/dist/server-Dh3JlBFB.js +1255 -0
  111. package/dist/session-store-BUiPz0Vv.js +5 -0
  112. package/dist/session-store-is4B6qmD.js +113 -0
  113. package/dist/sessions-tools-CbUTFe4i.js +5 -0
  114. package/dist/sessions-tools-CeqD7iil.js +95 -0
  115. package/dist/skill-loader-BaNLVmJy.js +7 -0
  116. package/dist/skill-loader-HgpF6Vqs.js +159 -0
  117. package/dist/skill-runtime-CJN24QPW.js +102 -0
  118. package/dist/skill-runtime-w1ig_lcw.js +5 -0
  119. package/dist/src-BxPHKO5x.js +63 -0
  120. package/dist/src-DIc-L2IG.js +20 -0
  121. package/dist/src-g_rNx5rh.js +458 -0
  122. package/dist/sub-agent-tools-CHQoHz9c.js +39 -0
  123. package/dist/theme-DcxwcUgZ.js +180 -0
  124. package/dist/theme-cx0fkgWC.js +8 -0
  125. package/dist/tool-policy-CNT-mF2Z.js +189 -0
  126. package/dist/tts-elevenlabs-BRosZv-f.js +61 -0
  127. package/dist/update-check-C2Dz85wJ.js +81 -0
  128. package/dist/vision-BMmiIKy7.js +121 -0
  129. package/dist/vision-tools-DVuYc17I.js +51 -0
  130. package/dist/vision-tools-U3YC4L-g.js +5 -0
  131. package/dist/voice-transcription-B555DbWR.js +138 -0
  132. package/dist/website-watch-tools-DFMrJU-R.js +139 -0
  133. package/dist/website-watch-tools-Du3W5sN7.js +5 -0
  134. package/package.json +1 -1
@@ -0,0 +1,441 @@
1
+ const require_chunk = require('./chunk-jS-bbMI5.js');
2
+ const chalk = require_chunk.__toESM(require("chalk"));
3
+ const ora = require_chunk.__toESM(require("ora"));
4
+ const fs_extra = require_chunk.__toESM(require("fs-extra"));
5
+ const path = require_chunk.__toESM(require("path"));
6
+ const os = require_chunk.__toESM(require("os"));
7
+
8
+ //#region src/security/audit.ts
9
+ const HC_DIR = path.default.join(os.default.homedir(), ".hyperclaw");
10
+ async function checkFilePermissions() {
11
+ const findings = [];
12
+ const sensitiveFiles = [
13
+ path.default.join(HC_DIR, "hyperclaw.json"),
14
+ path.default.join(HC_DIR, "auth.json"),
15
+ path.default.join(HC_DIR, ".env"),
16
+ path.default.join(HC_DIR, "AGENTS.md")
17
+ ];
18
+ for (const f of sensitiveFiles) if (await fs_extra.default.pathExists(f)) {
19
+ const stat = await fs_extra.default.stat(f);
20
+ if ((stat.mode & 63) !== 0) findings.push({
21
+ checkId: "creds-permissions",
22
+ severity: "high",
23
+ category: "File Permissions",
24
+ title: `Unsafe permissions on ${path.default.basename(f)}`,
25
+ detail: `Mode ${(stat.mode & 511).toString(8)} allows group/other read`,
26
+ remediation: `chmod 600 ${f}`,
27
+ cvss: 7.5,
28
+ autofix: async () => {
29
+ await fs_extra.default.chmod(f, 384);
30
+ }
31
+ });
32
+ }
33
+ const credsDir = path.default.join(HC_DIR, "credentials");
34
+ if (await fs_extra.default.pathExists(credsDir)) {
35
+ const stat = await fs_extra.default.stat(credsDir);
36
+ if ((stat.mode & 63) !== 0) findings.push({
37
+ checkId: "creds-dir-permissions",
38
+ severity: "critical",
39
+ category: "File Permissions",
40
+ title: "credentials/ directory is world-readable",
41
+ detail: `Mode ${(stat.mode & 511).toString(8)} — all credential files are exposed`,
42
+ remediation: `chmod 700 ${credsDir}`,
43
+ cvss: 9.1,
44
+ autofix: async () => {
45
+ await fs_extra.default.chmod(credsDir, 448);
46
+ }
47
+ });
48
+ }
49
+ if (await fs_extra.default.pathExists(HC_DIR)) {
50
+ const stat = await fs_extra.default.stat(HC_DIR);
51
+ if ((stat.mode & 63) !== 0) findings.push({
52
+ checkId: "config-dir-permissions",
53
+ severity: "high",
54
+ category: "File Permissions",
55
+ title: "~/.hyperclaw/ directory is group/world readable",
56
+ detail: `Mode ${(stat.mode & 511).toString(8)} — config directory accessible to others`,
57
+ remediation: `chmod 700 ${HC_DIR}`,
58
+ cvss: 6.5,
59
+ autofix: async () => {
60
+ await fs_extra.default.chmod(HC_DIR, 448);
61
+ }
62
+ });
63
+ }
64
+ const cwd = process.cwd();
65
+ const gitignorePath = path.default.join(cwd, ".gitignore");
66
+ if (await fs_extra.default.pathExists(gitignorePath)) {
67
+ const gi = await fs_extra.default.readFile(gitignorePath, "utf8");
68
+ if (!gi.includes(".env")) findings.push({
69
+ checkId: "gitignore-env",
70
+ severity: "high",
71
+ category: "Secret Exposure",
72
+ title: ".env is not in .gitignore",
73
+ detail: "Environment files with secrets may be committed to git",
74
+ remediation: "echo \".env\" >> .gitignore",
75
+ cvss: 8.1
76
+ });
77
+ if (!gi.includes("credentials/")) findings.push({
78
+ checkId: "gitignore-creds",
79
+ severity: "high",
80
+ category: "Secret Exposure",
81
+ title: "credentials/ is not in .gitignore",
82
+ detail: "Credential files may be committed to git",
83
+ remediation: "echo \"credentials/\" >> .gitignore",
84
+ cvss: 8.1
85
+ });
86
+ }
87
+ return findings;
88
+ }
89
+ async function checkGatewayConfig() {
90
+ const findings = [];
91
+ let cfg = null;
92
+ try {
93
+ cfg = await fs_extra.default.readJson(path.default.join(HC_DIR, "hyperclaw.json"));
94
+ } catch {
95
+ return findings;
96
+ }
97
+ const token = cfg.gateway?.authToken;
98
+ if (!token) findings.push({
99
+ checkId: "gateway-auth-missing",
100
+ severity: "critical",
101
+ category: "Authentication",
102
+ title: "Gateway auth token not set",
103
+ detail: "Any client can connect to the gateway without authentication",
104
+ remediation: "hyperclaw gateway config --set-token",
105
+ cvss: 9.8
106
+ });
107
+ else if (token.length < 32) findings.push({
108
+ checkId: "auth-token-strength",
109
+ severity: "high",
110
+ category: "Authentication",
111
+ title: "Gateway auth token is too short",
112
+ detail: `Token is ${token.length} chars — minimum recommended is 32`,
113
+ remediation: "hyperclaw gateway config --regenerate-token",
114
+ cvss: 7.3
115
+ });
116
+ if (cfg.gateway?.bind === "0.0.0.0") findings.push({
117
+ checkId: "gateway-bind-all",
118
+ severity: "medium",
119
+ category: "Network Exposure",
120
+ title: "Gateway bound to all interfaces (0.0.0.0)",
121
+ detail: "Gateway is accessible from the local network. Ensure auth token is strong.",
122
+ remediation: "Set gateway.bind: \"127.0.0.1\" unless LAN access is required",
123
+ cvss: 5.3
124
+ });
125
+ if (cfg.gateway?.tailscaleExposure === "funnel") findings.push({
126
+ checkId: "tailscale-funnel",
127
+ severity: "medium",
128
+ category: "Network Exposure",
129
+ title: "Gateway exposed via Tailscale Funnel (public internet)",
130
+ detail: "Your gateway is reachable from the public internet via Tailscale Funnel",
131
+ remediation: "Ensure auth token is strong and DM policies are restrictive",
132
+ cvss: 5.8
133
+ });
134
+ const tp = cfg.gateway?.trustedProxies;
135
+ if (Array.isArray(tp) && tp.length > 0 && !token) findings.push({
136
+ checkId: "trusted-proxies-no-auth",
137
+ severity: "high",
138
+ category: "Network Exposure",
139
+ title: "trustedProxies configured but gateway has no auth token",
140
+ detail: "Proxy IP spoofing combined with no auth allows unauthenticated access",
141
+ remediation: "Set gateway.authToken and gateway.trustedProxies to known proxy IPs only",
142
+ cvss: 8.1
143
+ });
144
+ const channels = cfg.channels || cfg.channelConfigs || {};
145
+ for (const [ch, chCfg] of Object.entries(channels)) {
146
+ const policy = chCfg?.dmPolicy ?? chCfg?.dm?.policy;
147
+ if (policy === "open") findings.push({
148
+ checkId: `dm-policy-open-${ch}`,
149
+ severity: "high",
150
+ category: "DM Policy",
151
+ title: `DM policy is "open" on ${ch}`,
152
+ detail: "Anyone can send DMs to your agent. This is a prompt injection risk.",
153
+ remediation: `hyperclaw channels add ${ch} # choose pairing or allowlist`,
154
+ cvss: 7.1
155
+ });
156
+ if (policy === "allowlist") {
157
+ const allowFrom = chCfg?.allowFrom ?? chCfg?.dm?.allowFrom ?? [];
158
+ if (allowFrom.length === 0) findings.push({
159
+ checkId: `allowlist-empty-${ch}`,
160
+ severity: "medium",
161
+ category: "DM Policy",
162
+ title: `Empty allowlist on ${ch} — DMs are silently dropped`,
163
+ detail: `allowFrom is [] — no one can reach your agent on ${ch}`,
164
+ remediation: `hyperclaw pairing approve ${ch} <code>`,
165
+ cvss: 4
166
+ });
167
+ if (allowFrom.includes("*")) findings.push({
168
+ checkId: `allowlist-wildcard-${ch}`,
169
+ severity: "high",
170
+ category: "DM Policy",
171
+ title: `Wildcard (*) in allowFrom on ${ch}`,
172
+ detail: "allowFrom: [\"*\"] is equivalent to open DMs — anyone can message your agent",
173
+ remediation: `Remove "*" and add specific users to allowFrom on ${ch}`,
174
+ cvss: 7.1
175
+ });
176
+ }
177
+ const groups = chCfg?.groups ?? {};
178
+ for (const [spaceId, gCfg] of Object.entries(groups)) if (gCfg?.requireMention === false) findings.push({
179
+ checkId: `group-mention-off-${ch}-${spaceId}`,
180
+ severity: "medium",
181
+ category: "Group Policy",
182
+ title: `requireMention disabled in ${ch} group ${spaceId}`,
183
+ detail: "Bot responds to all messages in the group — prompt injection surface is maximised",
184
+ remediation: `Set channels.${ch}.groups.${spaceId}.requireMention: true`,
185
+ cvss: 5.3
186
+ });
187
+ }
188
+ const dmScope = cfg.session?.dmScope;
189
+ if (!dmScope || dmScope === "global") findings.push({
190
+ checkId: "session-dmscope-global",
191
+ severity: "low",
192
+ category: "Session Isolation",
193
+ title: "session.dmScope is \"global\" — shared inbox not isolated",
194
+ detail: "All DMs share one session context. If multiple people can DM your bot, consider \"per-channel-peer\".",
195
+ remediation: "Set session.dmScope: \"per-channel-peer\" in your config",
196
+ cvss: 3.5
197
+ });
198
+ const toolsExec = cfg.tools?.exec;
199
+ if (toolsExec?.security === "allow" && !cfg.tools?.exec?.ask) findings.push({
200
+ checkId: "exec-allow-no-ask",
201
+ severity: "high",
202
+ category: "Tool Blast Radius",
203
+ title: "tools.exec.security is \"allow\" without ask confirmation",
204
+ detail: "Any agent turn can run arbitrary shell commands without your approval",
205
+ remediation: "Set tools.exec.ask: \"always\" or tools.exec.security: \"deny\"",
206
+ cvss: 8.5
207
+ });
208
+ const elevated = cfg.tools?.elevated;
209
+ if (elevated?.enabled === true) findings.push({
210
+ checkId: "elevated-enabled",
211
+ severity: "medium",
212
+ category: "Tool Blast Radius",
213
+ title: "tools.elevated is enabled",
214
+ detail: "Elevated mode allows running exec on the gateway host from chat. Keep allowFrom tight.",
215
+ remediation: "Set tools.elevated.allowFrom to specific trusted users only",
216
+ cvss: 6
217
+ });
218
+ const sandboxMode = cfg.agents?.defaults?.sandbox?.mode;
219
+ if (sandboxMode === "off" && cfg.agents?.defaults?.sandbox?.image) findings.push({
220
+ checkId: "sandbox-configured-off",
221
+ severity: "medium",
222
+ category: "Tool Blast Radius",
223
+ title: "Sandbox image configured but sandbox mode is \"off\"",
224
+ detail: "Tools run directly on the gateway host despite sandbox config being present",
225
+ remediation: "Set agents.defaults.sandbox.mode: \"all\" to activate the sandbox",
226
+ cvss: 5.5
227
+ });
228
+ const fsWorkspaceOnly = cfg.tools?.fs?.workspaceOnly;
229
+ const workspace = cfg.agents?.defaults?.workspace ?? cfg.agent?.workspace ?? "";
230
+ if (fsWorkspaceOnly === false || !fsWorkspaceOnly && workspace && (workspace === os.default.homedir() || workspace === "~")) findings.push({
231
+ checkId: "fs-workspace-broad",
232
+ severity: "medium",
233
+ category: "File System",
234
+ title: "Workspace root is very broad or workspaceOnly is disabled",
235
+ detail: "A broad workspace root exposes sensitive files like ~/.hyperclaw to the filesystem tools",
236
+ remediation: "Set tools.fs.workspaceOnly: true and use a dedicated workspace directory",
237
+ cvss: 5.3
238
+ });
239
+ const ssrfPolicy = cfg.browser?.ssrfPolicy;
240
+ if (ssrfPolicy?.dangerouslyAllowPrivateNetwork === false && (!ssrfPolicy?.hostnameAllowlist || ssrfPolicy.hostnameAllowlist.length === 0)) findings.push({
241
+ checkId: "browser-ssrf-no-allowlist",
242
+ severity: "info",
243
+ category: "Browser Security",
244
+ title: "Browser strict SSRF mode enabled but no hostnameAllowlist defined",
245
+ detail: "dangerouslyAllowPrivateNetwork: false without hostnameAllowlist may block all browser navigation",
246
+ remediation: "Add browser.ssrfPolicy.hostnameAllowlist with allowed domains",
247
+ cvss: 0
248
+ });
249
+ const plugins = cfg.plugins?.entries ?? {};
250
+ const hasPlugins = Object.keys(plugins).length > 0;
251
+ const hasAllowlist = Array.isArray(cfg.plugins?.allowlist) && cfg.plugins.allowlist.length > 0;
252
+ if (hasPlugins && !hasAllowlist) findings.push({
253
+ checkId: "plugin-allowlist-missing",
254
+ severity: "low",
255
+ category: "Plugins",
256
+ title: "Plugins installed without an explicit allowlist",
257
+ detail: `${Object.keys(plugins).length} plugin(s) installed; plugins.allowlist is not set`,
258
+ remediation: "Set plugins.allowlist: [\"<pluginId>\"] for each approved plugin",
259
+ cvss: 3
260
+ });
261
+ const baselinePath = path.default.join(process.cwd(), ".secrets.baseline");
262
+ if (!await fs_extra.default.pathExists(baselinePath)) findings.push({
263
+ checkId: "detect-secrets-baseline",
264
+ severity: "low",
265
+ category: "Secret Scanning",
266
+ title: "No .secrets.baseline file — detect-secrets not configured",
267
+ detail: "Secret scanning CI will fail without a baseline file",
268
+ remediation: "Run: detect-secrets scan > .secrets.baseline && git add .secrets.baseline",
269
+ cvss: 2
270
+ });
271
+ return findings;
272
+ }
273
+ async function checkSecretsInPrompts() {
274
+ const findings = [];
275
+ const secretPatterns = [
276
+ {
277
+ pattern: /sk-[a-zA-Z0-9]{20,}/,
278
+ name: "OpenAI API key"
279
+ },
280
+ {
281
+ pattern: /tvly-[a-zA-Z0-9]{20,}/,
282
+ name: "Tavily API key"
283
+ },
284
+ {
285
+ pattern: /xai-[a-zA-Z0-9]{20,}/,
286
+ name: "xAI API key"
287
+ },
288
+ {
289
+ pattern: /ghp_[a-zA-Z0-9]{36}/,
290
+ name: "GitHub PAT"
291
+ },
292
+ {
293
+ pattern: /or-[a-zA-Z0-9]{20,}/,
294
+ name: "OpenRouter API key"
295
+ },
296
+ {
297
+ pattern: /[a-f0-9]{64}/,
298
+ name: "Potential hex secret"
299
+ }
300
+ ];
301
+ const filesToScan = [
302
+ path.default.join(HC_DIR, "AGENTS.md"),
303
+ path.default.join(HC_DIR, "MEMORY.md"),
304
+ path.default.join(HC_DIR, "hyperclaw.json")
305
+ ];
306
+ for (const f of filesToScan) {
307
+ if (!await fs_extra.default.pathExists(f)) continue;
308
+ const content = await fs_extra.default.readFile(f, "utf8");
309
+ for (const { pattern, name } of secretPatterns) if (pattern.test(content)) findings.push({
310
+ checkId: "key-in-config",
311
+ severity: "critical",
312
+ category: "Secret Exposure",
313
+ title: `${name} potentially embedded in ${path.default.basename(f)}`,
314
+ detail: `Found pattern matching ${name} in plaintext file. Secrets in prompts are a serious risk.`,
315
+ remediation: `Remove the secret and use: hyperclaw secrets set KEY=value`,
316
+ cvss: 9.1
317
+ });
318
+ }
319
+ return findings;
320
+ }
321
+ async function deepScan() {
322
+ const findings = [];
323
+ const hubState = path.default.join(HC_DIR, "hub-state.json");
324
+ if (await fs_extra.default.pathExists(hubState)) {
325
+ const state = await fs_extra.default.readJson(hubState);
326
+ const dangerous = state.installed?.filter((s) => s.risk === "dangerous") || [];
327
+ for (const s of dangerous) findings.push({
328
+ checkId: `plugin-unreviewed-${s.id}`,
329
+ severity: "critical",
330
+ category: "Installed Skills",
331
+ title: `Dangerous skill installed: ${s.name}`,
332
+ detail: s.riskReason || "Skill is flagged as dangerous",
333
+ remediation: `hyperclaw hub --uninstall ${s.id}`,
334
+ cvss: 8.5
335
+ });
336
+ }
337
+ let cfg = null;
338
+ try {
339
+ cfg = await fs_extra.default.readJson(path.default.join(HC_DIR, "hyperclaw.json"));
340
+ } catch {}
341
+ if (cfg?.gateway?.authToken) {
342
+ const token = cfg.gateway.authToken;
343
+ const entropy = estimateEntropy(token);
344
+ if (entropy < 3.5) findings.push({
345
+ checkId: "auth-token-low-entropy",
346
+ severity: "high",
347
+ category: "Token Quality",
348
+ title: "Gateway auth token has low entropy",
349
+ detail: `Estimated entropy: ${entropy.toFixed(2)} bits/char — token may be guessable`,
350
+ remediation: "hyperclaw gateway config --regenerate-token",
351
+ cvss: 6.5
352
+ });
353
+ }
354
+ return findings;
355
+ }
356
+ function estimateEntropy(str) {
357
+ const freq = /* @__PURE__ */ new Map();
358
+ for (const ch of str) freq.set(ch, (freq.get(ch) || 0) + 1);
359
+ let entropy = 0;
360
+ for (const count of freq.values()) {
361
+ const p = count / str.length;
362
+ entropy -= p * Math.log2(p);
363
+ }
364
+ return entropy;
365
+ }
366
+ const SEVERITY_ORDER = [
367
+ "critical",
368
+ "high",
369
+ "medium",
370
+ "low",
371
+ "info"
372
+ ];
373
+ const SEVERITY_COLOR = {
374
+ critical: chalk.default.bgRed.white.bold,
375
+ high: chalk.default.red,
376
+ medium: chalk.default.yellow,
377
+ low: chalk.default.cyan,
378
+ info: chalk.default.gray
379
+ };
380
+ async function runSecurityAudit(opts = {}) {
381
+ const { deep = false, fix = false, json = false } = opts;
382
+ if (!json) console.log(chalk.default.bold.cyan("\n 🔐 HYPERCLAW SECURITY AUDIT\n"));
383
+ const spinner = json ? null : (0, ora.default)("Running security checks...").start();
384
+ const allFindings = [
385
+ ...await checkFilePermissions(),
386
+ ...await checkGatewayConfig(),
387
+ ...await checkSecretsInPrompts(),
388
+ ...deep ? await deepScan() : []
389
+ ];
390
+ spinner?.stop();
391
+ if (json) {
392
+ const summary = SEVERITY_ORDER.reduce((acc, sev) => {
393
+ acc[sev] = allFindings.filter((f) => f.severity === sev).length;
394
+ return acc;
395
+ }, {});
396
+ process.stdout.write(JSON.stringify({
397
+ mode: deep ? "deep" : "standard",
398
+ total: allFindings.length,
399
+ summary,
400
+ findings: allFindings.map(({ autofix: _af,...f }) => f)
401
+ }, null, 2) + "\n");
402
+ return;
403
+ }
404
+ if (deep) console.log(chalk.default.gray(" Mode: DEEP SCAN\n"));
405
+ else console.log(chalk.default.gray(" Mode: standard (run with --deep for full scan)\n"));
406
+ if (allFindings.length === 0) {
407
+ console.log(chalk.default.green(" ✔ No security issues found!\n"));
408
+ return;
409
+ }
410
+ allFindings.sort((a, b) => SEVERITY_ORDER.indexOf(a.severity) - SEVERITY_ORDER.indexOf(b.severity));
411
+ let fixedCount = 0;
412
+ for (const f of allFindings) {
413
+ const color = SEVERITY_COLOR[f.severity];
414
+ const badge = color(` ${f.severity.toUpperCase()} `);
415
+ const cvss = f.cvss ? chalk.default.gray(` CVSS ${f.cvss}`) : "";
416
+ const cid = chalk.default.gray(`[${f.checkId}]`);
417
+ console.log(` ${badge}${cvss} ${chalk.default.white(f.title)} ${cid}`);
418
+ console.log(` ${chalk.default.gray(`Category: ${f.category}`)}`);
419
+ console.log(` ${chalk.default.gray(f.detail)}`);
420
+ if (fix && f.autofix) try {
421
+ await f.autofix();
422
+ fixedCount++;
423
+ console.log(` ${chalk.default.green("✔ Auto-fixed")}`);
424
+ } catch (e) {
425
+ console.log(` ${chalk.default.yellow("⚠ Auto-fix failed: " + e.message)}`);
426
+ console.log(` ${chalk.default.cyan("Fix: " + f.remediation)}`);
427
+ }
428
+ else console.log(` ${chalk.default.cyan("Fix: " + f.remediation)}`);
429
+ console.log();
430
+ }
431
+ const counts = SEVERITY_ORDER.reduce((acc, sev) => {
432
+ acc[sev] = allFindings.filter((f) => f.severity === sev).length;
433
+ return acc;
434
+ }, {});
435
+ console.log(` ${chalk.default.bold("Summary:")} ${chalk.default.bgRed.white.bold(` ${counts.critical} CRITICAL `)} ${chalk.default.red(`${counts.high} high`)} ${chalk.default.yellow(`${counts.medium} medium`)} ${chalk.default.cyan(`${counts.low} low`)}\n`);
436
+ if (fix && fixedCount > 0) console.log(chalk.default.green(` ✔ ${fixedCount} issue(s) auto-fixed\n`));
437
+ if (!deep) console.log(chalk.default.gray(" Run: hyperclaw security audit --deep for full credential entropy scan\n"));
438
+ }
439
+
440
+ //#endregion
441
+ exports.runSecurityAudit = runSecurityAudit;
@@ -0,0 +1,211 @@
1
+ const require_chunk = require('./chunk-jS-bbMI5.js');
2
+ const require_env_resolve = require('./env-resolve-CmGWhWXJ.js');
3
+ const https = require_chunk.__toESM(require("https"));
4
+
5
+ //#region packages/core/src/agent/bounty-tools.ts
6
+ function fetchJson(url, auth) {
7
+ return new Promise((resolve, reject) => {
8
+ const u = new URL(url);
9
+ const opts = {
10
+ hostname: u.hostname,
11
+ port: 443,
12
+ path: u.pathname + u.search,
13
+ method: "GET",
14
+ headers: { "Accept": "application/json" }
15
+ };
16
+ if (auth) {
17
+ const h = opts.headers;
18
+ if (auth.startsWith("Basic ") || auth.startsWith("Token ") || auth.startsWith("Bearer ")) h["Authorization"] = auth;
19
+ else h["Authorization"] = `Basic ${Buffer.from(auth).toString("base64")}`;
20
+ }
21
+ https.default.get(opts, (res) => {
22
+ let data = "";
23
+ res.on("data", (c) => data += c);
24
+ res.on("end", () => {
25
+ try {
26
+ resolve(JSON.parse(data || "{}"));
27
+ } catch {
28
+ resolve({});
29
+ }
30
+ });
31
+ }).on("error", reject);
32
+ });
33
+ }
34
+ function getBountyTools(cfg) {
35
+ const hackeroneKey = require_env_resolve.resolveServiceApiKey("hackerone", cfg);
36
+ const bugcrowdKey = require_env_resolve.resolveServiceApiKey("bugcrowd", cfg);
37
+ const synackKey = require_env_resolve.resolveServiceApiKey("synack", cfg);
38
+ const tools = [];
39
+ if (hackeroneKey) tools.push({
40
+ name: "hackerone_list_programs",
41
+ description: "List HackerOne programs you have access to. Use for bug bounty research.",
42
+ input_schema: {
43
+ type: "object",
44
+ properties: { limit: {
45
+ type: "string",
46
+ description: "Max programs (default 20)"
47
+ } }
48
+ },
49
+ handler: async (input) => {
50
+ try {
51
+ const auth = Buffer.from(hackeroneKey.includes(":") ? hackeroneKey : `:${hackeroneKey}`).toString("base64");
52
+ const body = await fetchJson("https://api.hackerone.com/v1/hackers/programs", `Basic ${auth}`);
53
+ const progs = body.data || [];
54
+ const limit = parseInt(input.limit || "20", 10);
55
+ return progs.slice(0, limit).map((p) => {
56
+ const attrs = p.attributes;
57
+ return `- ${attrs?.name || p.id} (${attrs?.state || "?"})`;
58
+ }).join("\n") || "No programs found.";
59
+ } catch (e) {
60
+ return `Error: ${e.message}. Check HackerOne API key (username:token).`;
61
+ }
62
+ }
63
+ });
64
+ if (bugcrowdKey) tools.push({
65
+ name: "bugcrowd_list_programs",
66
+ description: "List Bugcrowd programs. Use for bug bounty research.",
67
+ input_schema: {
68
+ type: "object",
69
+ properties: { limit: {
70
+ type: "string",
71
+ description: "Max programs (default 20)"
72
+ } }
73
+ },
74
+ handler: async (input) => {
75
+ try {
76
+ const auth = bugcrowdKey.startsWith("Token ") ? bugcrowdKey : `Token ${bugcrowdKey}`;
77
+ const body = await fetchJson("https://api.bugcrowd.com/programs", auth);
78
+ const progs = (body && typeof body === "object" && "data" in body ? body.data : body) || [];
79
+ const arr = Array.isArray(progs) ? progs : [progs];
80
+ const limit = parseInt(input.limit || "20", 10);
81
+ return arr.slice(0, limit).map((p) => {
82
+ const attrs = p.attributes;
83
+ const name = attrs?.name || p.name || p.id || "?";
84
+ const links = p.links;
85
+ const linkUrl = links?.self || p.url || "";
86
+ return `- ${name} ${linkUrl ? `(${linkUrl})` : ""}`;
87
+ }).join("\n") || JSON.stringify(body).slice(0, 500);
88
+ } catch (e) {
89
+ return `Error: ${e.message}. Check Bugcrowd API token.`;
90
+ }
91
+ }
92
+ });
93
+ if (synackKey) tools.push({
94
+ name: "synack_list_targets",
95
+ description: "List Synack targets you have access to. Use for bug bounty research.",
96
+ input_schema: {
97
+ type: "object",
98
+ properties: {}
99
+ },
100
+ handler: async () => {
101
+ try {
102
+ const body = await fetchJson("https://api.synack.com/api/targets", `Bearer ${synackKey}`);
103
+ const targets = body.targets || body || [];
104
+ const arr = Array.isArray(targets) ? targets : [targets];
105
+ return arr.slice(0, 20).map((t) => `- ${t.name || t.slug || t.id || "?"}`).join("\n") || "No targets or check Synack API.";
106
+ } catch (e) {
107
+ return `Error: ${e.message}. Check Synack API token.`;
108
+ }
109
+ }
110
+ });
111
+ const apiKeys = cfg?.skills?.apiKeys ?? {};
112
+ const genericServiceIds = Object.keys(apiKeys).filter((id) => !KNOWN_BOUNTY_SERVICES.includes(id.toLowerCase()));
113
+ if (genericServiceIds.length > 0) tools.push({
114
+ name: "call_service_api",
115
+ description: `Call external API for services with configured keys. Available: ${genericServiceIds.join(", ")}. Use when the user needs to query an API for a service they've added (Stripe, GitHub, custom APIs, etc).`,
116
+ input_schema: {
117
+ type: "object",
118
+ properties: {
119
+ service_id: {
120
+ type: "string",
121
+ description: `Service id from configured keys: ${genericServiceIds.join(", ")}`
122
+ },
123
+ url: {
124
+ type: "string",
125
+ description: "Full API URL (e.g. https://api.example.com/v1/resource) or path if baseUrl configured"
126
+ },
127
+ method: {
128
+ type: "string",
129
+ description: "HTTP method",
130
+ enum: [
131
+ "GET",
132
+ "POST",
133
+ "PUT",
134
+ "PATCH",
135
+ "DELETE"
136
+ ]
137
+ },
138
+ body: {
139
+ type: "object",
140
+ description: "Request body for POST/PUT/PATCH (optional)"
141
+ }
142
+ },
143
+ required: ["service_id", "url"]
144
+ },
145
+ handler: async (input) => {
146
+ const serviceId = String(input.service_id || "").trim().toLowerCase();
147
+ const url = String(input.url || "");
148
+ const method = String(input.method || "GET").toUpperCase();
149
+ const key = require_env_resolve.resolveServiceApiKey(serviceId, cfg);
150
+ if (!key) return `Error: No API key for service "${serviceId}". Configure via: hyperclaw config set-service-key ${serviceId} <key>`;
151
+ if (!url || !url.startsWith("http")) return "Error: url must be a full URL (https://...).";
152
+ try {
153
+ const body = await genericHttpRequest(url, method, key, input.body);
154
+ return typeof body === "string" ? body : JSON.stringify(body, null, 2).slice(0, 8e3);
155
+ } catch (e) {
156
+ return `Error: ${e.message}`;
157
+ }
158
+ }
159
+ });
160
+ return tools;
161
+ }
162
+ function genericHttpRequest(url, method, apiKey, body) {
163
+ return new Promise((resolve, reject) => {
164
+ const u = new URL(url);
165
+ const authHeader = apiKey.startsWith("Bearer ") || apiKey.startsWith("Token ") || apiKey.startsWith("Basic ") ? apiKey : `Bearer ${apiKey}`;
166
+ const bodyStr = body && method !== "GET" ? JSON.stringify(body) : void 0;
167
+ const opts = {
168
+ hostname: u.hostname,
169
+ port: u.port || 443,
170
+ path: u.pathname + u.search,
171
+ method,
172
+ headers: {
173
+ "Accept": "application/json",
174
+ "Authorization": authHeader,
175
+ ...bodyStr ? {
176
+ "Content-Type": "application/json",
177
+ "Content-Length": Buffer.byteLength(bodyStr)
178
+ } : {}
179
+ }
180
+ };
181
+ const req = https.default.request(opts, (res) => {
182
+ let data = "";
183
+ res.on("data", (c) => {
184
+ data += c.toString();
185
+ });
186
+ res.on("end", () => {
187
+ try {
188
+ resolve(JSON.parse(data || "{}"));
189
+ } catch {
190
+ resolve(data || "{}");
191
+ }
192
+ });
193
+ });
194
+ req.on("error", reject);
195
+ if (bodyStr) req.write(bodyStr);
196
+ req.end();
197
+ });
198
+ }
199
+ var KNOWN_BOUNTY_SERVICES;
200
+ var init_bounty_tools = require_chunk.__esm({ "packages/core/src/agent/bounty-tools.ts"() {
201
+ require_env_resolve.init_env_resolve();
202
+ KNOWN_BOUNTY_SERVICES = [
203
+ "hackerone",
204
+ "bugcrowd",
205
+ "synack"
206
+ ];
207
+ } });
208
+
209
+ //#endregion
210
+ init_bounty_tools();
211
+ exports.getBountyTools = getBountyTools;
@@ -0,0 +1,5 @@
1
+ const require_chunk = require('./chunk-jS-bbMI5.js');
2
+ const require_browser_tools = require('./browser-tools-D8_rLe2p.js');
3
+
4
+ require_browser_tools.init_browser_tools();
5
+ exports.getBrowserTools = require_browser_tools.getBrowserTools;