Skip to content

Commit c04ea0e

Browse files
committed
test: tighten tmp dir security coverage
1 parent cb99a23 commit c04ea0e

1 file changed

Lines changed: 71 additions & 0 deletions

File tree

src/infra/tmp-openclaw-dir.test.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,27 @@ describe("resolvePreferredOpenClawTmpDir", () => {
197197
expectFallsBackToOsTmpDir({ lstatSync: vi.fn(() => makeDirStat({ mode: 0o40777 })) });
198198
});
199199

200+
it("repairs existing /tmp/openclaw permissions when they are too broad", () => {
201+
let preferredMode = 0o40777;
202+
const chmodSync = vi.fn((target: string, mode: number) => {
203+
if (target === POSIX_OPENCLAW_TMP_DIR && mode === 0o700) {
204+
preferredMode = 0o40700;
205+
}
206+
});
207+
const warn = vi.fn();
208+
209+
const { resolved, tmpdir } = resolveWithMocks({
210+
lstatSync: vi.fn(() => makeDirStat({ mode: preferredMode })),
211+
chmodSync,
212+
warn,
213+
});
214+
215+
expect(resolved).toBe(POSIX_OPENCLAW_TMP_DIR);
216+
expect(chmodSync).toHaveBeenCalledWith(POSIX_OPENCLAW_TMP_DIR, 0o700);
217+
expect(warn).toHaveBeenCalledWith(expect.stringContaining("tightened permissions on temp dir"));
218+
expect(tmpdir).not.toHaveBeenCalled();
219+
});
220+
200221
it("throws when fallback path is a symlink", () => {
201222
const lstatSync = symlinkTmpDirLstat();
202223
const fallbackLstatSync = vi.fn(() => makeDirStat({ isSymbolicLink: true, mode: 0o120777 }));
@@ -222,6 +243,35 @@ describe("resolvePreferredOpenClawTmpDir", () => {
222243
expect(mkdirSync).toHaveBeenCalledWith(fallbackTmp(), { recursive: true, mode: 0o700 });
223244
});
224245

246+
it("uses an unscoped fallback suffix when process uid is unavailable", () => {
247+
const tmpdirPath = "/var/fallback";
248+
const fallbackPath = path.join(tmpdirPath, "openclaw");
249+
250+
const resolved = resolvePreferredOpenClawTmpDir({
251+
accessSync: vi.fn((target: string) => {
252+
if (target === "/tmp") {
253+
throw new Error("read-only");
254+
}
255+
}),
256+
lstatSync: vi.fn((target: string) => {
257+
if (target === POSIX_OPENCLAW_TMP_DIR) {
258+
throw nodeErrorWithCode("ENOENT");
259+
}
260+
if (target === fallbackPath) {
261+
return makeDirStat({ uid: 0, mode: 0o40777 });
262+
}
263+
return secureDirStat();
264+
}),
265+
mkdirSync: vi.fn(),
266+
chmodSync: vi.fn(),
267+
getuid: vi.fn(() => undefined),
268+
tmpdir: vi.fn(() => tmpdirPath),
269+
warn: vi.fn(),
270+
});
271+
272+
expect(resolved).toBe(fallbackPath);
273+
});
274+
225275
it("repairs fallback directory permissions after create when umask makes it group-writable", () => {
226276
const fallbackPath = fallbackTmp();
227277
let fallbackMode = 0o40775;
@@ -287,4 +337,25 @@ describe("resolvePreferredOpenClawTmpDir", () => {
287337
expect(chmodSync).toHaveBeenCalledWith(fallbackPath, 0o700);
288338
expect(warn).toHaveBeenCalledWith(expect.stringContaining("tightened permissions on temp dir"));
289339
});
340+
341+
it("throws when the fallback directory cannot be created", () => {
342+
expect(() =>
343+
resolvePreferredOpenClawTmpDir({
344+
accessSync: readOnlyTmpAccessSync(),
345+
lstatSync: vi.fn((target: string) => {
346+
if (target === POSIX_OPENCLAW_TMP_DIR || target === fallbackTmp()) {
347+
throw nodeErrorWithCode("ENOENT");
348+
}
349+
return secureDirStat();
350+
}),
351+
mkdirSync: vi.fn(() => {
352+
throw new Error("mkdir failed");
353+
}),
354+
chmodSync: vi.fn(),
355+
getuid: vi.fn(() => 501),
356+
tmpdir: vi.fn(() => "/var/fallback"),
357+
warn: vi.fn(),
358+
}),
359+
).toThrow(/Unable to create fallback OpenClaw temp dir/);
360+
});
290361
});

0 commit comments

Comments
 (0)