Skip to content

Commit c1a0a58

Browse files
committed
doctor: require searchable config write parents
1 parent 162601a commit c1a0a58

2 files changed

Lines changed: 36 additions & 3 deletions

File tree

src/flows/doctor-health-contributions.test.ts

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2775,7 +2775,10 @@ describe("doctor health contributions", () => {
27752775
).resolves.toMatchObject({
27762776
findings: [],
27772777
});
2778-
expect(accessSpy).toHaveBeenCalledWith("/tmp/openclaw-home", fs.constants.W_OK);
2778+
expect(accessSpy).toHaveBeenCalledWith(
2779+
"/tmp/openclaw-home",
2780+
fs.constants.W_OK | fs.constants.X_OK,
2781+
);
27792782
});
27802783

27812784
it("reports an unwritable config directory for an existing config", async () => {
@@ -2827,7 +2830,7 @@ describe("doctor health contributions", () => {
28272830
).resolves.toMatchObject({
28282831
findings: [],
28292832
});
2830-
expect(accessSpy).toHaveBeenCalledWith("/tmp", fs.constants.W_OK);
2833+
expect(accessSpy).toHaveBeenCalledWith("/tmp", fs.constants.W_OK | fs.constants.X_OK);
28312834
});
28322835

28332836
it("reports an unwritable existing parent when the config file is missing", async () => {
@@ -2858,6 +2861,36 @@ describe("doctor health contributions", () => {
28582861
});
28592862
});
28602863

2864+
it("reports an existing parent without search permission", async () => {
2865+
vi.spyOn(fs, "existsSync").mockImplementation((path) => path === "/tmp");
2866+
vi.spyOn(fs, "accessSync").mockImplementation((_path, mode) => {
2867+
if (mode === (fs.constants.W_OK | fs.constants.X_OK)) {
2868+
throw new Error("EACCES");
2869+
}
2870+
});
2871+
2872+
await expect(
2873+
runDoctorLintChecks(
2874+
{
2875+
cfg: {},
2876+
mode: "lint" as const,
2877+
runtime: { log: vi.fn(), error: vi.fn(), exit: vi.fn() },
2878+
configPath: "/tmp/openclaw-home/openclaw.json",
2879+
},
2880+
{ checks: [check], onlyIds: ["core/doctor/write-config"] },
2881+
),
2882+
).resolves.toMatchObject({
2883+
findings: [
2884+
expect.objectContaining({
2885+
checkId: "core/doctor/write-config",
2886+
path: "/tmp",
2887+
target: "/tmp/openclaw-home",
2888+
requirement: "writable-config-directory",
2889+
}),
2890+
],
2891+
});
2892+
});
2893+
28612894
it("reports an existing file that blocks the config directory path", async () => {
28622895
vi.spyOn(fs, "existsSync").mockImplementation((path) => path === "/tmp/openclaw-home");
28632896
vi.spyOn(fs, "statSync").mockReturnValue({

src/flows/doctor-health-contributions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1347,7 +1347,7 @@ async function collectWriteConfigHealthFindings(
13471347
return findings;
13481348
}
13491349
try {
1350-
fs.accessSync(existingParent, fs.constants.W_OK);
1350+
fs.accessSync(existingParent, fs.constants.W_OK | fs.constants.X_OK);
13511351
} catch {
13521352
findings.push({
13531353
checkId: "core/doctor/write-config",

0 commit comments

Comments
 (0)