From 5a23032adba4d471811af151348c7fb614fcbbdc Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 27 Apr 2026 20:47:49 +0100 Subject: [PATCH] fix(plugins): detect install root rebinding --- src/infra/install-package-dir.test.ts | 4 ++-- src/infra/install-package-dir.ts | 22 +++++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/infra/install-package-dir.test.ts b/src/infra/install-package-dir.test.ts index ffeb80eaae4..540bcaf0090 100644 --- a/src/infra/install-package-dir.test.ts +++ b/src/infra/install-package-dir.test.ts @@ -218,7 +218,7 @@ describe("installPackageDir", () => { installBaseDir, preservedDir: preservedInstallRoot, outsideTarget: outsideInstallRoot, - rebindAtCall: 3, + rebindAtCall: 4, run: async () => { await expect( installPackageDir({ @@ -259,7 +259,7 @@ describe("installPackageDir", () => { installBaseDir, preservedDir: preservedInstallRoot, outsideTarget: outsideInstallRoot, - rebindAtCall: 7, + rebindAtCall: 8, run: async () => await installPackageDir({ sourceDir, diff --git a/src/infra/install-package-dir.ts b/src/infra/install-package-dir.ts index 357f800f165..6adf27d69f0 100644 --- a/src/infra/install-package-dir.ts +++ b/src/infra/install-package-dir.ts @@ -170,11 +170,17 @@ export async function installPackageDir(params: { }): Promise<{ ok: true } | { ok: false; error: string; code?: string }> { params.logger?.info?.(`Installing to ${params.targetDir}…`); const installBaseDir = path.dirname(params.targetDir); - await fs.mkdir(installBaseDir, { recursive: true }); - await assertInstallBoundaryPaths({ - installBaseDir, - candidatePaths: [params.targetDir], - }); + let initialInstallBaseRealPath: string; + try { + await fs.mkdir(installBaseDir, { recursive: true }); + initialInstallBaseRealPath = await fs.realpath(installBaseDir); + await assertInstallBoundaryPaths({ + installBaseDir, + candidatePaths: [params.targetDir], + }); + } catch (err) { + return { ok: false, error: `${params.copyErrorPrefix}: ${String(err)}` }; + } let installBaseRealPath: string; let canonicalTargetDir: string; try { @@ -182,7 +188,13 @@ export async function installPackageDir(params: { installBaseDir, targetDir: params.targetDir, })); + if (installBaseRealPath !== initialInstallBaseRealPath) { + throw new Error(INSTALL_BASE_CHANGED_ERROR_MESSAGE); + } } catch (err) { + if (isInstallBaseChangedError(err)) { + params.logger?.warn?.(INSTALL_BASE_CHANGED_ABORT_WARNING); + } return { ok: false, error: `${params.copyErrorPrefix}: ${String(err)}` }; }