Media

ApostropheCMS import-export tar guard misses './..' because it is a literal '../' substring test

  • Publicada: 20.10.2026
  • Actualizado: 20.10.2026 21:35

Resumen

La protección de extracción tar agregada a @apostrophecms/import-export para GHSA-mwxc-m426-3f78 consiste en una comprobación literal de subcadena para ../, y ./.. no contiene dicha subcadena. Por lo tanto, una entrada llamada ./.. pasa la validación, path.join la normaliza hacia el directorio padre del directorio de exportación, y el fs.WriteStream resultante abre un directorio y emite EISDIR sin ningún listener error asignado, lo que finaliza el proceso de Node.

Confirmado en la rama por defecto HEAD 1bd5d0a7a6fe880836fa75adad6964f1c577d640, versión de paquete 3.6.2.

Detalles

packages/import-export/lib/formats/gzip.js, en el manejador entry del flujo tar dentro de extract(filepath, exportPath):

js
const name = header.name.replace(/\\/g, '/');
if (name.includes('../')) {
  // skipped
}
...
stream.pipe(fs.WriteStream(path.join(exportPath, name)));

Existen dos problemas distintos en esas cuatro líneas. La protección verifica el nombre de entrada sin procesar buscando una subcadena en lugar de resolver la ruta combinada y requerir contención dentro del directorio, y el flujo de escritura carece de un manejador error, por lo que cualquier error del flujo se convierte en un evento 'error' no controlado.

Para alcanzarlo solo se requiere una cuenta con permisos de importación. packages/import-export/modules/@apostrophecms/import-export-page/index.js expone post.importExportImport, que llama a lib/methods/import.js; dicho manejador requiere req.user y luego llama a format.input(exportPath), que para una subida .gz consiste en gzip.js invocando extract() sobre el archivo subido.

PoC

Esto no es un exploit de extremo a extremo. A continuación se presentan cada una de las tres primitivas de las que depende la caída, ejecutadas individualmente, junto con un rastreo de la ruta a nivel de código fuente. La cadena desde post.importExportImport hasta extract() fue analizada en el código, no observada en ejecución.

La protección no detecta la carga útil:

bash
node -e "console.log('./..'.includes('../')); console.log('..'.includes('../')); console.log('a/./..'.includes('../'));"
false
false
false

path.join luego la resuelve fuera del directorio de exportación:

bash
node -e "const path=require('path'); const exportPath='/tmp/exportRoot'; console.log(path.join(exportPath,'./..'));"
/tmp

Y un flujo de escritura abierto sobre un directorio, sin un listener error, detiene el proceso en lugar de devolver un error en la solicitud:

bash
node -e "const fs=require('fs'); const s=fs.createWriteStream('/tmp'); s.write('x'); s.end(); setTimeout(()=>console.log('still alive'),500);"
events.js:291
      throw er; // Unhandled 'error' event
      ^

Error: EISDIR: illegal operation on a directory, open '/tmp'
Emitted 'error' event on WriteStream instance at:
    at internal/fs/streams.js:361:12
    at FSReqCallback.oncomplete (fs.js:156:23) {
  errno: -21,
  code: 'EISDIR',
  syscall: 'open',
  path: '/tmp'
}

El frame events.js:291 muestra que la salida provino de la línea de Node 14 presente en el entorno de auditoría. El comportamiento ante eventos error no manejados no es específico de una versión, aunque vale la pena volver a probar el mensaje exacto en las versiones de Node compatibles.

Confirmación en el código fuente sin ejecutar nada:

bash
grep -n "includes('../')" packages/import-export/lib/formats/gzip.js
grep -n "WriteStream" packages/import-export/lib/formats/gzip.js

Impacto

Un usuario autenticado que pueda acceder a la interfaz de importación puede subir un archivo manipulado y provocar la detención del proceso de Node que lo gestiona. Cada worker que procese dicho archivo caerá de la misma manera.

Solución sugerida

