Compare commits

...
2 changed files with 62 additions and 4 deletions

No files matched your search

+4 -1
View File
@@ -745,7 +745,10 @@ function createSessionOpsApi(ctx) {
home=$(id -P 2>/dev/null | awk -F: 'NR == 1 { print $9; exit }')
emit_home "$home"
emit_home "$HOME"
exit 1`;
exit 1`.replace(/^[ \t]*#[^\n]*\n/gm, "");
// Omit explanatory comments before quoting this static probe. Dropbear
// closes the entire transport for exec requests over 9000 bytes (#3731).
// Preserve the directory checks above to avoid regressing #3493.
// Capture sshd's pid in the exec'd POSIX sh (`$PPID` is sshd because
// `exec` replaced the login shell). Do not `exec` the inner posixScript:
// that would skip the watchdog cleanup after it.
@@ -127,9 +127,10 @@ test("session cwd probe decodes the marked lsof pathname", async () => {
assert.deepEqual(result, { success: true, cwd: "/srv/中文" });
});
test("lsof cwd fallback accepts only directory records, not diagnostic names (#3237, #3493)", async (t) => {
async function assertLsofCwdFallback(t, remoteSshVersion) {
let script;
const api = makeApi({
remoteSshVersion,
shellPid: "4242",
connRef: { count: 1 },
stream: {},
@@ -140,7 +141,7 @@ test("lsof cwd fallback accepts only directory records, not diagnostic names (#3
},
}, [], {
quoteShellArg(value) {
if (!script && value.includes("read_shell_cwd()")) script = value;
if (value.startsWith("SELF=")) script = value;
return quoteShellArg(value);
},
});
@@ -188,7 +189,12 @@ test("lsof cwd fallback accepts only directory records, not diagnostic names (#3
assert.equal(result.status, expected === null ? 1 : 0, `${output}\n${result.stderr}`);
assert.equal(result.stdout, expected === null ? "" : `NETCATTY_LSOF_CWD=${expected}\n`, output);
}
});
}
for (const remoteSshVersion of ["OpenSSH_9.6", "dropbear"]) {
test(`lsof cwd fallback accepts only directories on ${remoteSshVersion} (#3237, #3493)`,
(t) => assertLsofCwdFallback(t, remoteSshVersion));
}
test("session cwd probe closes a remote command that exceeds its timeout", async () => {
const stream = new EventEmitter();
@@ -897,3 +903,52 @@ test("getSessionPwd skips exec after the CloudLinker banner stamps the runtime c
assert.match(result.error, /extra exec channels/);
assert.equal(execCalls, 0);
});
for (const shellPid of [undefined, "4242"]) {
test(`cwd probe fits Dropbear's limit with shell pid ${shellPid ?? "unknown"} (#3731)`, async () => {
let command = "";
const session = {
shellPid,
remoteSshVersion: "dropbear",
connRef: { count: 1 },
stream: {},
conn: {
exec(nextCommand, callback) {
command = nextCommand;
callback(null, makePwdStream("/owrt/root", "4242"));
},
},
};
const result = await makeApi(session).getSessionPwd(null, { sessionId: "session-1" });
assert.deepEqual(result, { success: true, cwd: "/owrt/root" });
assert.ok(Buffer.byteLength(command, "utf8") <= 9000);
assert.ok(command.includes('readlink "/proc/$1/cwd"'));
assert.ok(command.includes("-Fnt 2>/dev/null"));
assert.ok(command.includes("_rc_test_cwd"), "preserve annotation validation");
assert.ok(!command.includes("# Find the user's interactive shell"));
});
}
test("OpenSSH peers keep the annotation-aware lsof cwd fallback (#3493)", async () => {
let command = "";
const session = {
shellPid: "4242",
remoteSshVersion: "OpenSSH_9.6",
connRef: { count: 1 },
stream: {},
conn: {
exec(nextCommand, callback) {
command = nextCommand;
callback(null, makePwdStream("/srv/app", "4242"));
},
},
};
const api = makeApi(session);
const result = await api.getSessionPwd(null, { sessionId: "session-1" });
assert.deepEqual(result, { success: true, cwd: "/srv/app" });
assert.ok(command.includes("_rc_test_cwd"), "keep the #3493 annotation decode");
assert.ok(command.includes("-Fnt 2>/dev/null"));
});