clawhatch 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +348 -0
- package/dist/checks/cloud-sync.d.ts +10 -0
- package/dist/checks/cloud-sync.d.ts.map +1 -0
- package/dist/checks/cloud-sync.js +62 -0
- package/dist/checks/cloud-sync.js.map +1 -0
- package/dist/checks/data-protection.d.ts +9 -0
- package/dist/checks/data-protection.d.ts.map +1 -0
- package/dist/checks/data-protection.js +197 -0
- package/dist/checks/data-protection.js.map +1 -0
- package/dist/checks/identity.d.ts +14 -0
- package/dist/checks/identity.d.ts.map +1 -0
- package/dist/checks/identity.js +327 -0
- package/dist/checks/identity.js.map +1 -0
- package/dist/checks/model.d.ts +10 -0
- package/dist/checks/model.d.ts.map +1 -0
- package/dist/checks/model.js +337 -0
- package/dist/checks/model.js.map +1 -0
- package/dist/checks/network.d.ts +9 -0
- package/dist/checks/network.d.ts.map +1 -0
- package/dist/checks/network.js +177 -0
- package/dist/checks/network.js.map +1 -0
- package/dist/checks/operational.d.ts +9 -0
- package/dist/checks/operational.d.ts.map +1 -0
- package/dist/checks/operational.js +158 -0
- package/dist/checks/operational.js.map +1 -0
- package/dist/checks/sandbox.d.ts +9 -0
- package/dist/checks/sandbox.d.ts.map +1 -0
- package/dist/checks/sandbox.js +135 -0
- package/dist/checks/sandbox.js.map +1 -0
- package/dist/checks/secrets.d.ts +9 -0
- package/dist/checks/secrets.d.ts.map +1 -0
- package/dist/checks/secrets.js +816 -0
- package/dist/checks/secrets.js.map +1 -0
- package/dist/checks/skills.d.ts +9 -0
- package/dist/checks/skills.d.ts.map +1 -0
- package/dist/checks/skills.js +303 -0
- package/dist/checks/skills.js.map +1 -0
- package/dist/checks/tools.d.ts +9 -0
- package/dist/checks/tools.d.ts.map +1 -0
- package/dist/checks/tools.js +397 -0
- package/dist/checks/tools.js.map +1 -0
- package/dist/discover.d.ts +22 -0
- package/dist/discover.d.ts.map +1 -0
- package/dist/discover.js +281 -0
- package/dist/discover.js.map +1 -0
- package/dist/fixer.d.ts +16 -0
- package/dist/fixer.d.ts.map +1 -0
- package/dist/fixer.js +361 -0
- package/dist/fixer.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +230 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +14 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +108 -0
- package/dist/init.js.map +1 -0
- package/dist/notify.d.ts +28 -0
- package/dist/notify.d.ts.map +1 -0
- package/dist/notify.js +217 -0
- package/dist/notify.js.map +1 -0
- package/dist/parsers/config.d.ts +16 -0
- package/dist/parsers/config.d.ts.map +1 -0
- package/dist/parsers/config.js +54 -0
- package/dist/parsers/config.js.map +1 -0
- package/dist/parsers/env.d.ts +6 -0
- package/dist/parsers/env.d.ts.map +1 -0
- package/dist/parsers/env.js +35 -0
- package/dist/parsers/env.js.map +1 -0
- package/dist/parsers/jsonl.d.ts +12 -0
- package/dist/parsers/jsonl.d.ts.map +1 -0
- package/dist/parsers/jsonl.js +61 -0
- package/dist/parsers/jsonl.js.map +1 -0
- package/dist/parsers/markdown.d.ts +17 -0
- package/dist/parsers/markdown.d.ts.map +1 -0
- package/dist/parsers/markdown.js +57 -0
- package/dist/parsers/markdown.js.map +1 -0
- package/dist/reporter-html.d.ts +9 -0
- package/dist/reporter-html.d.ts.map +1 -0
- package/dist/reporter-html.js +581 -0
- package/dist/reporter-html.js.map +1 -0
- package/dist/reporter.d.ts +10 -0
- package/dist/reporter.d.ts.map +1 -0
- package/dist/reporter.js +133 -0
- package/dist/reporter.js.map +1 -0
- package/dist/sanitize.d.ts +17 -0
- package/dist/sanitize.d.ts.map +1 -0
- package/dist/sanitize.js +83 -0
- package/dist/sanitize.js.map +1 -0
- package/dist/scanner.d.ts +18 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +236 -0
- package/dist/scanner.js.map +1 -0
- package/dist/scoring.d.ts +17 -0
- package/dist/scoring.d.ts.map +1 -0
- package/dist/scoring.js +47 -0
- package/dist/scoring.js.map +1 -0
- package/dist/telemetry.d.ts +16 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +52 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/threat-feed.d.ts +14 -0
- package/dist/threat-feed.d.ts.map +1 -0
- package/dist/threat-feed.js +133 -0
- package/dist/threat-feed.js.map +1 -0
- package/dist/types.d.ts +221 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +11 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +12 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +34 -0
- package/dist/utils.js.map +1 -0
- package/package.json +71 -0
package/dist/discover.js
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File discovery for OpenClaw installations.
|
|
3
|
+
*
|
|
4
|
+
* Finds all relevant files to scan, handling:
|
|
5
|
+
* - Default and custom paths
|
|
6
|
+
* - Symlink detection and boundary checking
|
|
7
|
+
* - Windows/macOS/Linux path differences
|
|
8
|
+
* - Graceful missing-file handling
|
|
9
|
+
*/
|
|
10
|
+
import { lstat, readlink, readdir, access, constants } from "node:fs/promises";
|
|
11
|
+
import { homedir, platform } from "node:os";
|
|
12
|
+
import { join, resolve } from "node:path";
|
|
13
|
+
import { glob } from "glob";
|
|
14
|
+
/** Common alternative locations for OpenClaw installations */
|
|
15
|
+
function getAlternativePaths() {
|
|
16
|
+
const home = homedir();
|
|
17
|
+
const paths = [join(home, ".openclaw")];
|
|
18
|
+
if (platform() === "win32") {
|
|
19
|
+
const appdata = process.env.APPDATA;
|
|
20
|
+
if (appdata) {
|
|
21
|
+
paths.push(join(appdata, "openclaw"));
|
|
22
|
+
}
|
|
23
|
+
paths.push(join(home, "AppData", "Roaming", "openclaw"));
|
|
24
|
+
}
|
|
25
|
+
return paths;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Resolve ~ to home directory.
|
|
29
|
+
*/
|
|
30
|
+
function expandHome(p) {
|
|
31
|
+
if (p.startsWith("~")) {
|
|
32
|
+
return join(homedir(), p.slice(1));
|
|
33
|
+
}
|
|
34
|
+
return p;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Check if a path is a symlink and if it stays within expected boundaries.
|
|
38
|
+
* Returns the resolved real path, or null if it's suspicious.
|
|
39
|
+
*/
|
|
40
|
+
async function safeResolvePath(filePath, expectedRoot) {
|
|
41
|
+
try {
|
|
42
|
+
const lstats = await lstat(filePath);
|
|
43
|
+
if (lstats.isSymbolicLink()) {
|
|
44
|
+
const target = await readlink(filePath);
|
|
45
|
+
const resolved = resolve(filePath, "..", target);
|
|
46
|
+
// FIX: Only use case-insensitive comparison on case-insensitive filesystems (Windows/macOS)
|
|
47
|
+
const caseSensitive = platform() === "linux";
|
|
48
|
+
const outsideBoundary = caseSensitive
|
|
49
|
+
? !resolved.startsWith(expectedRoot)
|
|
50
|
+
: !resolved.toLowerCase().startsWith(expectedRoot.toLowerCase());
|
|
51
|
+
return { path: resolved, isSymlink: true, outsideBoundary };
|
|
52
|
+
}
|
|
53
|
+
return { path: filePath, isSymlink: false, outsideBoundary: false };
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return { path: filePath, isSymlink: false, outsideBoundary: false };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Auto-detect OpenClaw installation path.
|
|
61
|
+
*/
|
|
62
|
+
export async function findOpenClawDir(customPath) {
|
|
63
|
+
if (customPath) {
|
|
64
|
+
const expanded = expandHome(customPath);
|
|
65
|
+
try {
|
|
66
|
+
await access(expanded, constants.R_OK);
|
|
67
|
+
return expanded;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
for (const candidate of getAlternativePaths()) {
|
|
74
|
+
try {
|
|
75
|
+
await access(candidate, constants.R_OK);
|
|
76
|
+
return candidate;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Discover all scannable files in an OpenClaw installation.
|
|
86
|
+
*/
|
|
87
|
+
export async function discoverFiles(openclawDir, workspaceDir) {
|
|
88
|
+
const symlinkWarnings = [];
|
|
89
|
+
const resolvedRoot = resolve(openclawDir);
|
|
90
|
+
const files = {
|
|
91
|
+
configPath: null,
|
|
92
|
+
envPath: null,
|
|
93
|
+
credentialFiles: [],
|
|
94
|
+
authProfileFiles: [],
|
|
95
|
+
sessionLogFiles: [],
|
|
96
|
+
workspaceMarkdownFiles: [],
|
|
97
|
+
skillFiles: [],
|
|
98
|
+
customCommandFiles: [],
|
|
99
|
+
skillPackageFiles: [],
|
|
100
|
+
privateKeyFiles: [],
|
|
101
|
+
sshKeyFiles: [],
|
|
102
|
+
openclawDir: resolvedRoot,
|
|
103
|
+
workspaceDir: workspaceDir ? resolve(workspaceDir) : null,
|
|
104
|
+
};
|
|
105
|
+
// Config file
|
|
106
|
+
const configCandidate = join(resolvedRoot, "openclaw.json");
|
|
107
|
+
try {
|
|
108
|
+
const resolved = await safeResolvePath(configCandidate, resolvedRoot);
|
|
109
|
+
if (resolved.isSymlink && resolved.outsideBoundary) {
|
|
110
|
+
symlinkWarnings.push(`Symlink: ${configCandidate} -> ${resolved.path} (outside OpenClaw directory)`);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
await access(resolved.path, constants.R_OK);
|
|
114
|
+
files.configPath = resolved.path;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// No config file
|
|
119
|
+
}
|
|
120
|
+
// .env file
|
|
121
|
+
const envCandidate = join(resolvedRoot, ".env");
|
|
122
|
+
try {
|
|
123
|
+
await access(envCandidate, constants.R_OK);
|
|
124
|
+
files.envPath = envCandidate;
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// No .env
|
|
128
|
+
}
|
|
129
|
+
// Credential files
|
|
130
|
+
try {
|
|
131
|
+
const credDir = join(resolvedRoot, "credentials");
|
|
132
|
+
const resolved = await safeResolvePath(credDir, resolvedRoot);
|
|
133
|
+
if (resolved.isSymlink && resolved.outsideBoundary) {
|
|
134
|
+
symlinkWarnings.push(`Symlink: credentials/ -> ${resolved.path} (outside OpenClaw directory)`);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
try {
|
|
138
|
+
const credFiles = await readdir(credDir);
|
|
139
|
+
for (const f of credFiles) {
|
|
140
|
+
if (f.endsWith(".json")) {
|
|
141
|
+
files.credentialFiles.push(join(credDir, f));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
// No credentials dir
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// Can't stat
|
|
152
|
+
}
|
|
153
|
+
// Auth profile files (agents/*/auth-profiles.json)
|
|
154
|
+
try {
|
|
155
|
+
const matches = await glob("agents/*/auth-profiles.json", {
|
|
156
|
+
cwd: resolvedRoot,
|
|
157
|
+
absolute: true,
|
|
158
|
+
});
|
|
159
|
+
files.authProfileFiles = matches;
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
// glob failed
|
|
163
|
+
}
|
|
164
|
+
// Session log files (agents/*/sessions/*.jsonl)
|
|
165
|
+
try {
|
|
166
|
+
const matches = await glob("agents/*/sessions/*.jsonl", {
|
|
167
|
+
cwd: resolvedRoot,
|
|
168
|
+
absolute: true,
|
|
169
|
+
});
|
|
170
|
+
files.sessionLogFiles = matches;
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
// glob failed
|
|
174
|
+
}
|
|
175
|
+
// Skill files (managed)
|
|
176
|
+
try {
|
|
177
|
+
const matches = await glob("skills/*/SKILL.md", {
|
|
178
|
+
cwd: resolvedRoot,
|
|
179
|
+
absolute: true,
|
|
180
|
+
});
|
|
181
|
+
files.skillFiles = matches;
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
// glob failed
|
|
185
|
+
}
|
|
186
|
+
// Workspace files
|
|
187
|
+
if (workspaceDir) {
|
|
188
|
+
const wsResolved = resolve(workspaceDir);
|
|
189
|
+
const mdFiles = ["SOUL.md", "AGENTS.md", "TOOLS.md", "MEMORY.md"];
|
|
190
|
+
for (const md of mdFiles) {
|
|
191
|
+
const candidate = join(wsResolved, md);
|
|
192
|
+
try {
|
|
193
|
+
await access(candidate, constants.R_OK);
|
|
194
|
+
files.workspaceMarkdownFiles.push(candidate);
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
// File doesn't exist
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// memory/*.md
|
|
201
|
+
try {
|
|
202
|
+
const matches = await glob("memory/*.md", {
|
|
203
|
+
cwd: wsResolved,
|
|
204
|
+
absolute: true,
|
|
205
|
+
});
|
|
206
|
+
files.workspaceMarkdownFiles.push(...matches);
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
// glob failed
|
|
210
|
+
}
|
|
211
|
+
// Workspace skills
|
|
212
|
+
try {
|
|
213
|
+
const matches = await glob("skills/*/SKILL.md", {
|
|
214
|
+
cwd: wsResolved,
|
|
215
|
+
absolute: true,
|
|
216
|
+
});
|
|
217
|
+
files.skillFiles.push(...matches);
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
// glob failed
|
|
221
|
+
}
|
|
222
|
+
// Custom command files (.claude/commands/*.md)
|
|
223
|
+
try {
|
|
224
|
+
const matches = await glob(".claude/commands/*.md", {
|
|
225
|
+
cwd: wsResolved,
|
|
226
|
+
absolute: true,
|
|
227
|
+
});
|
|
228
|
+
files.customCommandFiles.push(...matches);
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
// glob failed
|
|
232
|
+
}
|
|
233
|
+
// Skill package.json files (workspace)
|
|
234
|
+
try {
|
|
235
|
+
const matches = await glob("skills/*/package.json", {
|
|
236
|
+
cwd: wsResolved,
|
|
237
|
+
absolute: true,
|
|
238
|
+
});
|
|
239
|
+
files.skillPackageFiles.push(...matches);
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
// glob failed
|
|
243
|
+
}
|
|
244
|
+
// Private key files
|
|
245
|
+
try {
|
|
246
|
+
const matches = await glob("**/*.{pem,key,p12}", {
|
|
247
|
+
cwd: wsResolved,
|
|
248
|
+
absolute: true,
|
|
249
|
+
maxDepth: 3,
|
|
250
|
+
});
|
|
251
|
+
files.privateKeyFiles.push(...matches);
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
// glob failed
|
|
255
|
+
}
|
|
256
|
+
// SSH key files
|
|
257
|
+
for (const name of ["id_rsa", "id_ed25519"]) {
|
|
258
|
+
const candidate = join(wsResolved, name);
|
|
259
|
+
try {
|
|
260
|
+
await access(candidate, constants.R_OK);
|
|
261
|
+
files.sshKeyFiles.push(candidate);
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
// doesn't exist
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
// Skill package.json files (openclaw dir)
|
|
269
|
+
try {
|
|
270
|
+
const matches = await glob("skills/*/package.json", {
|
|
271
|
+
cwd: resolvedRoot,
|
|
272
|
+
absolute: true,
|
|
273
|
+
});
|
|
274
|
+
files.skillPackageFiles.push(...matches);
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
// glob failed
|
|
278
|
+
}
|
|
279
|
+
return { files, symlinkWarnings };
|
|
280
|
+
}
|
|
281
|
+
//# sourceMappingURL=discover.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discover.js","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"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"}
|
package/dist/fixer.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-fix system.
|
|
3
|
+
*
|
|
4
|
+
* Rules:
|
|
5
|
+
* 1. ALWAYS back up before modifying any file (.bak.{timestamp})
|
|
6
|
+
* 2. "safe" fixes: permissions, .gitignore, tokens — applied automatically
|
|
7
|
+
* 3. "behavioral" fixes: dmPolicy, sandbox changes — prompt user first
|
|
8
|
+
* 4. Log all changes made
|
|
9
|
+
*/
|
|
10
|
+
import type { Finding, FixResult } from "./types.js";
|
|
11
|
+
/**
|
|
12
|
+
* Apply fixes for all auto-fixable findings.
|
|
13
|
+
* Returns a list of what was fixed and what was skipped.
|
|
14
|
+
*/
|
|
15
|
+
export declare function applyFixes(findings: Finding[], configPath: string | null, openclawDir: string): Promise<FixResult[]>;
|
|
16
|
+
//# sourceMappingURL=fixer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixer.d.ts","sourceRoot":"","sources":["../src/fixer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AASH,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAkB,MAAM,YAAY,CAAC;AAuBrE;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,OAAO,EAAE,EACnB,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,SAAS,EAAE,CAAC,CAqEtB"}
|
package/dist/fixer.js
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-fix system.
|
|
3
|
+
*
|
|
4
|
+
* Rules:
|
|
5
|
+
* 1. ALWAYS back up before modifying any file (.bak.{timestamp})
|
|
6
|
+
* 2. "safe" fixes: permissions, .gitignore, tokens — applied automatically
|
|
7
|
+
* 3. "behavioral" fixes: dmPolicy, sandbox changes — prompt user first
|
|
8
|
+
* 4. Log all changes made
|
|
9
|
+
*/
|
|
10
|
+
import { readFile, writeFile, copyFile, chmod } from "node:fs/promises";
|
|
11
|
+
import { createInterface } from "node:readline";
|
|
12
|
+
import { platform } from "node:os";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { randomBytes } from "node:crypto";
|
|
15
|
+
import JSON5 from "json5";
|
|
16
|
+
import chalk from "chalk";
|
|
17
|
+
function backupPath(filePath) {
|
|
18
|
+
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
19
|
+
return `${filePath}.bak.${ts}`;
|
|
20
|
+
}
|
|
21
|
+
async function backupFile(filePath) {
|
|
22
|
+
const dest = backupPath(filePath);
|
|
23
|
+
await copyFile(filePath, dest);
|
|
24
|
+
return dest;
|
|
25
|
+
}
|
|
26
|
+
async function promptUser(message) {
|
|
27
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
28
|
+
return new Promise((resolve) => {
|
|
29
|
+
rl.question(`${message} [y/N] `, (answer) => {
|
|
30
|
+
rl.close();
|
|
31
|
+
resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Apply fixes for all auto-fixable findings.
|
|
37
|
+
* Returns a list of what was fixed and what was skipped.
|
|
38
|
+
*/
|
|
39
|
+
export async function applyFixes(findings, configPath, openclawDir) {
|
|
40
|
+
const results = [];
|
|
41
|
+
const fixable = findings.filter((f) => f.autoFixable);
|
|
42
|
+
if (fixable.length === 0) {
|
|
43
|
+
console.log(chalk.dim("\n No auto-fixable issues found.\n"));
|
|
44
|
+
return results;
|
|
45
|
+
}
|
|
46
|
+
console.log(chalk.cyan(`\n Found ${fixable.length} auto-fixable issue(s). Applying fixes...\n`));
|
|
47
|
+
// Group config-related fixes to apply them in a single write
|
|
48
|
+
const configFixes = [];
|
|
49
|
+
const permissionFixes = [];
|
|
50
|
+
const otherFixes = [];
|
|
51
|
+
for (const f of fixable) {
|
|
52
|
+
if (isConfigFix(f)) {
|
|
53
|
+
configFixes.push(f);
|
|
54
|
+
}
|
|
55
|
+
else if (isPermissionFix(f)) {
|
|
56
|
+
permissionFixes.push(f);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
otherFixes.push(f);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Apply config fixes (batch — single backup + write)
|
|
63
|
+
if (configFixes.length > 0 && configPath) {
|
|
64
|
+
const configResults = await applyConfigFixes(configFixes, configPath);
|
|
65
|
+
results.push(...configResults);
|
|
66
|
+
}
|
|
67
|
+
// Apply permission fixes
|
|
68
|
+
for (const f of permissionFixes) {
|
|
69
|
+
const r = await applyPermissionFix(f);
|
|
70
|
+
results.push(r);
|
|
71
|
+
}
|
|
72
|
+
// Apply other fixes (e.g., .gitignore)
|
|
73
|
+
for (const f of otherFixes) {
|
|
74
|
+
if (f.id === "SECRET-002") {
|
|
75
|
+
const r = await applyGitignoreFix(openclawDir);
|
|
76
|
+
results.push(r);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Summary
|
|
80
|
+
console.log("");
|
|
81
|
+
const applied = results.filter((r) => r.applied).length;
|
|
82
|
+
const skipped = results.filter((r) => !r.applied).length;
|
|
83
|
+
console.log(chalk.green(` ${applied} fix(es) applied, ${skipped} skipped.`));
|
|
84
|
+
for (const r of results) {
|
|
85
|
+
if (r.applied) {
|
|
86
|
+
console.log(chalk.green(` + ${r.description}`));
|
|
87
|
+
if (r.backupPath) {
|
|
88
|
+
console.log(chalk.dim(` Backup: ${r.backupPath}`));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
console.log(chalk.dim(` - Skipped: ${r.description} (${r.skippedReason})`));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
console.log("");
|
|
96
|
+
return results;
|
|
97
|
+
}
|
|
98
|
+
function isConfigFix(finding) {
|
|
99
|
+
// FIX: Removed IDENTITY-008 — applyConfigMutation doesn't implement it,
|
|
100
|
+
// so it would silently fail with "Mutation not implemented" message
|
|
101
|
+
return [
|
|
102
|
+
"IDENTITY-001", "IDENTITY-003", "IDENTITY-005", "IDENTITY-007",
|
|
103
|
+
"IDENTITY-009",
|
|
104
|
+
"NETWORK-001", "NETWORK-002", "NETWORK-003", "NETWORK-004",
|
|
105
|
+
"NETWORK-006", "NETWORK-007",
|
|
106
|
+
"SANDBOX-001", "SANDBOX-006", "SANDBOX-007", "SANDBOX-008",
|
|
107
|
+
].includes(finding.id);
|
|
108
|
+
}
|
|
109
|
+
function isPermissionFix(finding) {
|
|
110
|
+
return [
|
|
111
|
+
"IDENTITY-012",
|
|
112
|
+
"SECRET-003", "SECRET-004", "SECRET-005", "SECRET-006",
|
|
113
|
+
].includes(finding.id);
|
|
114
|
+
}
|
|
115
|
+
async function applyConfigFixes(fixes, configPath) {
|
|
116
|
+
const results = [];
|
|
117
|
+
// Separate safe vs behavioral
|
|
118
|
+
const safeFixes = fixes.filter((f) => f.fixType === "safe");
|
|
119
|
+
const behavioralFixes = fixes.filter((f) => f.fixType === "behavioral");
|
|
120
|
+
// Prompt for behavioral fixes
|
|
121
|
+
const approvedBehavioral = [];
|
|
122
|
+
for (const f of behavioralFixes) {
|
|
123
|
+
console.log(chalk.yellow(` Behavioral change: ${f.title}`));
|
|
124
|
+
console.log(chalk.dim(` ${f.description}`));
|
|
125
|
+
const approved = await promptUser(chalk.yellow(" Apply this change?"));
|
|
126
|
+
if (approved) {
|
|
127
|
+
approvedBehavioral.push(f);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
results.push({
|
|
131
|
+
finding: f,
|
|
132
|
+
applied: false,
|
|
133
|
+
description: f.title,
|
|
134
|
+
skippedReason: "User declined behavioral change",
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const allApproved = [...safeFixes, ...approvedBehavioral];
|
|
139
|
+
if (allApproved.length === 0)
|
|
140
|
+
return results;
|
|
141
|
+
// Backup config file
|
|
142
|
+
const bak = await backupFile(configPath);
|
|
143
|
+
console.log(chalk.dim(` Backup created: ${bak}`));
|
|
144
|
+
// Read and parse config
|
|
145
|
+
const raw = await readFile(configPath, "utf-8");
|
|
146
|
+
let config;
|
|
147
|
+
try {
|
|
148
|
+
config = JSON5.parse(raw);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
results.push({
|
|
152
|
+
finding: allApproved[0],
|
|
153
|
+
applied: false,
|
|
154
|
+
description: "Config file fixes",
|
|
155
|
+
skippedReason: "Could not parse openclaw.json",
|
|
156
|
+
});
|
|
157
|
+
return results;
|
|
158
|
+
}
|
|
159
|
+
// Apply each fix
|
|
160
|
+
for (const f of allApproved) {
|
|
161
|
+
const applied = applyConfigMutation(config, f);
|
|
162
|
+
results.push({
|
|
163
|
+
finding: f,
|
|
164
|
+
applied,
|
|
165
|
+
backupPath: bak,
|
|
166
|
+
description: f.title,
|
|
167
|
+
skippedReason: applied ? undefined : "Mutation not implemented for this check",
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
// Write updated config
|
|
171
|
+
await writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
172
|
+
return results;
|
|
173
|
+
}
|
|
174
|
+
function applyConfigMutation(config, finding) {
|
|
175
|
+
switch (finding.id) {
|
|
176
|
+
// Network fixes
|
|
177
|
+
case "NETWORK-001": {
|
|
178
|
+
if (!config.gateway)
|
|
179
|
+
config.gateway = {};
|
|
180
|
+
config.gateway.bind = "127.0.0.1";
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
case "NETWORK-002": {
|
|
184
|
+
if (!config.gateway)
|
|
185
|
+
config.gateway = {};
|
|
186
|
+
if (!config.gateway.auth)
|
|
187
|
+
config.gateway.auth = {};
|
|
188
|
+
config.gateway.auth.mode = "token";
|
|
189
|
+
config.gateway.auth.token = randomBytes(32).toString("hex");
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
case "NETWORK-003":
|
|
193
|
+
case "NETWORK-004": {
|
|
194
|
+
if (!config.gateway)
|
|
195
|
+
config.gateway = {};
|
|
196
|
+
if (!config.gateway.auth)
|
|
197
|
+
config.gateway.auth = {};
|
|
198
|
+
config.gateway.auth.token = randomBytes(32).toString("hex");
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
case "NETWORK-006": {
|
|
202
|
+
if (!config.gateway)
|
|
203
|
+
config.gateway = {};
|
|
204
|
+
config.gateway.allowInsecureAuth = false;
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
case "NETWORK-007": {
|
|
208
|
+
if (!config.gateway)
|
|
209
|
+
config.gateway = {};
|
|
210
|
+
config.gateway.dangerouslyDisableDeviceAuth = false;
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
// Identity fixes (behavioral — already prompted)
|
|
214
|
+
case "IDENTITY-001": {
|
|
215
|
+
// Set dmPolicy to "pairing" for matching channel
|
|
216
|
+
if (config.channels) {
|
|
217
|
+
for (const ch of Object.values(config.channels)) {
|
|
218
|
+
if (ch.dmPolicy === "open") {
|
|
219
|
+
ch.dmPolicy = "pairing";
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
case "IDENTITY-003": {
|
|
226
|
+
if (config.channels) {
|
|
227
|
+
for (const ch of Object.values(config.channels)) {
|
|
228
|
+
if (ch.groupPolicy === "open") {
|
|
229
|
+
ch.groupPolicy = "allowlist";
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
case "IDENTITY-005": {
|
|
236
|
+
if (config.channels) {
|
|
237
|
+
for (const ch of Object.values(config.channels)) {
|
|
238
|
+
if (ch.requireMention === false) {
|
|
239
|
+
ch.requireMention = true;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
case "IDENTITY-007": {
|
|
246
|
+
if (!config.pairing)
|
|
247
|
+
config.pairing = {};
|
|
248
|
+
config.pairing.storeTTL = 86400; // 24 hours
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
case "IDENTITY-009": {
|
|
252
|
+
if (!config.commands)
|
|
253
|
+
config.commands = {};
|
|
254
|
+
config.commands.useAccessGroups = true;
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
// Sandbox fixes
|
|
258
|
+
case "SANDBOX-001": {
|
|
259
|
+
if (!config.sandbox)
|
|
260
|
+
config.sandbox = {};
|
|
261
|
+
config.sandbox.mode = "all";
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
case "SANDBOX-006": {
|
|
265
|
+
if (config.sandbox?.docker) {
|
|
266
|
+
config.sandbox.docker.network = "none";
|
|
267
|
+
}
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
case "SANDBOX-007": {
|
|
271
|
+
if (config.sandbox?.docker) {
|
|
272
|
+
config.sandbox.docker.socketMounted = false;
|
|
273
|
+
}
|
|
274
|
+
return true;
|
|
275
|
+
}
|
|
276
|
+
case "SANDBOX-008": {
|
|
277
|
+
if (config.sandbox?.browser) {
|
|
278
|
+
config.sandbox.browser.allowHostControl = false;
|
|
279
|
+
}
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
282
|
+
default:
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
async function applyPermissionFix(finding) {
|
|
287
|
+
if (platform() === "win32") {
|
|
288
|
+
return {
|
|
289
|
+
finding,
|
|
290
|
+
applied: false,
|
|
291
|
+
description: finding.title,
|
|
292
|
+
skippedReason: "File permission fixes require Unix (use Windows Security settings)",
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
if (!finding.file) {
|
|
296
|
+
return {
|
|
297
|
+
finding,
|
|
298
|
+
applied: false,
|
|
299
|
+
description: finding.title,
|
|
300
|
+
skippedReason: "No file path in finding",
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
try {
|
|
304
|
+
const mode = finding.id === "SECRET-003" ? 0o700 : 0o600;
|
|
305
|
+
await chmod(finding.file, mode);
|
|
306
|
+
return {
|
|
307
|
+
finding,
|
|
308
|
+
applied: true,
|
|
309
|
+
description: `Set ${finding.file} to ${mode.toString(8)}`,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
catch (err) {
|
|
313
|
+
return {
|
|
314
|
+
finding,
|
|
315
|
+
applied: false,
|
|
316
|
+
description: finding.title,
|
|
317
|
+
skippedReason: `chmod failed: ${err instanceof Error ? err.message : "unknown error"}`,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
async function applyGitignoreFix(openclawDir) {
|
|
322
|
+
// FIX: Removed redundant dynamic import — join is now imported at top level
|
|
323
|
+
const gitignorePath = join(openclawDir, ".gitignore");
|
|
324
|
+
try {
|
|
325
|
+
let content = "";
|
|
326
|
+
try {
|
|
327
|
+
content = await readFile(gitignorePath, "utf-8");
|
|
328
|
+
}
|
|
329
|
+
catch {
|
|
330
|
+
// File doesn't exist — create it
|
|
331
|
+
}
|
|
332
|
+
const bak = content ? await backupFile(gitignorePath) : undefined;
|
|
333
|
+
const entries = [".env", ".env.*", "credentials/", "*.key", "*.pem"];
|
|
334
|
+
const missing = entries.filter((e) => !content.includes(e));
|
|
335
|
+
if (missing.length === 0) {
|
|
336
|
+
return {
|
|
337
|
+
finding: { id: "SECRET-002" },
|
|
338
|
+
applied: false,
|
|
339
|
+
description: "Update .gitignore",
|
|
340
|
+
skippedReason: "All entries already present",
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
const addition = `\n# Added by Clawhatch security scanner\n${missing.join("\n")}\n`;
|
|
344
|
+
await writeFile(gitignorePath, content + addition, "utf-8");
|
|
345
|
+
return {
|
|
346
|
+
finding: { id: "SECRET-002" },
|
|
347
|
+
applied: true,
|
|
348
|
+
backupPath: bak,
|
|
349
|
+
description: `Added ${missing.length} entries to .gitignore`,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
catch (err) {
|
|
353
|
+
return {
|
|
354
|
+
finding: { id: "SECRET-002" },
|
|
355
|
+
applied: false,
|
|
356
|
+
description: "Update .gitignore",
|
|
357
|
+
skippedReason: `Failed: ${err instanceof Error ? err.message : "unknown error"}`,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
//# sourceMappingURL=fixer.js.map
|