Files
NetMesh/electron/bridges/sshConnectionPool.test.cjs

1425 lines
47 KiB
JavaScript
Raw Normal View History

const test = require("node:test");
const assert = require("node:assert/strict");
const { EventEmitter } = require("node:events");
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const {
createConnectionRef,
acquireConnectionRef,
releaseConnectionRef,
transferConnectionRef,
consumePendingShellReconnectRisk,
findReusableSession,
createTransport,
borrowTransport,
returnTransport,
discardTransport,
discardAllTransports,
findTransportByEndpoint,
resolveTransportForReuse,
beginTransportDial,
waitForTransportDial,
completeTransportDial,
failTransportDial,
getTransportStats,
setDefaultTransportIdleTtlMs,
getDefaultTransportIdleTtlMs,
buildEndpointKey,
buildConnectionReuseEndpoint,
normalizeEndpoint,
endpointAllowsReuse,
fingerprintAuth,
resetSshTransportRegistryForTests,
markEndpointNoIdlePark,
endpointAllowsIdlePark,
DEFAULT_SSH_TRANSPORT_IDLE_TTL_MS,
DEFAULT_MAX_IDLE_SSH_TRANSPORTS,
LEASE_KINDS,
} = require("./sshConnectionPool.cjs");
function makeConn() {
return {
ended: 0,
_sock: { destroyed: false },
end() { this.ended += 1; },
};
}
function makeChainConn() {
return {
ended: 0,
end() { this.ended += 1; },
};
}
function makeLifecycleConn({ emitCloseFromEnd = false } = {}) {
const conn = new EventEmitter();
conn.ended = 0;
conn._sock = { destroyed: false };
conn.end = () => {
conn.ended += 1;
if (emitCloseFromEnd) conn.emit("close");
};
return conn;
}
/** Product default: TTL 0 parks until quit (never auto-reclaim). */
function useParkForever() {
resetSshTransportRegistryForTests({ defaultIdleTtlMs: 0 });
}
/**
* Short positive TTL with fake timers so tests can fire idle reclaim.
* Returns the timer list.
*/
function useShortTtlTimers(ttlMs = 1) {
const timers = [];
resetSshTransportRegistryForTests({
defaultIdleTtlMs: ttlMs,
setTimeout: (fn, ms) => {
const handle = { fn, ms, cleared: false };
timers.push(handle);
return handle;
},
clearTimeout: (handle) => {
if (handle) handle.cleared = true;
},
});
return timers;
}
function fireIdleTimers(timers) {
for (const t of [...timers]) {
if (!t.cleared && typeof t.fn === "function") t.fn();
}
}
test.beforeEach(() => {
useParkForever();
});
test.afterEach(() => {
resetSshTransportRegistryForTests({ defaultIdleTtlMs: 0 });
});
test("concurrent compatible opens share one in-flight physical dial", async () => {
const endpoint = {
hostId: "host-1",
hostname: "same.example",
username: "alice",
authType: "password",
password: "secret",
};
let physicalDials = 0;
let releaseDial;
const dialGate = new Promise((resolve) => { releaseDial = resolve; });
const open = async () => {
const coordination = beginTransportDial(endpoint, { kind: "shell" });
if (coordination.role === "reuse") return coordination.transport;
if (coordination.role === "join") return waitForTransportDial(coordination);
physicalDials += 1;
await dialGate;
const transport = createTransport({ conn: makeConn(), endpoint });
completeTransportDial(coordination, transport);
return transport;
};
const first = open();
const second = open();
assert.equal(physicalDials, 1, "only the leader may create the physical SSH connection");
releaseDial();
const [left, right] = await Promise.all([first, second]);
assert.equal(left, right);
const sequential = beginTransportDial(endpoint, { kind: "shell" });
assert.equal(sequential.role, "reuse");
assert.equal(sequential.transport, left);
});
test("in-flight dials isolate endpoint, profile, credentials, and ForwardAgent policy", () => {
const base = {
hostId: "host-1",
hostname: "same.example",
username: "alice",
authType: "password",
password: "secret",
agentForwarding: false,
};
const leader = beginTransportDial(base, { kind: "shell" });
assert.equal(leader.role, "leader");
assert.equal(beginTransportDial({ ...base }, { kind: "shell" }).role, "join");
assert.equal(
beginTransportDial({ ...base, hostname: "other.example" }, { kind: "shell" }).role,
"leader",
);
assert.equal(
beginTransportDial({ ...base, hostId: "host-2" }, { kind: "shell" }).role,
"leader",
);
assert.equal(
beginTransportDial({ ...base, password: "rotated" }, { kind: "shell" }).role,
"leader",
);
assert.equal(
beginTransportDial({ ...base, agentForwarding: true }, { kind: "shell" }).role,
"leader",
);
});
test("automatic key/password dials coalesce only when every possible credential matches", () => {
const base = {
hostId: "host-auto",
hostname: "auto.example",
username: "alice",
authType: "auto",
privateKey: "configured-private-key",
password: "fallback-password-v1",
};
const leader = beginTransportDial(base, { kind: "shell" });
assert.equal(leader.role, "leader");
assert.equal(
beginTransportDial({ ...base }, { kind: "channel" }).role,
"join",
"identical automatic opens should still share one pending physical dial",
);
assert.equal(
beginTransportDial({ ...base, password: "fallback-password-v2" }, { kind: "channel" }).role,
"leader",
"a rotated fallback password must not join a dial whose final auth method is not known yet",
);
});
test("channel opens can join a ForwardAgent leader but not the reverse", () => {
const base = {
hostId: "host-1",
hostname: "same.example",
username: "alice",
};
const forwardAgentLeader = beginTransportDial(
{ ...base, agentForwarding: true },
{ kind: "shell" },
);
assert.equal(forwardAgentLeader.role, "leader");
assert.equal(
beginTransportDial({ ...base, agentForwarding: false }, { kind: "channel" }).role,
"join",
);
resetSshTransportRegistryForTests({ defaultIdleTtlMs: 0 });
const noForwardAgentLeader = beginTransportDial(
{ ...base, agentForwarding: false },
{ kind: "channel" },
);
assert.equal(noForwardAgentLeader.role, "leader");
assert.equal(
beginTransportDial({ ...base, agentForwarding: true }, { kind: "shell" }).role,
"leader",
);
});
test("a channel waiter can use a leader transport that negotiated ForwardAgent off", async () => {
const requestedByLeader = {
hostId: "host-agent-downgrade",
hostname: "agent-downgrade.example",
username: "alice",
agentForwarding: true,
};
const leader = beginTransportDial(requestedByLeader, { kind: "shell" });
const channelWaiter = beginTransportDial({
...requestedByLeader,
agentForwarding: false,
}, { kind: "channel" });
assert.equal(channelWaiter.role, "join");
const transport = createTransport({
conn: makeConn(),
endpoint: { ...requestedByLeader, agentForwarding: false },
});
assert.equal(completeTransportDial(leader, transport), true);
assert.equal(await waitForTransportDial(channelWaiter), transport);
await assert.rejects(waitForTransportDial(leader), /incompatible/i);
});
test("cancelled in-flight waiter does not cancel the shared physical dial", async () => {
const endpoint = { hostname: "same.example", username: "alice" };
const leader = beginTransportDial(endpoint, { kind: "shell" });
const joined = beginTransportDial(endpoint, { kind: "shell" });
const controller = new AbortController();
const waiting = waitForTransportDial(joined, { signal: controller.signal });
controller.abort(new Error("caller cancelled"));
await assert.rejects(waiting, /caller cancelled/);
const transport = createTransport({ conn: makeConn(), endpoint });
assert.equal(completeTransportDial(leader, transport), true);
assert.equal(beginTransportDial(endpoint, { kind: "shell" }).transport, transport);
});
test("failed in-flight dial releases the slot so a later open can retry", async () => {
const endpoint = { hostname: "retry.example", username: "alice" };
const leader = beginTransportDial(endpoint, { kind: "shell" });
const joined = beginTransportDial(endpoint, { kind: "shell" });
const waiting = waitForTransportDial(joined);
assert.equal(failTransportDial(leader, new Error("authentication failed")), true);
await assert.rejects(waiting, /authentication failed/);
const retry = beginTransportDial(endpoint, { kind: "shell" });
assert.equal(retry.role, "leader");
});
test("TERM-SSHD last lease ends the transport instead of idle-parking", () => {
const conn = makeConn();
conn._remoteVer = "TERM-SSHD";
const owner = { remoteSshVersion: "TERM-SSHD" };
const connRef = createConnectionRef(owner, conn, []);
assert.equal(connRef.allowIdlePark, false);
assert.equal(endpointAllowsIdlePark(connRef.endpoint, "TERM-SSHD"), false);
const ended = releaseConnectionRef(owner);
assert.equal(ended, true);
assert.equal(conn.ended, 1);
assert.equal(connRef.state, "dead");
assert.equal(connRef.endedReason, "no-idle-park");
});
test("TERM-SSHD last shell with an SFTP lease refuses later shell reuse", () => {
const conn = makeConn();
conn._remoteVer = "TERM-SSHD";
const endpoint = { hostname: "blj.yd.com.cn", username: "test", port: 22 };
const owner = { stream: {}, remoteSshVersion: "TERM-SSHD", _reuseEndpoint: endpoint };
const sftp = { id: "sftp", __sshLeaseKind: "sftp" };
const transport = createConnectionRef(owner, conn, []);
acquireConnectionRef(sftp, transport);
assert.equal(releaseConnectionRef(owner), false);
assert.equal(transport.state, "live");
assert.equal(transport.allowShellReuse, false);
assert.equal(findTransportByEndpoint(transport.endpoint, { kind: "shell" }), null);
assert.equal(findTransportByEndpoint(transport.endpoint, { kind: "channel" }), transport);
});
test("an endpoint denylisted after a dead parked shell does not idle-park later transports", () => {
const endpoint = { hostname: "bastion.example", username: "alice", port: 22 };
markEndpointNoIdlePark(endpoint);
assert.equal(endpointAllowsIdlePark(endpoint, "OpenSSH_9.0"), false);
const conn = makeConn();
conn._remoteVer = "OpenSSH_9.0";
const transport = createTransport({ conn, endpoint });
assert.equal(transport.allowIdlePark, false);
const holder = {};
borrowTransport(transport, { kind: "shell", holder });
assert.equal(releaseConnectionRef(holder), true);
assert.equal(transport.state, "dead");
assert.equal(conn.ended, 1);
});
test("releaseConnectionRef parks on last channel when TTL is 0 (until quit)", () => {
const conn = makeConn();
const chain = [makeChainConn(), makeChainConn()];
const owner = {};
const reused = {};
const connRef = createConnectionRef(owner, conn, chain);
assert.equal(connRef.count, 1);
acquireConnectionRef(reused, connRef);
assert.equal(connRef.count, 2);
assert.equal(reused.connRef, connRef);
let ended = releaseConnectionRef(reused);
assert.equal(ended, false);
assert.equal(conn.ended, 0);
assert.equal(reused.connRef, null);
// Last lease parks (TTL 0 = never auto-end).
ended = releaseConnectionRef(owner);
assert.equal(ended, false);
assert.equal(conn.ended, 0);
assert.equal(connRef.state, "idle");
assert.equal(owner.connRef, null);
assert.equal(discardTransport(connRef), true);
assert.equal(conn.ended, 1);
assert.equal(chain[0].ended, 1);
assert.equal(chain[1].ended, 1);
});
test("releaseConnectionRef keeps siblings alive when the owner closes first", () => {
const timers = useShortTtlTimers(1);
const conn = makeConn();
const owner = {};
const reused = {};
const connRef = createConnectionRef(owner, conn, []);
acquireConnectionRef(reused, connRef);
assert.equal(releaseConnectionRef(owner), false);
assert.equal(conn.ended, 0, "connection must survive for the remaining copy");
// Last holder parks; fire TTL to end.
assert.equal(releaseConnectionRef(reused), false);
assert.equal(connRef.state, "idle");
fireIdleTimers(timers);
assert.equal(conn.ended, 1);
});
test("releaseConnectionRef is idempotent per session", () => {
const conn = makeConn();
const owner = {};
const connRef = createConnectionRef(owner, conn, []);
acquireConnectionRef({}, connRef); // bump count to 2 so a double release can't reach 0 by itself
assert.equal(releaseConnectionRef(owner), false);
assert.equal(releaseConnectionRef(owner), false);
assert.equal(connRef.count, 1);
assert.equal(conn.ended, 0);
});
test("releaseConnectionRef on a session without a descriptor is a safe no-op", () => {
assert.equal(releaseConnectionRef({}), false);
assert.equal(releaseConnectionRef(null), false);
assert.equal(releaseConnectionRef(undefined), false);
});
test("last shell close marks reconnect risk while an SFTP lease keeps the transport live", () => {
const owner = { id: "shell", stream: {} };
const sftp = { id: "sftp", __sshLeaseKind: "sftp" };
const transport = createConnectionRef(owner, makeConn(), []);
acquireConnectionRef(sftp, transport);
assert.equal(releaseConnectionRef(owner), false);
assert.deepEqual(consumePendingShellReconnectRisk(transport), {
oldShellPids: [],
hasUnknownOldShell: true,
});
assert.equal(consumePendingShellReconnectRisk(transport), false);
});
test("single-channel connection parks on release when TTL is 0", () => {
const conn = makeConn();
const chain = [makeChainConn()];
const owner = {};
const transport = createConnectionRef(owner, conn, chain);
assert.equal(releaseConnectionRef(owner), false);
assert.equal(transport.state, "idle");
assert.equal(conn.ended, 0);
assert.equal(discardTransport(transport), true);
assert.equal(conn.ended, 1);
assert.equal(chain[0].ended, 1);
});
test("findReusableSession returns a live interactive SSH shell session", () => {
const sessions = new Map();
const source = {
conn: { _sock: { destroyed: false } },
stream: {},
connRef: { count: 1, state: "live" },
};
sessions.set("src", source);
assert.equal(findReusableSession(sessions, "src"), source);
});
test("findReusableSession rejects sessions missing a usable connection", () => {
const sessions = new Map();
sessions.set("no-stream", { conn: {}, connRef: { count: 1, state: "live" } });
assert.equal(findReusableSession(sessions, "no-stream"), null);
sessions.set("no-ref", { conn: {}, stream: {} });
assert.equal(findReusableSession(sessions, "no-ref"), null);
sessions.set("no-conn", { stream: {}, connRef: { count: 1, state: "live" } });
assert.equal(findReusableSession(sessions, "no-conn"), null);
sessions.set("dead", {
conn: { _sock: { destroyed: true } },
stream: {},
connRef: { count: 1, state: "live" },
});
assert.equal(findReusableSession(sessions, "dead"), null);
});
test("findReusableSession handles missing inputs gracefully", () => {
assert.equal(findReusableSession(null, "x"), null);
assert.equal(findReusableSession(new Map(), ""), null);
assert.equal(findReusableSession(new Map(), "absent"), null);
});
test("findReusableSession enforces an exact target endpoint match", () => {
const sessions = new Map();
const source = {
conn: { _sock: { destroyed: false } },
stream: {},
connRef: { count: 1, state: "live" },
_reuseEndpoint: { hostname: "10.0.0.1", port: 22, username: "alice" },
};
sessions.set("src", source);
assert.equal(
findReusableSession(sessions, "src", { hostname: "10.0.0.1", port: 22, username: "alice" }),
source,
);
assert.equal(
findReusableSession(sessions, "src", { hostname: "10.0.0.1", username: "alice" }),
source,
);
assert.equal(findReusableSession(sessions, "src", { hostname: "10.0.0.2", port: 22, username: "alice" }), null);
assert.equal(findReusableSession(sessions, "src", { hostname: "10.0.0.1", port: 2222, username: "alice" }), null);
assert.equal(findReusableSession(sessions, "src", { hostname: "10.0.0.1", port: 22, username: "bob" }), null);
sessions.set("root-src", {
conn: { _sock: { destroyed: false } },
stream: {},
connRef: { count: 1, state: "live" },
_reuseEndpoint: { hostname: "10.0.0.9", port: 22, username: "root" },
});
assert.ok(findReusableSession(sessions, "root-src", { hostname: "10.0.0.9" }));
});
test("findReusableSession refuses reuse when the source has no recorded endpoint", () => {
const sessions = new Map();
sessions.set("src", {
conn: { _sock: { destroyed: false } },
stream: {},
connRef: { count: 1, state: "live" },
});
assert.equal(findReusableSession(sessions, "src", { hostname: "10.0.0.1" }), null);
assert.ok(findReusableSession(sessions, "src"));
});
// ---------------------------------------------------------------------------
// Transport registry + idle park
// ---------------------------------------------------------------------------
test("default idle TTL constant is 5 minutes", () => {
assert.equal(DEFAULT_SSH_TRANSPORT_IDLE_TTL_MS, 5 * 60_000);
assert.equal(DEFAULT_MAX_IDLE_SSH_TRANSPORTS, 128);
});
test("TTL-zero idle pool enforces a hard global limit without ending active transports", () => {
resetSshTransportRegistryForTests({
defaultIdleTtlMs: 0,
maxIdleTransports: 3,
});
const activeConn = makeConn();
const active = createTransport({
conn: activeConn,
endpoint: { hostname: "active.example", username: "root" },
});
borrowTransport(active, { holder: {} });
const parked = [];
for (let index = 0; index < 5; index += 1) {
const conn = makeConn();
const holder = {};
const transport = createTransport({
conn,
endpoint: { hostname: `idle-${index}.example`, username: "root" },
});
borrowTransport(transport, { holder });
returnTransport(holder);
parked.push({ conn, transport });
}
assert.deepEqual(getTransportStats(), {
transports: 4,
pendingDials: 0,
live: 1,
idle: 3,
leases: 1,
defaultIdleTtlMs: 0,
});
assert.equal(active.state, "live");
assert.equal(activeConn.ended, 0, "a leased transport must never be evicted");
assert.deepEqual(parked.map(({ conn }) => conn.ended), [1, 1, 0, 0, 0]);
assert.deepEqual(parked.map(({ transport }) => transport.state), [
"dead",
"dead",
"idle",
"idle",
"idle",
]);
});
test("idle endpoint hits refresh LRU order before the next eviction", () => {
resetSshTransportRegistryForTests({
defaultIdleTtlMs: 0,
maxIdleTransports: 2,
});
const park = (hostname) => {
const endpoint = { hostname, username: "root" };
const conn = makeConn();
const holder = {};
const transport = createTransport({ conn, endpoint });
borrowTransport(transport, { holder });
returnTransport(holder);
return { conn, endpoint, transport };
};
const first = park("first.example");
const second = park("second.example");
assert.equal(findTransportByEndpoint(first.endpoint), first.transport);
const third = park("third.example");
assert.equal(first.conn.ended, 0);
assert.equal(second.conn.ended, 1, "the untouched oldest idle transport is evicted");
assert.equal(third.conn.ended, 0);
assert.equal(findTransportByEndpoint(first.endpoint), first.transport);
assert.equal(findTransportByEndpoint(second.endpoint), null);
assert.equal(findTransportByEndpoint(third.endpoint), third.transport);
});
test("borrow and return make a previously idle transport the newest LRU entry", () => {
resetSshTransportRegistryForTests({
defaultIdleTtlMs: 0,
maxIdleTransports: 2,
});
const park = (hostname) => {
const conn = makeConn();
const holder = {};
const transport = createTransport({
conn,
endpoint: { hostname, username: "root" },
});
borrowTransport(transport, { holder });
returnTransport(holder);
return { conn, transport };
};
const first = park("first.example");
const second = park("second.example");
const borrowed = {};
borrowTransport(first.transport, { holder: borrowed });
const third = park("third.example");
assert.equal(second.conn.ended, 0, "capacity ignores the active borrowed transport");
returnTransport(borrowed);
assert.equal(first.conn.ended, 0);
assert.equal(second.conn.ended, 1, "returning the borrowed transport evicts the oldest idle one");
assert.equal(third.conn.ended, 0);
assert.equal(first.transport.state, "idle");
});
test("buildEndpointKey normalizes port and username defaults", () => {
assert.equal(
buildEndpointKey({ hostname: "a.example", port: 22, username: "root" }),
buildEndpointKey({ hostname: "a.example" }),
);
assert.notEqual(
buildEndpointKey({ hostname: "a.example", username: "alice" }),
buildEndpointKey({ hostname: "a.example", username: "bob" }),
);
assert.notEqual(
buildEndpointKey({ hostname: "a.example", jumpFingerprint: "bastion" }),
buildEndpointKey({ hostname: "a.example", jumpFingerprint: "other" }),
);
});
test("buildEndpointKey distinguishes jump host chains", () => {
assert.notEqual(
buildEndpointKey({
hostname: "target",
jumpHosts: [{ hostname: "bastion-a", port: 22, username: "j" }],
}),
buildEndpointKey({
hostname: "target",
jumpHosts: [{ hostname: "bastion-b", port: 22, username: "j" }],
}),
);
});
test("buildEndpointKey scopes vault hostId so different profiles never share", () => {
assert.notEqual(
buildEndpointKey({ hostId: "host-a", hostname: "same.example", username: "root" }),
buildEndpointKey({ hostId: "host-b", hostname: "same.example", username: "root" }),
);
// Missing hostId still indexes under a distinct profile slot ("-").
assert.notEqual(
buildEndpointKey({ hostname: "same.example", username: "root" }),
buildEndpointKey({ hostId: "host-a", hostname: "same.example", username: "root" }),
);
});
test("fingerprintAuth changes when credential material rotates under same keyId", () => {
const keyBase = {
hostname: "h.example",
username: "alice",
authType: "key",
keyId: "key-1",
};
assert.notEqual(
fingerprintAuth({ ...keyBase, privateKey: "-----BEGIN OLD-----" }),
fingerprintAuth({ ...keyBase, privateKey: "-----BEGIN NEW-----" }),
);
assert.notEqual(
fingerprintAuth({ authType: "password", password: "secret-a" }),
fingerprintAuth({ authType: "password", password: "secret-b" }),
);
assert.notEqual(
fingerprintAuth({ authType: "certificate", certificate: "old-cert", privateKey: "k" }),
fingerprintAuth({ authType: "certificate", certificate: "new-cert", privateKey: "k" }),
);
// Same material is stable.
assert.equal(
fingerprintAuth({ ...keyBase, privateKey: "same" }),
fingerprintAuth({ ...keyBase, privateKey: "same" }),
);
// Automatic auth fingerprints every credential that may be selected. This
// remains stable for an unchanged key/password policy and invalidates when
// either the key or its fallback password rotates.
const autoWithPub = {
authType: "auto",
keyId: "k1",
publicKey: "ssh-ed25519 AAAA",
privateKey: "-----BEGIN-----",
password: "pw",
};
assert.equal(
fingerprintAuth(autoWithPub),
fingerprintAuth({ ...autoWithPub }),
);
assert.notEqual(
fingerprintAuth(autoWithPub),
fingerprintAuth({ ...autoWithPub, password: "rotated-password" }),
);
// password-only auto: password rotation invalidates.
assert.notEqual(
fingerprintAuth({ authType: "auto", password: "old" }),
fingerprintAuth({ authType: "auto", password: "new" }),
);
});
test("forwarding socket changes invalidate shell reuse without blocking channel reuse", () => {
const base = {
hostname: "h.example",
username: "alice",
authType: "password",
password: "secret",
agentForwarding: true,
};
const apple = { ...base, forwardingAgentSocket: "/tmp/apple-agent.sock" };
const bitwarden = {
...base,
forwardingAgentSocket: "/Users/alice/.bitwarden-ssh-agent.sock",
};
assert.equal(
fingerprintAuth(apple),
fingerprintAuth(bitwarden),
);
assert.equal(buildEndpointKey(apple), buildEndpointKey(bitwarden));
assert.equal(endpointAllowsReuse(bitwarden, apple, "shell"), false);
assert.equal(endpointAllowsReuse(
{ ...base, agentForwarding: false },
apple,
"channel",
), true);
resetSshTransportRegistryForTests({ defaultIdleTtlMs: 0 });
const transport = createTransport({ conn: makeConn(), endpoint: apple });
assert.equal(
findTransportByEndpoint({ ...base, agentForwarding: false }),
transport,
);
assert.equal(findTransportByEndpoint(bitwarden, { kind: "shell" }), null);
resetSshTransportRegistryForTests({ defaultIdleTtlMs: 0 });
});
test("automatic key success reuses an unchanged endpoint without exposing credentials", () => {
const endpoint = buildConnectionReuseEndpoint({
hostId: "auto-key-profile",
hostname: "auto-key.example",
username: "alice",
authMethod: "auto",
privateKey: "private-key-secret",
publicKey: "ssh-ed25519 public-key-material",
password: "unused-fallback-secret",
});
const endpointKey = buildEndpointKey(endpoint);
const transport = createTransport({ conn: makeConn(), endpoint });
assert.equal(findTransportByEndpoint({ ...endpoint }), transport);
assert.equal(beginTransportDial({ ...endpoint }, { kind: "shell" }).role, "reuse");
for (const secret of ["private-key-secret", "public-key-material", "unused-fallback-secret"]) {
assert.equal(endpointKey.includes(secret), false);
}
});
test("TTL-zero automatic password fallback is not reused after password rotation", () => {
const firstEndpoint = buildConnectionReuseEndpoint({
hostId: "auto-fallback-profile",
hostname: "auto-fallback.example",
username: "alice",
authMethod: "auto",
privateKey: "rejected-private-key",
password: "accepted-password-v1",
});
const rotatedEndpoint = buildConnectionReuseEndpoint({
hostId: "auto-fallback-profile",
hostname: "auto-fallback.example",
username: "alice",
authMethod: "auto",
privateKey: "rejected-private-key",
password: "accepted-password-v2",
});
const conn = makeConn();
const holder = {};
const transport = createTransport({ conn, endpoint: firstEndpoint });
borrowTransport(transport, { kind: "shell", holder });
returnTransport(holder);
assert.equal(transport.state, "idle");
assert.equal(conn.ended, 0, "TTL zero intentionally keeps the old transport parked");
assert.equal(findTransportByEndpoint(firstEndpoint), transport);
assert.equal(findTransportByEndpoint(rotatedEndpoint), null);
assert.equal(beginTransportDial(rotatedEndpoint, { kind: "shell" }).role, "leader");
});
test("endpoint key securely covers jump, proxy, agent, and SSH security policy", () => {
const base = buildConnectionReuseEndpoint({
hostId: "target-id",
hostname: "target.example",
username: "alice",
authMethod: "key",
privateKey: "target-private-key",
passphrase: "target-passphrase",
useSshAgent: true,
identityAgent: "/run/user/1000/agent-a.sock",
identitiesOnly: true,
agentPublicKeys: ["ssh-ed25519 TARGET-A"],
verifyHostKeys: true,
legacyAlgorithms: false,
skipEcdsaHostKey: false,
algorithmOverrides: { kex: ["curve25519-sha256"] },
proxy: {
type: "socks5",
host: "proxy.example",
port: 1080,
identityId: "proxy-identity-a",
username: "proxy-user",
password: "proxy-secret-a",
},
jumpHosts: [{
hostId: "jump-id",
hostname: "jump.example",
username: "jump-user",
authMethod: "password",
password: "jump-secret-a",
requiresMfa: true,
verifyHostKeys: true,
legacyAlgorithms: false,
skipEcdsaHostKey: false,
algorithmOverrides: { serverHostKey: ["ssh-ed25519"] },
proxy: {
type: "http",
host: "jump-proxy.example",
port: 8080,
username: "jump-proxy-user",
password: "jump-proxy-secret-a",
},
}],
});
const baseKey = buildEndpointKey(base);
const variants = [
{ ...base, identityAgent: "/run/user/1000/agent-b.sock" },
{ ...base, identitiesOnly: false },
{ ...base, agentPublicKeys: ["ssh-ed25519 TARGET-B"] },
{ ...base, verifyHostKeys: false },
{ ...base, algorithmOverrides: { kex: ["diffie-hellman-group14-sha256"] } },
{ ...base, proxy: { ...base.proxy, password: "proxy-secret-b" } },
{ ...base, proxy: { ...base.proxy, identityId: "proxy-identity-b" } },
{ ...base, jumpHosts: [{ ...base.jumpHosts[0], password: "jump-secret-b" }] },
{ ...base, jumpHosts: [{ ...base.jumpHosts[0], verifyHostKeys: false }] },
{ ...base, jumpHosts: [{
...base.jumpHosts[0],
proxy: { ...base.jumpHosts[0].proxy, password: "jump-proxy-secret-b" },
}] },
];
for (const variant of variants) {
assert.notEqual(buildEndpointKey(variant), baseKey);
}
for (const secret of [
"target-private-key",
"target-passphrase",
"proxy-secret-a",
"jump-secret-a",
"jump-proxy-secret-a",
]) {
assert.equal(baseKey.includes(secret), false, `endpoint key must not expose ${secret}`);
}
});
test("endpoint key changes when known-host trust content changes", () => {
const base = {
hostId: "known-host-profile",
hostname: "known-host.example",
port: 22,
username: "alice",
authMethod: "password",
password: "secret",
verifyHostKeys: true,
};
const first = buildEndpointKey(buildConnectionReuseEndpoint({
...base,
knownHosts: [{ hostname: "known-host.example", port: 22, publicKey: "ssh-ed25519 AAAA_FIRST" }],
}));
const rotated = buildEndpointKey(buildConnectionReuseEndpoint({
...base,
knownHosts: [{ hostname: "known-host.example", port: 22, publicKey: "ssh-ed25519 AAAA_ROTATED" }],
}));
assert.notEqual(rotated, first);
const jumpFirst = buildEndpointKey(buildConnectionReuseEndpoint({
...base,
jumpHosts: [{ hostname: "bastion.example", port: 2222 }],
knownHosts: [{
hostname: "[bastion.example]:2222",
port: 2222,
publicKey: "ssh-ed25519 AAAA_JUMP_FIRST",
}],
}));
const jumpRotatedEndpoint = buildConnectionReuseEndpoint({
...base,
jumpHosts: [{ hostname: "bastion.example", port: 2222 }],
knownHosts: [{
hostname: "[bastion.example]:2222",
port: 2222,
publicKey: "ssh-ed25519 AAAA_JUMP_ROTATED",
}],
});
assert.notEqual(buildEndpointKey(jumpRotatedEndpoint), jumpFirst);
assert.equal(
JSON.stringify(normalizeEndpoint(jumpRotatedEndpoint)).includes("AAAA_JUMP_ROTATED"),
false,
);
});
test("endpoint key changes when an identity file changes in place", (t) => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "netcatty-pool-key-"));
t.after(() => fs.rmSync(dir, { recursive: true, force: true }));
const identityPath = path.join(dir, "id_ed25519");
fs.writeFileSync(identityPath, "first-private-key");
const endpoint = buildConnectionReuseEndpoint({
hostname: "identity-file.example",
username: "alice",
authMethod: "key",
identityFilePaths: [identityPath],
});
const first = buildEndpointKey(endpoint);
fs.writeFileSync(identityPath, "rotated-private-key");
const second = buildEndpointKey(endpoint);
assert.notEqual(second, first);
assert.equal(first.includes("first-private-key"), false);
assert.equal(second.includes("rotated-private-key"), false);
});
test("auto-auth endpoint changes when the inline private key rotates", () => {
const base = {
hostId: "inline-key-profile",
hostname: "inline-key.example",
username: "alice",
authMethod: "auto",
publicKey: "stale-public-key-metadata",
};
const first = buildEndpointKey(buildConnectionReuseEndpoint({
...base,
privateKey: "first-private-key",
}));
const rotated = buildEndpointKey(buildConnectionReuseEndpoint({
...base,
privateKey: "rotated-private-key",
}));
assert.notEqual(rotated, first);
assert.equal(first.includes("first-private-key"), false);
assert.equal(rotated.includes("rotated-private-key"), false);
});
test("Terminal and SFTP aliases build the same reuse endpoint semantics", () => {
const options = {
hostId: "shared-profile",
hostname: "shared.example",
username: "alice",
authMethod: "certificate",
keyId: "key-1",
privateKey: "private",
publicKey: "public",
certificate: "certificate",
passphrase: "passphrase",
identityFilePaths: ["/keys/id"],
useSshAgent: true,
identityAgent: "SSH_AUTH_SOCK",
identitiesOnly: true,
addKeysToAgent: "confirm",
useKeychain: true,
agentPublicKeys: ["ssh-ed25519 SELECTED"],
verifyHostKeys: true,
legacyAlgorithms: true,
skipEcdsaHostKey: true,
algorithmOverrides: { hmac: ["hmac-sha2-256"] },
jumpHosts: [{ hostname: "jump", password: "jump-password" }],
proxy: { type: "http", host: "proxy", port: 8080, password: "proxy-password" },
};
assert.equal(
buildEndpointKey(buildConnectionReuseEndpoint(options)),
buildEndpointKey(buildConnectionReuseEndpoint({
...options,
authType: options.authMethod,
authMethod: undefined,
})),
);
});
test("connection reuse identity changes with the effective keepalive policy", () => {
const base = { hostname: "keepalive.example", username: "root", port: 22 };
const enabled = buildConnectionReuseEndpoint({
...base,
keepaliveInterval: 30,
keepaliveCountMax: 10,
});
const disabled = buildConnectionReuseEndpoint({
...base,
keepaliveInterval: 0,
keepaliveCountMax: 0,
});
assert.notEqual(buildEndpointKey(enabled), buildEndpointKey(disabled));
assert.equal(endpointAllowsReuse(enabled, disabled, "shell"), false);
});
test("connection endpoint can record negotiated agent forwarding instead of the request", () => {
const endpoint = buildConnectionReuseEndpoint(
{ hostname: "agent.example", username: "root", agentForwarding: true },
{ agentForwarding: false },
);
assert.equal(normalizeEndpoint(endpoint).agentForwarding, false);
});
test("channel reuse can borrow a normal SSH transport for sudo SFTP", () => {
const base = { hostname: "sudo.example", username: "root", port: 22 };
const normalShell = buildConnectionReuseEndpoint(base, { sftpSudo: false });
const sudoSftp = buildConnectionReuseEndpoint(base, { sftpSudo: true });
assert.notEqual(buildEndpointKey(normalShell), buildEndpointKey(sudoSftp));
assert.equal(endpointAllowsReuse(sudoSftp, normalShell, "channel"), true);
assert.equal(endpointAllowsReuse(sudoSftp, normalShell, "shell"), false);
});
test("endpointAllowsReuse: shell exact vs channel asymmetric for agentForwarding", () => {
const base = { hostname: "a.example", username: "root", port: 22 };
// Channel (default): request needs ForwardAgent, existing never enabled it -> reject.
assert.equal(
endpointAllowsReuse({ ...base, agentForwarding: true }, { ...base, agentForwarding: false }),
false,
);
// Channel: request does not need ForwardAgent; existing with it is fine (SFTP/PF).
assert.equal(
endpointAllowsReuse({ ...base, agentForwarding: false }, { ...base, agentForwarding: true }),
true,
);
// Shell: disabling ForwardAgent must not reattach to a warm agent-forward conn.
assert.equal(
endpointAllowsReuse(
{ ...base, agentForwarding: false },
{ ...base, agentForwarding: true },
"shell",
),
false,
);
assert.equal(
endpointAllowsReuse(
{ ...base, agentForwarding: true },
{ ...base, agentForwarding: true },
"shell",
),
true,
);
// agentForwarding does not change the shared endpoint key (park index stays stable).
assert.equal(
buildEndpointKey({ ...base, agentForwarding: true }),
buildEndpointKey({ ...base, agentForwarding: false }),
);
});
test("findTransportByEndpoint shell kind refuses mismatched agentForwarding", () => {
resetSshTransportRegistryForTests({ defaultIdleTtlMs: 0 });
const holder = { id: "s1" };
const withFwd = createTransport({
conn: makeConn(),
endpoint: { hostname: "fwd.example", username: "root", agentForwarding: true },
});
borrowTransport(withFwd, { kind: "shell", holder, leaseId: "shell:s1" });
returnTransport(holder); // park idle forever
// Shell open after user disables ForwardAgent must not reuse.
assert.equal(
findTransportByEndpoint(
{ hostname: "fwd.example", username: "root", agentForwarding: false },
{ kind: "shell" },
),
null,
);
// SFTP/PF channel reuse may still borrow the ForwardAgent transport.
assert.ok(
findTransportByEndpoint({ hostname: "fwd.example", username: "root", agentForwarding: false }),
);
assert.ok(
findTransportByEndpoint(
{ hostname: "fwd.example", username: "root", agentForwarding: true },
{ kind: "shell" },
),
);
discardTransport(withFwd);
});
test("last return parks with positive TTL then ends when timer fires", () => {
const timers = useShortTtlTimers(60_000);
const conn = makeConn();
const holder = { id: "shell-1" };
const transport = createTransport({
conn,
endpoint: { hostname: "10.0.0.1", username: "alice" },
});
borrowTransport(transport, { kind: LEASE_KINDS.shell, holder });
const result = returnTransport(holder);
assert.equal(result.released, true);
assert.equal(result.ended, false);
assert.equal(result.idle, true);
assert.equal(conn.ended, 0);
assert.equal(transport.state, "idle");
assert.equal(timers.length, 1);
assert.equal(timers[0].ms, 60_000);
timers[0].fn();
assert.equal(conn.ended, 1);
assert.equal(transport.state, "dead");
});
test("idle reclaim timer does not keep the app process alive", () => {
let unrefCalls = 0;
resetSshTransportRegistryForTests({
defaultIdleTtlMs: 60_000,
setTimeout: () => ({
unref() { unrefCalls += 1; },
}),
clearTimeout: () => {},
});
const transport = createTransport({ conn: makeConn() });
const holder = {};
borrowTransport(transport, { holder });
returnTransport(holder);
assert.equal(unrefCalls, 1);
});
test("TTL 0 parks forever without scheduling a timer", () => {
const timers = [];
resetSshTransportRegistryForTests({
defaultIdleTtlMs: 0,
setTimeout: (fn, ms) => {
const handle = { fn, ms, cleared: false };
timers.push(handle);
return handle;
},
clearTimeout: (handle) => {
if (handle) handle.cleared = true;
},
});
const conn = makeConn();
const transport = createTransport({ conn, endpoint: { hostname: "forever.example" } });
const holder = {};
borrowTransport(transport, { holder });
const result = returnTransport(holder);
assert.equal(result.idle, true);
assert.equal(result.ended, false);
assert.equal(timers.length, 0, "never-reclaim must not schedule idle end");
assert.equal(conn.ended, 0);
assert.equal(transport.state, "idle");
assert.ok(findTransportByEndpoint({ hostname: "forever.example" }));
});
test("borrow while idle cancels park and reuses the same conn", () => {
const timers = useShortTtlTimers(60_000);
const conn = makeConn();
const endpoint = { hostname: "10.0.0.2", port: 22, username: "root" };
const transport = createTransport({ conn, endpoint });
const first = {};
borrowTransport(transport, { kind: LEASE_KINDS.shell, holder: first });
returnTransport(first);
assert.equal(transport.state, "idle");
assert.equal(timers[0].cleared, false);
const found = findTransportByEndpoint(endpoint);
assert.equal(found, transport);
const second = {};
borrowTransport(found, { kind: LEASE_KINDS.sftp, holder: second, leaseId: "sftp:panel-1" });
assert.equal(transport.state, "live");
assert.equal(timers[0].cleared, true);
assert.equal(conn.ended, 0);
assert.equal(transport.count, 1);
assert.equal(second.connRef, transport);
});
test("findTransportByEndpoint prefers live transport over idle", () => {
resetSshTransportRegistryForTests({ defaultIdleTtlMs: 60_000 });
const endpoint = { hostname: "shared.example", username: "u" };
const idleConn = makeConn();
const idleTransport = createTransport({ conn: idleConn, endpoint });
const idleHolder = {};
borrowTransport(idleTransport, { holder: idleHolder });
returnTransport(idleHolder);
assert.equal(idleTransport.state, "idle");
const liveConn = makeConn();
const liveTransport = createTransport({ conn: liveConn, endpoint });
borrowTransport(liveTransport, { holder: {} });
assert.equal(findTransportByEndpoint(endpoint), liveTransport);
});
test("sftp and shell leases share one transport until both return", () => {
const timers = useShortTtlTimers(1);
const conn = makeConn();
const session = { id: "term-1", _reuseEndpoint: { hostname: "h", port: 22, username: "root" } };
const transport = createConnectionRef(session, conn, []);
const sftpHolder = { id: "sftp-1", __sshLeaseKind: LEASE_KINDS.sftp };
acquireConnectionRef(sftpHolder, transport);
assert.equal(transport.count, 2);
assert.equal(releaseConnectionRef(session), false);
assert.equal(conn.ended, 0);
assert.equal(releaseConnectionRef(sftpHolder), false);
assert.equal(transport.state, "idle");
fireIdleTimers(timers);
assert.equal(conn.ended, 1);
});
test("resolveTransportForReuse finds idle transport by endpoint without a session", () => {
const timers = useShortTtlTimers(30_000);
const conn = makeConn();
const endpoint = { hostname: "parked.example", username: "ops" };
const transport = createTransport({ conn, endpoint });
const holder = {};
borrowTransport(transport, { holder });
returnTransport(holder);
const resolved = resolveTransportForReuse({ endpoint });
assert.equal(resolved, transport);
assert.equal(resolved.state, "idle");
assert.ok(timers.length >= 1);
});
test("findTransportByEndpoint ends transports whose socket is destroyed", () => {
resetSshTransportRegistryForTests({ defaultIdleTtlMs: 0 });
const endpoint = { hostId: "h1", hostname: "dead.example", username: "root" };
const conn = makeConn();
const transport = createTransport({ conn, endpoint });
const holder = {};
borrowTransport(transport, { holder });
returnTransport(holder);
assert.equal(transport.state, "idle");
conn._sock.destroyed = true;
assert.equal(findTransportByEndpoint(endpoint), null);
assert.equal(conn.ended, 1);
assert.equal(getTransportStats().transports, 0);
});
test("discardTransport force-ends and unregisters", () => {
resetSshTransportRegistryForTests({ defaultIdleTtlMs: 60_000 });
const conn = makeConn();
const endpoint = { hostname: "x.example" };
const transport = createTransport({ conn, endpoint });
borrowTransport(transport, { holder: {} });
assert.equal(discardTransport(transport), true);
assert.equal(conn.ended, 1);
assert.equal(findTransportByEndpoint(endpoint), null);
assert.equal(getTransportStats().transports, 0);
});
test("discardAllTransports clears the registry", () => {
resetSshTransportRegistryForTests({ defaultIdleTtlMs: 60_000 });
const a = createTransport({ conn: makeConn(), endpoint: { hostname: "a" } });
const b = createTransport({ conn: makeConn(), endpoint: { hostname: "b" } });
borrowTransport(a, { holder: {} });
borrowTransport(b, { holder: {} });
assert.equal(discardAllTransports(), 2);
assert.equal(getTransportStats().transports, 0);
});
test("active transport error unregisters leases and closes its jump chain once", () => {
useParkForever();
const conn = makeLifecycleConn({ emitCloseFromEnd: true });
const chain = makeChainConn();
const holder = {};
const transport = createTransport({
conn,
chainConnections: [chain],
endpoint: { hostname: "active-error.example", username: "alice" },
});
borrowTransport(transport, { holder, kind: "sftp" });
conn.emit("error", new Error("remote transport failed"));
assert.equal(getTransportStats().transports, 0);
assert.equal(getTransportStats().leases, 0);
assert.equal(holder.connRef, null);
assert.equal(holder._sshTransportLeaseId, null);
assert.equal(conn.ended, 1);
assert.equal(chain.ended, 1);
assert.equal(transport.state, "dead");
});
test("remote close unregisters a TTL-zero idle transport and its jump chain once", () => {
useParkForever();
const conn = makeLifecycleConn();
const chain = makeChainConn();
const holder = {};
const transport = createTransport({
conn,
chainConnections: [chain],
endpoint: { hostname: "idle-close.example", username: "alice" },
});
borrowTransport(transport, { holder, kind: "shell" });
returnTransport(holder);
assert.equal(getTransportStats().idle, 1);
conn._sock.destroyed = true;
conn.emit("close");
conn.emit("close");
assert.equal(getTransportStats().transports, 0);
assert.equal(getTransportStats().idle, 0);
assert.equal(conn.ended, 0, "a remote close must not call end on the closed socket again");
assert.equal(chain.ended, 1);
assert.equal(transport.state, "dead");
});
test("setDefaultTransportIdleTtlMs updates default and reschedules idle transports", () => {
const timers = [];
let now = 10_000;
resetSshTransportRegistryForTests({
defaultIdleTtlMs: 60_000,
now: () => now,
setTimeout: (fn, ms) => {
const handle = { fn, ms, cleared: false };
timers.push(handle);
return handle;
},
clearTimeout: (handle) => {
if (handle) handle.cleared = true;
},
});
const conn = makeConn();
const transport = createTransport({ conn, endpoint: { hostname: "resched.example" } });
const holder = {};
borrowTransport(transport, { holder });
returnTransport(holder);
assert.equal(transport.state, "idle");
assert.equal(timers[0].ms, 60_000);
now += 250;
setDefaultTransportIdleTtlMs(5_000);
assert.equal(getDefaultTransportIdleTtlMs(), 5_000);
assert.equal(timers[0].cleared, true, "old idle timer must be cancelled");
const last = timers[timers.length - 1];
assert.equal(last.ms, 4_750, "reschedule must preserve elapsed idle time");
assert.equal(transport.idleTtlMs, 5_000);
});
test("createConnectionRef indexes endpoint from session._reuseEndpoint including jumpHosts and hostId", () => {
resetSshTransportRegistryForTests({ defaultIdleTtlMs: 60_000 });
const conn = makeConn();
const session = {
id: "s1",
_reuseEndpoint: {
hostId: "vault-host-1",
hostname: "indexed.example",
port: 2222,
username: "deploy",
jumpHosts: [{ hostname: "bastion", port: 22, username: "jump" }],
},
};
createConnectionRef(session, conn, []);
const found = findTransportByEndpoint({
hostId: "vault-host-1",
hostname: "indexed.example",
port: 2222,
username: "deploy",
jumpHosts: [{ hostname: "bastion", port: 22, username: "jump" }],
});
assert.ok(found);
assert.equal(found.conn, conn);
assert.equal(
findTransportByEndpoint({
hostId: "vault-host-1",
hostname: "indexed.example",
port: 2222,
username: "deploy",
}),
null,
"missing jump chain must not match",
);
assert.equal(
findTransportByEndpoint({
hostId: "other-host",
hostname: "indexed.example",
port: 2222,
username: "deploy",
jumpHosts: [{ hostname: "bastion", port: 22, username: "jump" }],
}),
null,
"different vault hostId must not match",
);
});
test("borrowTransport rejects bare non-registry connRef objects", () => {
const bare = { count: 1, conn: makeConn(), chainConnections: [] };
assert.throws(
() => acquireConnectionRef({ id: "x" }, bare),
/not a registry transport/,
);
});
test("transferConnectionRef rebinds a lease without changing count", () => {
const timers = useShortTtlTimers(1);
const conn = makeConn();
const transport = createTransport({ conn, endpoint: { hostname: "t.example" } });
const temp = {};
const session = { id: "shell-copy" };
borrowTransport(transport, { kind: LEASE_KINDS.shell, holder: temp });
assert.equal(transport.count, 1);
assert.equal(transferConnectionRef(temp, session), true);
assert.equal(transport.count, 1);
assert.equal(temp.connRef, null);
assert.equal(session.connRef, transport);
assert.ok(session._sshTransportLeaseId);
assert.equal(releaseConnectionRef(session), false);
assert.equal(transport.state, "idle");
fireIdleTimers(timers);
assert.equal(conn.ended, 1);
});