From 694f1c212f58237b4f85620ed2b458af8a508ad2 Mon Sep 17 00:00:00 2001 From: Nicolas Meienberger Date: Mon, 20 Apr 2026 22:28:42 +0200 Subject: [PATCH] fix(volumes): isolate test-connection mounts in temp directories --- app/server/modules/backends/backend.ts | 16 +++++------ .../volumes/__tests__/volumes.service.test.ts | 28 +++++++++++++++++++ app/server/modules/volumes/volume.service.ts | 3 +- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/app/server/modules/backends/backend.ts b/app/server/modules/backends/backend.ts index 25811167..f6eb3e3a 100644 --- a/app/server/modules/backends/backend.ts +++ b/app/server/modules/backends/backend.ts @@ -19,27 +19,25 @@ export type VolumeBackend = { checkHealth: () => Promise; }; -export const createVolumeBackend = (volume: Volume): VolumeBackend => { - const path = getVolumePath(volume); - +export const createVolumeBackend = (volume: Volume, mountPath = getVolumePath(volume)): VolumeBackend => { switch (volume.config.backend) { case "nfs": { - return makeNfsBackend(volume.config, path); + return makeNfsBackend(volume.config, mountPath); } case "smb": { - return makeSmbBackend(volume.config, path); + return makeSmbBackend(volume.config, mountPath); } case "directory": { - return makeDirectoryBackend(volume.config, path); + return makeDirectoryBackend(volume.config, mountPath); } case "webdav": { - return makeWebdavBackend(volume.config, path); + return makeWebdavBackend(volume.config, mountPath); } case "rclone": { - return makeRcloneBackend(volume.config, path); + return makeRcloneBackend(volume.config, mountPath); } case "sftp": { - return makeSftpBackend(volume.config, path); + return makeSftpBackend(volume.config, mountPath); } default: { throw new Error("Unsupported backend"); diff --git a/app/server/modules/volumes/__tests__/volumes.service.test.ts b/app/server/modules/volumes/__tests__/volumes.service.test.ts index 1aa09f42..3dce96d2 100644 --- a/app/server/modules/volumes/__tests__/volumes.service.test.ts +++ b/app/server/modules/volumes/__tests__/volumes.service.test.ts @@ -211,3 +211,31 @@ describe("volumeService.ensureHealthyVolume", () => { }); }); }); + +describe("volumeService.testConnection", () => { + test("uses an isolated temp mount path for backend test connections", async () => { + const mount = vi.fn().mockResolvedValue({ status: "mounted" }); + const unmount = vi.fn().mockResolvedValue({ status: "unmounted" }); + const createVolumeBackendSpy = vi.spyOn(backendModule, "createVolumeBackend").mockReturnValue({ + mount, + unmount, + checkHealth: vi.fn(), + }); + + await volumeService.testConnection({ + backend: "nfs", + server: "127.0.0.1", + exportPath: "/exports/test", + version: "4", + port: 2049, + readOnly: false, + }); + + expect(createVolumeBackendSpy).toHaveBeenCalledOnce(); + const [, mountPath] = createVolumeBackendSpy.mock.calls[0]; + expect(mountPath).toEqual(expect.stringContaining(`${path.sep}zerobyte-test-`)); + await expect(fs.access(mountPath as string)).rejects.toThrow(); + expect(mount).toHaveBeenCalledOnce(); + expect(unmount).toHaveBeenCalledOnce(); + }); +}); diff --git a/app/server/modules/volumes/volume.service.ts b/app/server/modules/volumes/volume.service.ts index c4b73952..835d3d71 100644 --- a/app/server/modules/volumes/volume.service.ts +++ b/app/server/modules/volumes/volume.service.ts @@ -237,7 +237,6 @@ const testConnection = async (backendConfig: BackendConfig) => { id: 0, shortId: asShortId("test"), name: "test-connection", - path: tempDir, config: encryptedConfig, createdAt: Date.now(), updatedAt: Date.now(), @@ -250,7 +249,7 @@ const testConnection = async (backendConfig: BackendConfig) => { organizationId: "test-org", }; - const backend = createVolumeBackend(mockVolume); + const backend = createVolumeBackend(mockVolume, tempDir); const { error } = await backend.mount(); await backend.unmount();