mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-11 04:14:32 +00:00
fix: harden sandbox writes and centralize atomic file writes
This commit is contained in:
@@ -14,23 +14,45 @@ export async function readJsonFile<T>(filePath: string): Promise<T | null> {
|
||||
export async function writeJsonAtomic(
|
||||
filePath: string,
|
||||
value: unknown,
|
||||
options?: { mode?: number },
|
||||
options?: { mode?: number; trailingNewline?: boolean; ensureDirMode?: number },
|
||||
) {
|
||||
const text = JSON.stringify(value, null, 2);
|
||||
await writeTextAtomic(filePath, text, {
|
||||
mode: options?.mode,
|
||||
ensureDirMode: options?.ensureDirMode,
|
||||
appendTrailingNewline: options?.trailingNewline,
|
||||
});
|
||||
}
|
||||
|
||||
export async function writeTextAtomic(
|
||||
filePath: string,
|
||||
content: string,
|
||||
options?: { mode?: number; ensureDirMode?: number; appendTrailingNewline?: boolean },
|
||||
) {
|
||||
const mode = options?.mode ?? 0o600;
|
||||
const dir = path.dirname(filePath);
|
||||
await fs.mkdir(dir, { recursive: true });
|
||||
const tmp = `${filePath}.${randomUUID()}.tmp`;
|
||||
await fs.writeFile(tmp, JSON.stringify(value, null, 2), "utf8");
|
||||
try {
|
||||
await fs.chmod(tmp, mode);
|
||||
} catch {
|
||||
// best-effort; ignore on platforms without chmod
|
||||
const payload =
|
||||
options?.appendTrailingNewline && !content.endsWith("\n") ? `${content}\n` : content;
|
||||
const mkdirOptions: { recursive: true; mode?: number } = { recursive: true };
|
||||
if (typeof options?.ensureDirMode === "number") {
|
||||
mkdirOptions.mode = options.ensureDirMode;
|
||||
}
|
||||
await fs.rename(tmp, filePath);
|
||||
await fs.mkdir(path.dirname(filePath), mkdirOptions);
|
||||
const tmp = `${filePath}.${randomUUID()}.tmp`;
|
||||
try {
|
||||
await fs.chmod(filePath, mode);
|
||||
} catch {
|
||||
// best-effort; ignore on platforms without chmod
|
||||
await fs.writeFile(tmp, payload, "utf8");
|
||||
try {
|
||||
await fs.chmod(tmp, mode);
|
||||
} catch {
|
||||
// best-effort; ignore on platforms without chmod
|
||||
}
|
||||
await fs.rename(tmp, filePath);
|
||||
try {
|
||||
await fs.chmod(filePath, mode);
|
||||
} catch {
|
||||
// best-effort; ignore on platforms without chmod
|
||||
}
|
||||
} finally {
|
||||
await fs.rm(tmp, { force: true }).catch(() => undefined);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user