test: align fs-safe race expectations

This commit is contained in:
Peter Steinberger
2026-05-06 03:02:39 +01:00
parent 9671a91590
commit 8b9b849b19
2 changed files with 30 additions and 23 deletions

View File

@@ -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<ArchiveSecurityError>);
await expect(fs.readFile(outsideTarget, "utf8")).resolves.toBe("SAFE");
await expect(fs.readFile(path.join(slotDir, "target.txt"), "utf8")).resolves.toBe("owned");
});
});

View File

@@ -23,7 +23,7 @@ afterEach(async () => {
await tempDirs.cleanup();
});
async function expectWriteOpenRaceIsBlocked(params: {
async function runWriteOpenRace(params: {
slotPath: string;
outsideDir: string;
runWrite: () => Promise<void>;
@@ -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<void>;
}): Promise<void> {
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) =>