import assert from "node:assert/strict"; import { createHash } from "node:crypto"; import { readFile } from "node:fs/promises"; import test from "node:test"; import { validatePiSshConfig } from "../src/config.ts"; import { effectiveValue, effectiveValues, parseSshG } from "../src/import.ts"; import { fingerprintHostKey } from "../src/ssh2-transport.ts"; test("parses the effective ssh -G output including repeated identity files", () => { const parsed = parseSshG([ "host packaging-server", "hostname 192.0.2.20", "user builder", "port 2222", "identityfile ~/.ssh/first", "identityfile ~/.ssh/second", "proxyjump none", ].join("\n")); assert.equal(effectiveValue(parsed, "hostname"), "192.0.2.20"); assert.equal(effectiveValue(parsed, "port"), "2222"); assert.deepEqual(effectiveValues(parsed, "identityfile"), ["~/.ssh/first", "~/.ssh/second"]); }); test("validates password and private-key host definitions", () => { const password = validatePiSshConfig({ version: 1, hosts: { build: { hostName: "build.example.test", user: "builder", port: 22, auth: { type: "password", password: "secret" }, hostKey: { algorithm: "ssh-ed25519", fingerprint: "SHA256:value" }, }, }, groups: { builders: { label: "Builders", hosts: ["build"] } }, }); assert.equal(password.hosts.build.auth.type, "password"); assert.throws(() => validatePiSshConfig({ version: 1, hosts: { build: { hostName: "build.example.test", user: "builder", port: 22, auth: { type: "password", password: "" }, hostKey: { algorithm: "ssh-ed25519", fingerprint: "SHA256:value" }, }, }, }), /password/); }); test("formats SSH host keys as pinned SHA256 fingerprints", () => { const algorithm = Buffer.from("ssh-ed25519", "ascii"); const length = Buffer.alloc(4); length.writeUInt32BE(algorithm.length); const key = Buffer.concat([length, algorithm, Buffer.from("public-key-fixture")]); const expected = createHash("sha256").update(key).digest("base64").replace(/=+$/, ""); assert.deepEqual(fingerprintHostKey(key), { algorithm: "ssh-ed25519", fingerprint: `SHA256:${expected}`, }); }); test("configuration import validates remote HOME and cwd with framed probes", async () => { const source = await readFile(new URL("../scripts/ssh-config.mjs", import.meta.url), "utf8"); assert.match(source, /probeRemotePath\(transport, "home"\)/); assert.match(source, /probeRemotePath\(transport, "cwd"\)/); assert.doesNotMatch(source, /transport\.capture\(/); });