fix(ssh): read force keyboard-interactive as a boolean when resolving hosts (#1553)

The column holds the text "true"/"false", and the resolver passed it
through as-is. The string "false" is truthy, so the password provider
treated every saved host as forced keyboard-interactive and left the
password out. Jump hops are built straight from the resolved host, so a
password hop whose server doesn't offer keyboard-interactive failed with
"All configured authentication methods failed".
This commit is contained in:
ZacharyZcR authored and GitHub committed 2026-10-03 21:04:21 +08:00
1 parent 57151c0e84
commit 59083aac00
2 files changed
+14

No files matched your search

+4
View File
@@ -129,6 +129,10 @@ export async function resolveHostById(
}
host.terminalConfig = rest;
}
// Stored as the text "true"/"false"; the string "false" is truthy and would
// make the password provider skip password auth.
host.forceKeyboardInteractive =
String(host.forceKeyboardInteractive) === "true";
// A row the boot copy has not reached yet still has them in terminal_config.
host.sshOptions = parseSshOptions(
host.sshOptions != null ? host.sshOptions : host.terminalConfig,
@@ -487,6 +487,16 @@ describe("resolveHostById", () => {
expect(host.sshOptions).toEqual({ keepaliveCountMax: 3 });
});
it("turns the stored force_keyboard_interactive text into a boolean", async () => {
state.host = baseHost({ forceKeyboardInteractive: "false" });
const off = (await resolveHostById(42, "owner")) as Record<string, unknown>;
expect(off.forceKeyboardInteractive).toBe(false);
state.host = baseHost({ forceKeyboardInteractive: "true" });
const on = (await resolveHostById(42, "owner")) as Record<string, unknown>;
expect(on.forceKeyboardInteractive).toBe(true);
});
it("resolves an admin bypass like the owner, keeping owner-only secrets", async () => {
state.isAdminBypass = true;
state.host = baseHost({