fix(cras-install): dejar de rechazar el layout normal de carpetas de trabajo

isInsideWorkFolder miraba la relación en los dos sentidos, así que también
rechazaba que las carpetas de trabajo colgaran de la de instalación. Ese 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.

Efecto: ninguna instalación ni actualización podía pasar, en Linux ni en
Windows. Fallaba en resolveInstallPath —antes de abrir el run, así que no
quedaba nada en la bitácora— con un mensaje que decía justo lo contrario de lo
que ocurría: "la ruta está dentro de una carpeta de trabajo". Solo se libraban
las instalaciones fuera de la ruta por omisión, por accidente del nombre.

Se conserva el peligro que el guard existe para atajar: el binario dentro de
Entrada/Procesados, donde el agente lo tomaría por un respaldo a procesar.

La función no tenía ninguna prueba. Se agregan las dos direcciones, el caso
exacto, la normalización de separador/caja/barra final y el prefijo que no es
de carpeta (/srv/entradas vs /srv/entrada).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-30 12:25:01 -06:00
parent d570c7dac2
commit 5b83a57d18
2 changed files with 68 additions and 1 deletions

View File

@@ -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);
});
});

View File

@@ -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}/`);
});
}