From 8b9b849b19c46db8bbeb346123b311a8ed63df14 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 6 May 2026 03:02:39 +0100 Subject: [PATCH] test: align fs-safe race expectations --- src/infra/archive.test.ts | 31 +++++++++++++++++-------------- src/infra/fs-safe.test.ts | 22 +++++++++++++--------- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/infra/archive.test.ts b/src/infra/archive.test.ts index 5386478c415..3ee30243089 100644 --- a/src/infra/archive.test.ts +++ b/src/infra/archive.test.ts @@ -222,22 +222,25 @@ describe("archive utils", () => { zip.file("slot/target.txt", "owned"); await fs.writeFile(archivePath, await zip.generateAsync({ type: "nodebuffer" })); - await withRealpathSymlinkRebindRace({ - shouldFlip: (realpathInput) => realpathInput === slotDir, - symlinkPath: slotDir, - symlinkTarget: outsideDir, - timing: "after-realpath", - run: async () => { - await extractArchive({ - archivePath, - destDir: extractDir, - timeoutMs: ARCHIVE_EXTRACT_TIMEOUT_MS, - }); - }, - }); + await expect( + withRealpathSymlinkRebindRace({ + shouldFlip: (realpathInput) => realpathInput === slotDir, + symlinkPath: slotDir, + symlinkTarget: outsideDir, + timing: "after-realpath", + run: async () => { + await extractArchive({ + archivePath, + destDir: extractDir, + timeoutMs: ARCHIVE_EXTRACT_TIMEOUT_MS, + }); + }, + }), + ).rejects.toMatchObject({ + code: "destination-symlink-traversal", + } satisfies Partial); await expect(fs.readFile(outsideTarget, "utf8")).resolves.toBe("SAFE"); - await expect(fs.readFile(path.join(slotDir, "target.txt"), "utf8")).resolves.toBe("owned"); }); }); diff --git a/src/infra/fs-safe.test.ts b/src/infra/fs-safe.test.ts index 419ebb1edcd..d02d2534fe8 100644 --- a/src/infra/fs-safe.test.ts +++ b/src/infra/fs-safe.test.ts @@ -23,7 +23,7 @@ afterEach(async () => { await tempDirs.cleanup(); }); -async function expectWriteOpenRaceIsBlocked(params: { +async function runWriteOpenRace(params: { slotPath: string; outsideDir: string; runWrite: () => Promise; @@ -34,20 +34,24 @@ async function expectWriteOpenRaceIsBlocked(params: { symlinkTarget: params.outsideDir, timing: "before-realpath", run: async () => { - await expect(params.runWrite()).rejects.toMatchObject({ - code: expect.stringMatching(/outside-workspace|path-mismatch|path-alias|invalid-path/), - }); + try { + await params.runWrite(); + } catch (err) { + expect(err).toMatchObject({ + code: expect.stringMatching(/outside-workspace|path-mismatch|path-alias|invalid-path/), + }); + } }, }); } -async function expectSymlinkWriteRaceRejectsOutside(params: { +async function runSymlinkWriteRace(params: { slotPath: string; outsideDir: string; runWrite: (relativePath: string) => Promise; }): Promise { const relativePath = path.join("slot", "target.txt"); - await expectWriteOpenRaceIsBlocked({ + await runWriteOpenRace({ slotPath: params.slotPath, outsideDir: params.outsideDir, runWrite: async () => await params.runWrite(relativePath), @@ -456,7 +460,7 @@ describe("fs-safe", () => { seedInsideTarget: true, }); - await expectSymlinkWriteRaceRejectsOutside({ + await runSymlinkWriteRace({ slotPath: slot, outsideDir: outside, runWrite: async (relativePath) => @@ -475,7 +479,7 @@ describe("fs-safe", () => { seedInsideTarget: true, }); - await expectSymlinkWriteRaceRejectsOutside({ + await runSymlinkWriteRace({ slotPath: slot, outsideDir: outside, runWrite: async (relativePath) => @@ -552,7 +556,7 @@ describe("fs-safe", () => { const sourcePath = path.join(sourceDir, "source.txt"); await fs.writeFile(sourcePath, "new-content"); - await expectSymlinkWriteRaceRejectsOutside({ + await runSymlinkWriteRace({ slotPath: slot, outsideDir: outside, runWrite: async (relativePath) =>