execCommand corrupts multi-byte UTF-8 output split across channel data chunks

Abierto Apto para principiantes
#487 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
2/5
Tiempo estimado
1-3 horas
Aptitud para principiantes
78/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
nodejs, typescript
Área
networking

Línea de trabajo

El error está en execCommand en src/index.ts (líneas 394-401), donde los manejadores data de channel y channel.stderr llaman a chunk.toString(options.encoding) en cada fragmento por separado. Empieza ejecutando el script de reproducción del issue y lee esos manejadores junto con el envoltorio exec que llama a execCommand. Está terminado cuando el repro imprime equal: true con longitud 12000 y stdout ya no contiene U+FFFD alrededor del punto de corte.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

execCommand (and exec, which calls it) decodes each SSH channel data chunk on its own with chunk.toString(options.encoding), so any multi-byte UTF-8 character whose bytes straddle two SSH packets (for example at the default 32 KiB channel packet boundary) turns into U+FFFD replacement characters.

Save as repro.mjs and run npm install [email protected] && node repro.mjs:

import ssh2 from "ssh2";
import { NodeSSH } from "node-ssh";

const expected = "日".repeat(12000); // 36,000 UTF-8 bytes (> 32 KiB SSH packet size)
const hostKey = ssh2.utils.generateKeyPairSync("ed25519").private;
const server = new ssh2.Server({ hostKeys: [hostKey] }, (client) => {
  client.on("authentication", (ctx) => ctx.accept());
  client.on("session", (accept) => accept().on("exec", (accept) => {
    const ch = accept();
    ch.write(expected);
    ch.exit(0);
    ch.end();
  }));
});
await new Promise((r) => server.listen(0, "127.0.0.1", r));

const ssh = new NodeSSH();
await ssh.connect({ host: "127.0.0.1", port: server.address().port, username: "u", password: "p" });
const { stdout } = await ssh.execCommand("cat file.txt");
console.log({
  equal: stdout === expected,
  length: stdout.length,
  codePointsAroundSplit: [...stdout.slice(10921, 10925)].map((c) => c.codePointAt(0).toString(16)),
});
ssh.dispose();
server.close();

Actual:

{
  equal: false,
  length: 12001,
  codePointsAroundSplit: [ '65e5', 'fffd', 'fffd', '65e5' ]
}

Expected:

{
  equal: true,
  length: 12000,
  codePointsAroundSplit: [ '65e5', '65e5', '65e5', '65e5' ]
}

In src/index.ts lines 394-401, channel and channel.stderr convert each chunk via chunk.toString(options.encoding) instead of collecting the Buffer chunks for Buffer.concat(chunks).toString(options.encoding) (or using StringDecoder).

Version: [email protected] ([email protected]), also reproduced on main (fca27fa), Node.js v26.13.0.

Credit: https://ucsbplse.github.io/

Lenguaje dominante
TypeScript
Estrellas
1k
Forks
94
Métricas de merge de PR
Sin PR fusionados en 30 d

Preparar el entorno

Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de steelbrain/node-ssh

Todos los issues de steelbrain/node-ssh

Issues similares

Más issues de TypeScript

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.