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

未关闭 适合新手
#487 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

评估

难度
2/5
预计耗时
1-3 小时
新手友好度
78/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
nodejs, typescript
领域
networking

调研方向

该 bug 位于 src/index.ts 中的 execCommand(第 394-401 行),其中 data 处理器(作用于 channel 和 channel.stderr)对每个数据块分别调用 chunk.toString(options.encoding)。请先运行 issue 中的复现脚本,并结合调用 execCommand 的 exec 包装器阅读这些处理器。完成标准是复现脚本输出长度为 12000 的 equal: true,且 stdout 在拆分位置附近不再包含 U+FFFD。

由索引模型根据 Issue 内容生成。

描述

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/

主要语言
TypeScript
星标
1k
派生
94
PR 合并指标
30 天内没有已合并 PR

环境准备

这个项目没有提供开发容器、Dockerfile 或贡献指南,环境需要你自己搭建:先看它的 README,通用步骤见我们的新手贡献指南。

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

steelbrain/node-ssh 的其他 Issue

查看 steelbrain/node-ssh 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。