Compare commits

...
Author SHA1 Message Date
netcatty-bot cb788ef712 fix(#3577): automated AI fix 2026-10-18 06:05:46 +00:00
2 changed files with 38 additions and 10 deletions

No files matched your search

+7 -10
View File
@@ -254,17 +254,14 @@ function startPtyJob(ptyStream, command, options) {
startupTimeoutMs: maxBufferedChars > 0 ? BG_STARTUP_TIMEOUT_MS : timeoutMs,
});
// Deliver the interrupt as terminal data (ETX). This matches manual Ctrl+C
// in the terminal and terminalBridge.interruptSession: the remote line
// discipline turns \x03 into SIGINT. Never send an SSH "signal" channel
// request here — real OpenSSH rejects it ("session_signal_req: session
// signalling requires privilege separation"), which pollutes auth logs and
// makes other host-side tooling report brute-force noise (#3577).
function sendInterrupt(trigger = null) {
let signalSent = false;
let etxWritten = false;
try {
if (typeof ptyStream.signal === "function") {
ptyStream.signal("INT");
signalSent = true;
}
} catch {
// Ignore signal failures and fall back to ETX.
}
try {
if (typeof ptyStream.write === "function") {
ptyStream.write("\x03");
@@ -275,7 +272,7 @@ function startPtyJob(ptyStream, command, options) {
}
jobDiag.emit("send-interrupt", {
trigger,
signalSent,
signalSent: false,
etxWritten,
session: diagSnapshot(),
});
+31
View File
@@ -153,6 +153,37 @@ test("foreground PTY cancellation returns only a bounded tail", async () => {
assert.equal(result.outputTruncated, true);
});
test("PTY cancellation delivers Ctrl+C as terminal data without a channel signal request", async () => {
const calls = [];
class SignalSpyPty extends EventEmitter {
signal(name) {
calls.push(["signal", name]);
}
write(data) {
if (data === "\x03") calls.push(["write", data]);
const marker = markerFromWrite(data);
if (!marker || this.started) return;
this.started = true;
queueMicrotask(() => {
this.emit("data", Buffer.from(`${marker}_S\n`));
});
}
}
const pty = new SignalSpyPty();
const job = startPtyJob(pty, "cancel-signal-request", {
shellKind: "posix",
timeoutMs: 1_000,
expectedPrompt: "$ ",
});
await new Promise((resolve) => setImmediate(resolve));
job.cancel();
pty.emit("data", Buffer.from("$ "));
const result = await job.resultPromise;
assert.equal(result.error, "Cancelled");
assert.deepEqual(calls, [["write", "\x03"]]);
});
test("background PTY jobs preserve output that has no trailing newline", async () => {
const job = startPtyJob(new ShellBackedPty(), "printf 'abc'", {
shellKind: "posix",