Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
693 lines
18 KiB
TypeScript
693 lines
18 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
import {
|
|
buildSftpHostCredentials,
|
|
} from "./useSftpHostCredentials.ts";
|
|
import type { Host, Identity, KnownHost, SSHKey } from "../../../domain/models.ts";
|
|
|
|
const host = (overrides: Partial<Host> = {}): Host => ({
|
|
id: "host-1",
|
|
label: "Host",
|
|
hostname: "example.com",
|
|
username: "root",
|
|
tags: [],
|
|
os: "linux",
|
|
...overrides,
|
|
});
|
|
|
|
test("buildSftpHostCredentials forwards system agent settings for target and jump hosts", () => {
|
|
const jump = host({
|
|
id: "jump-1",
|
|
hostname: "jump.example.com",
|
|
useSshAgent: true,
|
|
identityFilePaths: ["~/.ssh/jump"],
|
|
identitiesOnly: true,
|
|
});
|
|
const credentials = buildSftpHostCredentials({
|
|
host: host({
|
|
hostChain: { hostIds: ["jump-1"] },
|
|
useSshAgent: true,
|
|
identityAgent: "$SSH_AUTH_SOCK",
|
|
identityFilePaths: ["~/.ssh/aws_root"],
|
|
identitiesOnly: true,
|
|
addKeysToAgent: "yes",
|
|
useKeychain: true,
|
|
}),
|
|
hosts: [jump],
|
|
keys: [],
|
|
identities: [],
|
|
});
|
|
|
|
assert.deepEqual(
|
|
{
|
|
useSshAgent: credentials.useSshAgent,
|
|
identityAgent: credentials.identityAgent,
|
|
identityFilePaths: credentials.identityFilePaths,
|
|
identitiesOnly: credentials.identitiesOnly,
|
|
addKeysToAgent: credentials.addKeysToAgent,
|
|
useKeychain: credentials.useKeychain,
|
|
},
|
|
{
|
|
useSshAgent: true,
|
|
identityAgent: "$SSH_AUTH_SOCK",
|
|
identityFilePaths: ["~/.ssh/aws_root"],
|
|
identitiesOnly: true,
|
|
addKeysToAgent: "yes",
|
|
useKeychain: true,
|
|
},
|
|
);
|
|
assert.equal(credentials.jumpHosts?.[0]?.useSshAgent, true);
|
|
assert.deepEqual(credentials.jumpHosts?.[0]?.identityFilePaths, ["~/.ssh/jump"]);
|
|
assert.equal(credentials.jumpHosts?.[0]?.identitiesOnly, true);
|
|
});
|
|
|
|
test("buildSftpHostCredentials forwards target and jump-host timeouts", () => {
|
|
const jumpHost = host({
|
|
id: "jump-1",
|
|
sshTcpConnectTimeoutSeconds: 75,
|
|
sshAuthReadyTimeoutSeconds: 360,
|
|
});
|
|
const credentials = buildSftpHostCredentials({
|
|
host: host({
|
|
hostChain: { hostIds: ["jump-1"] },
|
|
sshTcpConnectTimeoutSeconds: 45,
|
|
sshAuthReadyTimeoutSeconds: 300,
|
|
}),
|
|
hosts: [jumpHost],
|
|
keys: [],
|
|
identities: [],
|
|
});
|
|
|
|
assert.equal(credentials.sshTcpConnectTimeoutMs, 45_000);
|
|
assert.equal(credentials.sshAuthReadyTimeoutMs, 300_000);
|
|
assert.equal(credentials.jumpHosts?.[0]?.sshTcpConnectTimeoutMs, 75_000);
|
|
assert.equal(credentials.jumpHosts?.[0]?.sshAuthReadyTimeoutMs, 360_000);
|
|
});
|
|
|
|
test("buildSftpHostCredentials keeps automatic auth per target and jump host", () => {
|
|
const jumpHost = host({ id: "jump-1", authMethod: "auto", password: "jump-secret", requiresMfa: true });
|
|
const credentials = buildSftpHostCredentials({
|
|
host: host({
|
|
authMethod: "auto",
|
|
password: "target-secret",
|
|
requiresMfa: true,
|
|
hostChain: { hostIds: ["jump-1"] },
|
|
}),
|
|
hosts: [jumpHost],
|
|
keys: [],
|
|
identities: [],
|
|
});
|
|
|
|
assert.equal(credentials.authMethod, "auto");
|
|
assert.equal(credentials.requiresMfa, true);
|
|
assert.equal(credentials.jumpHosts?.[0]?.authMethod, "auto");
|
|
assert.equal(credentials.jumpHosts?.[0]?.requiresMfa, true);
|
|
});
|
|
|
|
test("buildSftpHostCredentials drops stale identity paths for password-only hosts", () => {
|
|
const jumpHost = host({
|
|
id: "jump-1",
|
|
authMethod: "password",
|
|
password: "jump-secret",
|
|
useSshAgent: true,
|
|
identityFilePaths: ["~/.ssh/stale-jump"],
|
|
});
|
|
const credentials = buildSftpHostCredentials({
|
|
host: host({
|
|
authMethod: "password",
|
|
password: "target-secret",
|
|
useSshAgent: true,
|
|
identityFilePaths: ["~/.ssh/stale-target"],
|
|
hostChain: { hostIds: ["jump-1"] },
|
|
}),
|
|
hosts: [jumpHost],
|
|
keys: [],
|
|
identities: [],
|
|
});
|
|
|
|
assert.equal(credentials.identityFilePaths, undefined);
|
|
assert.equal(credentials.useSshAgent, false);
|
|
assert.equal(credentials.jumpHosts?.[0]?.identityFilePaths, undefined);
|
|
assert.equal(credentials.jumpHosts?.[0]?.useSshAgent, false);
|
|
});
|
|
|
|
test("buildSftpHostCredentials rejects missing jump hosts", () => {
|
|
assert.throws(
|
|
() => buildSftpHostCredentials({
|
|
host: host({ hostChain: { hostIds: ["missing-jump"] } }),
|
|
hosts: [],
|
|
keys: [],
|
|
identities: [],
|
|
}),
|
|
/Jump host "missing-jump" is missing/,
|
|
);
|
|
});
|
|
|
|
test("buildSftpHostCredentials rejects missing saved proxy profiles", () => {
|
|
assert.throws(
|
|
() => buildSftpHostCredentials({
|
|
host: host({ proxyProfileId: "missing-proxy" }),
|
|
hosts: [],
|
|
keys: [],
|
|
identities: [],
|
|
}),
|
|
/Saved proxy for host "Host" is missing/,
|
|
);
|
|
});
|
|
|
|
test("buildSftpHostCredentials rejects missing saved proxy profiles on jump hosts", () => {
|
|
const jumpHost = host({ id: "jump-1", label: "Jump", proxyProfileId: "missing-proxy" });
|
|
|
|
assert.throws(
|
|
() => buildSftpHostCredentials({
|
|
host: host({ hostChain: { hostIds: ["jump-1"] } }),
|
|
hosts: [jumpHost],
|
|
keys: [],
|
|
identities: [],
|
|
}),
|
|
/Saved proxy for jump host "Jump" is missing/,
|
|
);
|
|
});
|
|
|
|
test("buildSftpHostCredentials rejects missing proxy identities", () => {
|
|
assert.throws(
|
|
() => buildSftpHostCredentials({
|
|
host: host({
|
|
proxyConfig: {
|
|
type: "http",
|
|
host: "proxy.example.com",
|
|
port: 3128,
|
|
identityId: "missing-identity",
|
|
},
|
|
}),
|
|
hosts: [],
|
|
keys: [],
|
|
identities: [],
|
|
}),
|
|
/Proxy identity for "Host" is missing/,
|
|
);
|
|
});
|
|
|
|
test("buildSftpHostCredentials rejects incomplete proxy identities", () => {
|
|
assert.throws(
|
|
() => buildSftpHostCredentials({
|
|
host: host({
|
|
proxyConfig: {
|
|
type: "http",
|
|
host: "proxy.example.com",
|
|
port: 3128,
|
|
identityId: "identity-1",
|
|
},
|
|
}),
|
|
hosts: [],
|
|
keys: [],
|
|
identities: [{
|
|
id: "identity-1",
|
|
label: "Proxy login",
|
|
username: "proxy-user",
|
|
authMethod: "password",
|
|
created: 1,
|
|
}],
|
|
}),
|
|
/Proxy identity for "Host" is incomplete/,
|
|
);
|
|
});
|
|
|
|
test("buildSftpHostCredentials rejects proxy identities with blank usernames even when passwords are encrypted", () => {
|
|
assert.throws(
|
|
() => buildSftpHostCredentials({
|
|
host: host({
|
|
proxyConfig: {
|
|
type: "http",
|
|
host: "proxy.example.com",
|
|
port: 3128,
|
|
identityId: "identity-1",
|
|
},
|
|
}),
|
|
hosts: [],
|
|
keys: [],
|
|
identities: [{
|
|
id: "identity-1",
|
|
label: "Proxy login",
|
|
username: " ",
|
|
authMethod: "password",
|
|
password: "enc:v1:djEwdGVzdAAAAAAAAAAAAAAAAA==",
|
|
created: 1,
|
|
}],
|
|
}),
|
|
/Proxy identity for "Host" is incomplete/,
|
|
);
|
|
});
|
|
|
|
test("buildSftpHostCredentials rejects missing proxy identities on jump hosts", () => {
|
|
const jumpHost = host({
|
|
id: "jump-1",
|
|
label: "Jump",
|
|
proxyConfig: {
|
|
type: "http",
|
|
host: "proxy.example.com",
|
|
port: 3128,
|
|
identityId: "missing-identity",
|
|
},
|
|
});
|
|
|
|
assert.throws(
|
|
() => buildSftpHostCredentials({
|
|
host: host({ hostChain: { hostIds: ["jump-1"] } }),
|
|
hosts: [jumpHost],
|
|
keys: [],
|
|
identities: [],
|
|
}),
|
|
/Proxy identity for "Jump" is missing/,
|
|
);
|
|
});
|
|
|
|
test("buildSftpHostCredentials rejects incomplete proxy identities on jump hosts", () => {
|
|
const jumpHost = host({
|
|
id: "jump-1",
|
|
label: "Jump",
|
|
proxyConfig: {
|
|
type: "http",
|
|
host: "proxy.example.com",
|
|
port: 3128,
|
|
identityId: "identity-1",
|
|
},
|
|
});
|
|
|
|
assert.throws(
|
|
() => buildSftpHostCredentials({
|
|
host: host({ hostChain: { hostIds: ["jump-1"] } }),
|
|
hosts: [jumpHost],
|
|
keys: [],
|
|
identities: [{
|
|
id: "identity-1",
|
|
label: "Proxy login",
|
|
username: "",
|
|
authMethod: "password",
|
|
password: "enc:v1:djEwdGVzdAAAAAAAAAAAAAAAAA==",
|
|
created: 1,
|
|
}],
|
|
}),
|
|
/Proxy identity for "Jump" is incomplete/,
|
|
);
|
|
});
|
|
|
|
test("buildSftpHostCredentials forwards custom ProxyCommand settings", () => {
|
|
const credentials = buildSftpHostCredentials({
|
|
host: host({
|
|
proxyConfig: {
|
|
type: "command",
|
|
host: "",
|
|
port: 0,
|
|
command: "cloudflared access ssh --hostname %h",
|
|
},
|
|
}),
|
|
hosts: [],
|
|
keys: [],
|
|
identities: [],
|
|
});
|
|
|
|
assert.deepEqual(credentials.proxy, {
|
|
type: "command",
|
|
host: "",
|
|
port: 0,
|
|
command: "cloudflared access ssh --hostname %h",
|
|
username: undefined,
|
|
password: undefined,
|
|
});
|
|
});
|
|
|
|
test("buildSftpHostCredentials resolves proxy credentials from a selected identity", () => {
|
|
const identity: Identity = {
|
|
id: "identity-1",
|
|
label: "Proxy login",
|
|
username: "proxy-user",
|
|
authMethod: "password",
|
|
password: "proxy-secret",
|
|
created: 1,
|
|
};
|
|
const credentials = buildSftpHostCredentials({
|
|
host: host({
|
|
proxyConfig: {
|
|
type: "socks5",
|
|
host: "proxy.example.com",
|
|
port: 1080,
|
|
identityId: identity.id,
|
|
},
|
|
}),
|
|
hosts: [],
|
|
keys: [],
|
|
identities: [identity],
|
|
});
|
|
|
|
assert.deepEqual(credentials.proxy, {
|
|
type: "socks5",
|
|
host: "proxy.example.com",
|
|
port: 1080,
|
|
username: "proxy-user",
|
|
password: "proxy-secret",
|
|
});
|
|
});
|
|
|
|
test("buildSftpHostCredentials rejects undecryptable proxy identity passwords", () => {
|
|
assert.throws(
|
|
() => buildSftpHostCredentials({
|
|
host: host({
|
|
proxyConfig: {
|
|
type: "http",
|
|
host: "proxy.example.com",
|
|
port: 3128,
|
|
identityId: "identity-1",
|
|
},
|
|
}),
|
|
hosts: [],
|
|
keys: [],
|
|
identities: [{
|
|
id: "identity-1",
|
|
label: "Proxy login",
|
|
username: "proxy-user",
|
|
authMethod: "password",
|
|
password: "enc:v1:djEwdGVzdAAAAAAAAAAAAAAAAA==",
|
|
created: 1,
|
|
}],
|
|
}),
|
|
/Proxy credentials cannot be decrypted/,
|
|
);
|
|
});
|
|
|
|
test("buildSftpHostCredentials resolves jump host proxy credentials from a selected identity", () => {
|
|
const identity: Identity = {
|
|
id: "identity-1",
|
|
label: "Proxy login",
|
|
username: "proxy-user",
|
|
authMethod: "password",
|
|
password: "proxy-secret",
|
|
created: 1,
|
|
};
|
|
const jumpHost = host({
|
|
id: "jump-1",
|
|
label: "Jump",
|
|
proxyConfig: {
|
|
type: "socks5",
|
|
host: "jump-proxy.example.com",
|
|
port: 1080,
|
|
identityId: identity.id,
|
|
},
|
|
});
|
|
|
|
const credentials = buildSftpHostCredentials({
|
|
host: host({ hostChain: { hostIds: ["jump-1"] } }),
|
|
hosts: [jumpHost],
|
|
keys: [],
|
|
identities: [identity],
|
|
});
|
|
|
|
assert.deepEqual(credentials.jumpHosts?.[0]?.proxy, {
|
|
type: "socks5",
|
|
host: "jump-proxy.example.com",
|
|
port: 1080,
|
|
username: "proxy-user",
|
|
password: "proxy-secret",
|
|
});
|
|
});
|
|
|
|
test("buildSftpHostCredentials rejects undecryptable jump host proxy identity passwords", () => {
|
|
const jumpHost = host({
|
|
id: "jump-1",
|
|
label: "Jump",
|
|
proxyConfig: {
|
|
type: "http",
|
|
host: "proxy.example.com",
|
|
port: 3128,
|
|
identityId: "identity-1",
|
|
},
|
|
});
|
|
|
|
assert.throws(
|
|
() => buildSftpHostCredentials({
|
|
host: host({ hostChain: { hostIds: ["jump-1"] } }),
|
|
hosts: [jumpHost],
|
|
keys: [],
|
|
identities: [{
|
|
id: "identity-1",
|
|
label: "Proxy login",
|
|
username: "proxy-user",
|
|
authMethod: "password",
|
|
password: "enc:v1:djEwdGVzdAAAAAAAAAAAAAAAAA==",
|
|
created: 1,
|
|
}],
|
|
}),
|
|
/Proxy credentials for jump host "Jump" cannot be decrypted/,
|
|
);
|
|
});
|
|
|
|
test("buildSftpHostCredentials passes reference keys as identity file paths", () => {
|
|
const key: SSHKey = {
|
|
id: "key-1",
|
|
label: "Reference key",
|
|
type: "ED25519",
|
|
privateKey: "",
|
|
source: "reference",
|
|
category: "key",
|
|
created: 1,
|
|
filePath: "/Users/alice/.ssh/id_ed25519",
|
|
passphrase: "saved-passphrase",
|
|
};
|
|
|
|
const credentials = buildSftpHostCredentials({
|
|
host: host({ authMethod: "key", identityFileId: "key-1" }),
|
|
hosts: [],
|
|
keys: [key],
|
|
identities: [],
|
|
});
|
|
|
|
assert.equal(credentials.privateKey, undefined);
|
|
assert.deepEqual(credentials.identityFilePaths, ["/Users/alice/.ssh/id_ed25519"]);
|
|
assert.equal(credentials.passphrase, "saved-passphrase");
|
|
});
|
|
|
|
test("buildSftpHostCredentials forwards known hosts for SFTP host-key checks", () => {
|
|
const knownHosts: KnownHost[] = [{
|
|
id: "kh-1",
|
|
hostname: "example.com",
|
|
port: 22,
|
|
keyType: "ssh-ed25519",
|
|
publicKey: "SHA256:abc",
|
|
fingerprint: "abc",
|
|
discoveredAt: 1,
|
|
}];
|
|
|
|
const credentials = buildSftpHostCredentials({
|
|
host: host(),
|
|
hosts: [],
|
|
keys: [],
|
|
identities: [],
|
|
knownHosts,
|
|
});
|
|
|
|
assert.equal(credentials.knownHosts, knownHosts);
|
|
});
|
|
|
|
test("buildSftpHostCredentials forwards the host-key verification setting", () => {
|
|
const credentials = buildSftpHostCredentials({
|
|
host: host(),
|
|
hosts: [],
|
|
keys: [],
|
|
identities: [],
|
|
terminalSettings: {
|
|
verifyHostKeys: false,
|
|
keepaliveInterval: 30,
|
|
keepaliveCountMax: 10,
|
|
},
|
|
});
|
|
|
|
assert.equal(credentials.verifyHostKeys, false);
|
|
});
|
|
|
|
test("buildSftpHostCredentials passes jump host reference keys as identity file paths", () => {
|
|
const key: SSHKey = {
|
|
id: "jump-key",
|
|
label: "Jump key",
|
|
type: "ED25519",
|
|
privateKey: "",
|
|
source: "reference",
|
|
category: "key",
|
|
created: 1,
|
|
filePath: "/Users/alice/.ssh/jump_ed25519",
|
|
};
|
|
const jumpHost = host({
|
|
id: "jump-1",
|
|
label: "Jump",
|
|
authMethod: "key",
|
|
identityFileId: "jump-key",
|
|
});
|
|
|
|
const credentials = buildSftpHostCredentials({
|
|
host: host({ hostChain: { hostIds: ["jump-1"] } }),
|
|
hosts: [jumpHost],
|
|
keys: [key],
|
|
identities: [],
|
|
});
|
|
|
|
assert.equal(credentials.jumpHosts?.[0]?.privateKey, undefined);
|
|
assert.deepEqual(credentials.jumpHosts?.[0]?.identityFilePaths, ["/Users/alice/.ssh/jump_ed25519"]);
|
|
});
|
|
|
|
test("buildSftpHostCredentials rejects undecryptable saved password credentials", () => {
|
|
assert.throws(
|
|
() => buildSftpHostCredentials({
|
|
host: host({
|
|
authMethod: "password",
|
|
password: "enc:v1:djEwdGVzdAAAAAAAAAAAAAAAAA==",
|
|
}),
|
|
hosts: [],
|
|
keys: [],
|
|
identities: [],
|
|
}),
|
|
/Saved credentials cannot be decrypted/,
|
|
);
|
|
});
|
|
|
|
test("buildSftpHostCredentials keeps automatic target discovery available with an unreadable saved password", () => {
|
|
const credentials = buildSftpHostCredentials({
|
|
host: host({
|
|
authMethod: "auto",
|
|
password: "enc:v1:djEwdGVzdAAAAAAAAAAAAAAAAA==",
|
|
}),
|
|
hosts: [],
|
|
keys: [],
|
|
identities: [],
|
|
});
|
|
|
|
assert.equal(credentials.authMethod, "auto");
|
|
assert.equal(credentials.password, undefined);
|
|
});
|
|
|
|
test("buildSftpHostCredentials keeps automatic jump discovery available with an unreadable saved password", () => {
|
|
const jumpHost = host({
|
|
id: "jump-1",
|
|
label: "Jump",
|
|
authMethod: "auto",
|
|
password: "enc:v1:djEwdGVzdAAAAAAAAAAAAAAAAA==",
|
|
});
|
|
const credentials = buildSftpHostCredentials({
|
|
host: host({ hostChain: { hostIds: ["jump-1"] } }),
|
|
hosts: [jumpHost],
|
|
keys: [],
|
|
identities: [],
|
|
});
|
|
|
|
assert.equal(credentials.jumpHosts?.[0]?.authMethod, "auto");
|
|
assert.equal(credentials.jumpHosts?.[0]?.password, undefined);
|
|
});
|
|
|
|
test("buildSftpHostCredentials omits local key file paths for password auth", () => {
|
|
const credentials = buildSftpHostCredentials({
|
|
host: host({
|
|
authMethod: "password",
|
|
password: "secret",
|
|
identityFilePaths: ["/Users/alice/.ssh/id_ed25519"],
|
|
}),
|
|
hosts: [],
|
|
keys: [],
|
|
identities: [],
|
|
});
|
|
|
|
assert.equal(credentials.password, "secret");
|
|
assert.equal(credentials.privateKey, undefined);
|
|
assert.equal(credentials.identityFilePaths, undefined);
|
|
});
|
|
|
|
test("buildSftpHostCredentials rejects undecryptable saved key material without fallback credentials", () => {
|
|
const key: SSHKey = {
|
|
id: "key-1",
|
|
label: "Imported key",
|
|
type: "ED25519",
|
|
privateKey: "enc:v1:djEwdGVzdAAAAAAAAAAAAAAAAA==",
|
|
source: "imported",
|
|
category: "key",
|
|
created: 1,
|
|
};
|
|
|
|
assert.throws(
|
|
() => buildSftpHostCredentials({
|
|
host: host({ authMethod: "key", identityFileId: "key-1" }),
|
|
hosts: [],
|
|
keys: [key],
|
|
identities: [],
|
|
}),
|
|
/Saved credentials cannot be decrypted/,
|
|
);
|
|
});
|
|
|
|
test("buildSftpHostCredentials does not use stale local key paths when a selected key is unavailable", () => {
|
|
const key: SSHKey = {
|
|
id: "key-1",
|
|
label: "Imported key",
|
|
type: "ED25519",
|
|
privateKey: "enc:v1:djEwdGVzdAAAAAAAAAAAAAAAAA==",
|
|
source: "imported",
|
|
category: "key",
|
|
created: 1,
|
|
};
|
|
|
|
assert.throws(
|
|
() => buildSftpHostCredentials({
|
|
host: host({
|
|
authMethod: "key",
|
|
identityFileId: "key-1",
|
|
identityFilePaths: ["/Users/alice/.ssh/stale_ed25519"],
|
|
}),
|
|
hosts: [],
|
|
keys: [key],
|
|
identities: [],
|
|
}),
|
|
/Saved credentials cannot be decrypted/,
|
|
);
|
|
});
|
|
|
|
test("buildSftpHostCredentials uses the system agent when a synced key cannot be decrypted", () => {
|
|
const key: SSHKey = {
|
|
id: "key-1",
|
|
label: "Synced key",
|
|
type: "ED25519",
|
|
publicKey: "ssh-ed25519 AAAASELECTED",
|
|
privateKey: "enc:v1:djEwdGVzdAAAAAAAAAAAAAAAAA==",
|
|
source: "imported",
|
|
category: "key",
|
|
created: 1,
|
|
};
|
|
|
|
const credentials = buildSftpHostCredentials({
|
|
host: host({
|
|
authMethod: "key",
|
|
identityFileId: "key-1",
|
|
useSshAgent: true,
|
|
}),
|
|
hosts: [],
|
|
keys: [key],
|
|
identities: [],
|
|
});
|
|
|
|
assert.equal(credentials.useSshAgent, true);
|
|
assert.deepEqual(credentials.agentPublicKeys, ["ssh-ed25519 AAAASELECTED"]);
|
|
assert.equal(credentials.privateKey, undefined);
|
|
});
|
|
|
|
test("imported IdentityFile paths remain usable when agent login is disabled", () => {
|
|
const credentials = buildSftpHostCredentials({
|
|
host: host({
|
|
identityFilePaths: ["~/.ssh/id_work"],
|
|
useSshAgent: false,
|
|
identityAgent: "none",
|
|
}),
|
|
hosts: [],
|
|
keys: [],
|
|
identities: [],
|
|
});
|
|
|
|
assert.equal(credentials.useSshAgent, false);
|
|
assert.deepEqual(credentials.identityFilePaths, ["~/.ssh/id_work"]);
|
|
});
|