fix: host key silently accepted when the host is missing from the database (#1397)

This commit is contained in:
LukeGus committed 2026-10-03 16:35:09 -05:00
1 parent 44be18988b
commit b3e220acf1
2 files changed
+75 -2

No files matched your search

+31 -2
View File
@@ -84,18 +84,47 @@ export class SSHHostKeyVerifier {
hostId,
);
// No row means nothing to compare against and nowhere to save the
// key, so it is never accepted silently (#1397): the user decides
// when there is a socket to ask on, otherwise it is refused.
if (!host) {
if (!ws) {
sshLogger.error(
"Host not found in database during key verification, rejecting",
{
operation: "host_key_no_host",
hostId,
ip,
port,
userId,
},
);
verify(false);
return;
}
const accepted = await this.promptUserForNewKey(
ws,
ip,
port,
undefined,
fingerprint,
keyType,
algorithm,
);
sshLogger.warn(
"Host not found in database during key verification",
accepted
? "Key for a host missing from the database accepted for this session only"
: "Key for a host missing from the database rejected",
{
operation: "host_key_no_host",
hostId,
ip,
port,
fingerprint,
userId,
},
);
verify(true);
verify(accepted);
return;
}
@@ -76,3 +76,47 @@ describe("host key recovery", () => {
expect(accepted).toEqual([]);
});
});
describe("a host missing from the database", () => {
function answeringSocket(action: "accept" | "reject") {
const socket = new EventEmitter() as EventEmitter & {
send: (data: string) => void;
};
socket.send = () =>
socket.emit(
"message",
Buffer.from(
JSON.stringify({
type: "host_key_verification_response",
data: { action },
}),
),
);
return socket as unknown as WebSocket;
}
async function verifyMissingHost(ws: WebSocket | null) {
const verifier = await SSHHostKeyVerifier.createHostVerifier(
hostId,
"127.0.0.1",
22,
ws,
"user",
false,
null,
);
return new Promise<boolean>((resolve) =>
verifier(Buffer.from("any-key"), resolve),
);
}
it("refuses the key when nobody can be asked", async () => {
expect(await verifyMissingHost(null)).toBe(false);
});
it("asks the user and saves nothing", async () => {
expect(await verifyMissingHost(answeringSocket("accept"))).toBe(true);
expect(await verifyMissingHost(answeringSocket("reject"))).toBe(false);
expect(updateHostKey).not.toHaveBeenCalled();
});
});