diff --git a/src/lib/cras-version.test.ts b/src/lib/cras-version.test.ts index 9941016..3b8f4cb 100644 --- a/src/lib/cras-version.test.ts +++ b/src/lib/cras-version.test.ts @@ -12,6 +12,7 @@ import { effectivePlatform, installPlatformVerdict, isCrasPlatform, + isInsideWorkFolder, isNewer, isValidVersion, osToPlatform, @@ -201,3 +202,61 @@ describe('installPlatformVerdict', () => { expect(installPlatformVerdict(null, 'windows', ' WINDOWS ')).toBe('ok'); }); }); + +/** + * Guard de la ruta de instalación contra las carpetas de trabajo del agente. + * + * No tenía ni una prueba, y con la relación mirada en los dos sentidos rechazaba el layout + * NORMAL —`DIR_ENTRADA = APP_DIR / "Entrada"` en constants.py del agente— así que ninguna + * instalación ni actualización podía pasar. Se salvaban solo las instalaciones fuera de la ruta + * por omisión, por accidente del nombre. + */ +describe('isInsideWorkFolder', () => { + it('acepta el layout normal: las carpetas de trabajo cuelgan de la de instalación', () => { + expect( + isInsideWorkFolder('/opt/cloudrestoreas', [ + '/opt/cloudrestoreas/Entrada', + '/opt/cloudrestoreas/Procesados' + ]) + ).toBe(false); + expect( + isInsideWorkFolder('C:\\Aduanasoft\\CloudRestoreAS', [ + 'C:\\Aduanasoft\\CloudRestoreAS\\Entrada' + ]) + ).toBe(false); + }); + + it('rechaza el binario DENTRO de una carpeta de trabajo, que es el peligro real', () => { + // Ahí el agente tomaría su propio ejecutable por un respaldo a procesar. + expect(isInsideWorkFolder('/srv/entrada/cras', ['/srv/entrada'])).toBe(true); + expect( + isInsideWorkFolder('D:\\Respaldos\\Entrada\\app', ['D:\\Respaldos\\Entrada']) + ).toBe(true); + }); + + it('rechaza instalar exactamente EN la carpeta de trabajo', () => { + expect(isInsideWorkFolder('/srv/entrada', ['/srv/entrada'])).toBe(true); + expect(isInsideWorkFolder('/srv/entrada/', ['/srv/entrada'])).toBe(true); + }); + + it('normaliza separadores, barra final y caja antes de comparar', () => { + expect(isInsideWorkFolder('C:/Respaldos/Entrada/app', ['c:\\respaldos\\entrada\\'])).toBe( + true + ); + }); + + it('un prefijo que no es de carpeta no cuenta como estar dentro', () => { + // Es el caso que dejaba pasar a Principal: -win rompe el prefijo de carpeta. + expect( + isInsideWorkFolder('C:\\Aduanasoft\\CloudRestoreAS', [ + 'C:\\Aduanasoft\\CloudRestoreAS-win\\Entrada' + ]) + ).toBe(false); + expect(isInsideWorkFolder('/srv/entradas', ['/srv/entrada'])).toBe(false); + }); + + it('ignora carpetas vacías o sin reportar en lugar de bloquear', () => { + expect(isInsideWorkFolder('/opt/cloudrestoreas', [null, undefined, '', ' '])).toBe(false); + expect(isInsideWorkFolder('', ['/srv/entrada'])).toBe(false); + }); +}); diff --git a/src/lib/cras-version.ts b/src/lib/cras-version.ts index 5457ab1..8914083 100644 --- a/src/lib/cras-version.ts +++ b/src/lib/cras-version.ts @@ -189,6 +189,14 @@ export function isDefaultInstallPath( * * Instalar el binario dentro de `Entrada`/`Procesados` sería grave: el agente vigila esa * carpeta y trataría de procesar sus propios archivos como si fueran respaldos. + * + * La relación se mira en UN solo sentido, y eso es deliberado. La carpeta de trabajo **debajo** + * de la de instalación es el layout normal, no un error: `constants.py` del agente define + * `DIR_ENTRADA = APP_DIR / "Entrada"`, así que una instalación correcta en + * `/opt/cloudrestoreas` reporta `input_folder = /opt/cloudrestoreas/Entrada`. Cuando esto también + * rechazaba ese caso, ninguna instalación ni actualización podía pasar: fallaba en + * resolveInstallPath, antes de abrir el run, con un mensaje que decía justo lo contrario de lo + * que ocurría. Solo se salvaban las instalaciones fuera de la ruta por omisión, por accidente. */ export function isInsideWorkFolder( installPath: string | null | undefined, @@ -206,7 +214,7 @@ export function isInsideWorkFolder( return workFolders.some((folder) => { const base = norm(folder); if (!base) return false; - return target === base || target.startsWith(`${base}/`) || base.startsWith(`${target}/`); + return target === base || target.startsWith(`${base}/`); }); }