diff --git a/src/backend/hosts/host-resolver.ts b/src/backend/hosts/host-resolver.ts index 7c9befb0f..771553b38 100644 --- a/src/backend/hosts/host-resolver.ts +++ b/src/backend/hosts/host-resolver.ts @@ -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, diff --git a/src/backend/tests/hosts/host-resolver.test.ts b/src/backend/tests/hosts/host-resolver.test.ts index 94d8ecff3..e1057d643 100644 --- a/src/backend/tests/hosts/host-resolver.test.ts +++ b/src/backend/tests/hosts/host-resolver.test.ts @@ -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; + expect(off.forceKeyboardInteractive).toBe(false); + + state.host = baseHost({ forceKeyboardInteractive: "true" }); + const on = (await resolveHostById(42, "owner")) as Record; + expect(on.forceKeyboardInteractive).toBe(true); + }); + it("resolves an admin bypass like the owner, keeping owner-only secrets", async () => { state.isAdminBypass = true; state.host = baseHost({