Skip to content

Commit 91fe6a6

Browse files
committed
fix(exec-approvals): normalize fallback temp mode
1 parent 460ad5c commit 91fe6a6

2 files changed

Lines changed: 39 additions & 0 deletions

File tree

src/infra/exec-approvals-store.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,40 @@ describe("exec approvals store helpers", () => {
237237
expect(listExecApprovalTempFiles(dir)).toEqual([]);
238238
});
239239

240+
it("normalizes fallback temp files before copying", () => {
241+
const dir = createHomeDir();
242+
const approvalsPath = approvalsFilePath(dir);
243+
fs.mkdirSync(path.dirname(approvalsPath), { recursive: true });
244+
fs.writeFileSync(approvalsPath, '{"version":1,"agents":{}}\n', "utf8");
245+
const actualWriteFileSync = fs.writeFileSync.bind(fs);
246+
vi.spyOn(fs, "writeFileSync").mockImplementation((file, data, options) => {
247+
const result = actualWriteFileSync(file, data, options as never);
248+
const filePath = String(file);
249+
if (
250+
typeof file !== "number" &&
251+
filePath.includes(".exec-approvals.") &&
252+
filePath.endsWith(".tmp")
253+
) {
254+
fs.chmodSync(file, 0o000);
255+
}
256+
return result;
257+
});
258+
const actualRenameSync = fs.renameSync.bind(fs);
259+
vi.spyOn(fs, "renameSync").mockImplementation((from, to) => {
260+
if (String(to) === approvalsPath) {
261+
const error = Object.assign(new Error("locked target"), { code: "EPERM" });
262+
throw error;
263+
}
264+
return actualRenameSync(from, to);
265+
});
266+
267+
saveExecApprovals({ version: 1, defaults: { security: "full" }, agents: {} });
268+
269+
expect(fs.readFileSync(approvalsPath, "utf8")).toContain('"security": "full"');
270+
expect(fs.statSync(approvalsPath).mode & 0o777).toBe(0o600);
271+
expect(listExecApprovalTempFiles(dir)).toEqual([]);
272+
});
273+
240274
it("restores the previous approvals file when fallback copy fails", () => {
241275
const dir = createHomeDir();
242276
const approvalsPath = approvalsFilePath(dir);

src/infra/exec-approvals.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,11 @@ function writeExecApprovalsRaw(filePath: string, raw: string) {
705705
let tempWritten = false;
706706
try {
707707
fs.writeFileSync(tempPath, raw, { mode: 0o600, flag: "wx" });
708+
try {
709+
fs.chmodSync(tempPath, 0o600);
710+
} catch {
711+
// best-effort on platforms without chmod
712+
}
708713
tempWritten = true;
709714
renameExecApprovalsWithFallback(tempPath, filePath);
710715
} finally {

0 commit comments

Comments
 (0)