Files
Netcatty/domain/sshAuth.test.ts
bincxz 4b07b4826a fix(terminal): resolve sudo autofill password through identity references (#1284)
Sudo autofill only read host.password and never resolved a host's reference
to a Keychain identity (host.identityId). When the account password lived in
a referenced identity, the autofill got nothing — while SSH login worked
because it goes through resolveHostAuth, which resolves the identity.

Add domain resolveHostAutofillPassword (same resolveHostAuth resolution:
identity.password ?? host.password, honoring savePassword and dropping
undecryptable placeholders) and use it as the terminal autofill password
source. Login and autofill now share one resolution path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 13:21:54 +08:00

156 lines
4.0 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import { resolveBridgeKeyAuth, resolveHostAuth, resolveHostAutofillPassword } from "./sshAuth.ts";
import type { Host, Identity, SSHKey } from "./models.ts";
const referenceKey: SSHKey = {
id: "key-1",
label: "Reference key",
type: "ED25519",
privateKey: "",
source: "reference",
category: "key",
created: 1,
filePath: "/Users/alice/.ssh/id_ed25519",
};
test("resolveBridgeKeyAuth passes reference keys as identity file paths", () => {
assert.deepEqual(
resolveBridgeKeyAuth({
key: referenceKey,
fallbackIdentityFilePaths: ["/legacy/key"],
passphrase: "saved-passphrase",
}),
{
privateKey: undefined,
identityFilePaths: ["/Users/alice/.ssh/id_ed25519"],
passphrase: "saved-passphrase",
},
);
});
test("resolveBridgeKeyAuth ignores undecryptable passphrase placeholders", () => {
assert.equal(
resolveBridgeKeyAuth({
key: {
...referenceKey,
passphrase: "enc:v1:djEwAAAA",
},
}).passphrase,
undefined,
);
});
test("resolveBridgeKeyAuth ignores undecryptable private key placeholders", () => {
assert.equal(
resolveBridgeKeyAuth({
key: {
...referenceKey,
source: "imported",
filePath: undefined,
privateKey: "enc:v1:djEwAAAA",
},
}).privateKey,
undefined,
);
});
test("resolveBridgeKeyAuth preserves imported key material", () => {
const importedKey: SSHKey = {
...referenceKey,
source: "imported",
privateKey: "PRIVATE KEY",
filePath: undefined,
};
assert.deepEqual(
resolveBridgeKeyAuth({
key: importedKey,
fallbackIdentityFilePaths: ["/legacy/key"],
}),
{
privateKey: "PRIVATE KEY",
identityFilePaths: ["/legacy/key"],
passphrase: undefined,
},
);
});
test("resolveHostAuth respects password auth over stale key selections", () => {
const host: Host = {
id: "host-1",
label: "Host",
hostname: "example.com",
username: "root",
authMethod: "password",
identityFileId: "key-1",
};
const resolved = resolveHostAuth({
host,
keys: [referenceKey],
identities: [],
});
assert.equal(resolved.authMethod, "password");
assert.equal(resolved.key, undefined);
assert.equal(resolved.keyId, undefined);
});
const autofillBaseHost = {
id: "h1",
label: "Host",
hostname: "h.example.test",
username: "alice",
} as Host;
test("resolveHostAutofillPassword uses the host's own saved password", () => {
assert.equal(
resolveHostAutofillPassword({ host: { ...autofillBaseHost, password: "direct-secret" }, keys: [] }),
"direct-secret",
);
});
test("resolveHostAutofillPassword resolves a referenced keychain identity's password", () => {
// host stores no password of its own; the credential lives in a Keychain
// identity it references (host.identityId) — the #1284 scenario.
const identity = {
id: "id-1",
label: "alice@prod",
username: "alice",
authMethod: "password",
password: "identity-secret",
created: 1,
} as Identity;
assert.equal(
resolveHostAutofillPassword({
host: { ...autofillBaseHost, password: undefined, identityId: "id-1" },
keys: [],
identities: [identity],
}),
"identity-secret",
);
});
test("resolveHostAutofillPassword returns undefined when the host opts out of saving", () => {
assert.equal(
resolveHostAutofillPassword({ host: { ...autofillBaseHost, password: "x", savePassword: false }, keys: [] }),
undefined,
);
});
test("resolveHostAutofillPassword returns undefined when no password is available", () => {
assert.equal(
resolveHostAutofillPassword({ host: { ...autofillBaseHost, password: undefined }, keys: [] }),
undefined,
);
});
test("resolveHostAutofillPassword ignores undecryptable password placeholders", () => {
assert.equal(
resolveHostAutofillPassword({ host: { ...autofillBaseHost, password: "enc:v1:djEwAAAA" }, keys: [] }),
undefined,
);
});