Resolver la ruta de destino, exigir contención y asociar un manejador de errores:

js
const base = path.resolve(exportPath);
const target = path.resolve(path.join(exportPath, name));

if (target !== base && !target.startsWith(base + path.sep)) {
  stream.on('end', next);
  stream.resume();
  return;
}

if (header.type === 'directory') {
  fsp.mkdir(target).then(next).catch(reject);
} else {
  const out = fs.WriteStream(target);
  out.on('error', reject);
  stream.pipe(out);
  stream.on('end', next);
}

El manejador de errores se justifica de forma independiente. Cualquier entrada tar que coincida con el nombre de un directorio existente tiene el mismo efecto, implique o no un salto de directorio.

Por qué esto no es GHSA-mwxc-m426-3f78

La verificación name.includes('../') es la mitigación publicada para GHSA-mwxc-m426-3f78 / CVE-2026-32731. Detiene los nombres que contienen la secuencia literal ../. Sin embargo, no canoniza la ruta unida, por lo que ./.. y un simple .. siguen pasando, y no corrigió la falta del listener error. Este informe trata sobre lo que la prueba de subcadena deja desprotegido.

Se utilizó asistencia de IA para la auditoría de código y la redacción. Las líneas exactas de la protección y de WriteStream en packages/import-export/lib/formats/gzip.js en el HEAD indicado se verificaron manualmente, la ruta de importación desde el módulo de página a través de lib/methods/import.js hasta el manejador de entradas fue rastreada manualmente, y los tres comandos de una sola línea de Node se ejecutaron y copiaron de forma textual.

Qué hacer

Actualice @apostrophecms/import-export a la versión 3.7.0 o superior.

Software afectado

Haz clic en una tecnología para ver todas sus vulnerabilidades.

PlataformaPaqueteVersiones vulnerablesCorregida en
Paquete de npm @apostrophecms/import-export < 3.7.0 3.7.0

Referencias

Puntuación CVSS -
Vector CVSS
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N
Debilidad (CWE)
CWE-22
Plataforma
npm
Paquete
@apostrophecms/import-export
Publicada
20.10.2026
Actualizado
20.10.2026 21:35

Fuentes y licencias · Editado o traducido por DockRay; se mantienen los derechos y las licencias de las fuentes.

OSV / GitHub Advisory Database - GHSA-97wv-p4xx-c7mg - CC BY 4.0

Historial de vulnerabilidades: @apostrophecms/import-export

Todas las vulnerabilidades publicadas de este paquete (Paquete de npm), de la más reciente a la más antigua. Muestra si es un fallo aislado o un paquete que hay que vigilar.

Entradas en total
4
Primera → última
03.2026 10.2026
Sin corrección
0
Explotada en ataques
0

Distribución por gravedad

  • Crítica 1
  • Media 3

Divulgaciones por año

  1. 2026: 4
  1. ApostropheCMS import-export tar guard misses './..' because it is a literal '../' substring test esta entrada
    Media CVE-2026-113664 < 3.7.0 3.7.0

Identifica las vulnerabilidades de tu aplicación

Con la integración de DockRay y el inventario de software activado, DockRay compara las versiones notificadas de los componentes con las vulnerabilidades conocidas y avisa de las coincidencias confirmadas. La cobertura depende de la tecnología compatible y de los avisos disponibles.

Conectar tu aplicación
Newsletter DockRay

La seguridad de tus aplicaciones en tu correo

Vulnerabilidades importantes, recomendaciones concretas y consejos prácticos para tus aplicaciones. Elige tus tecnologías y sigue lo que importa para tu proyecto.

  • Un resumen diario de nuevas vulnerabilidades, también las críticas.
  • Avisos sobre actualizaciones importantes.
  • Consejos prácticos sobre seguridad y monitorización de aplicaciones.

Déjalo vacío para recibir avisos de todas las tecnologías.

Suscripción gratuita. Date de baja con un clic en cualquier correo.