Files
Netcatty/domain/sshAlgorithmList.test.ts
2026-06-05 05:54:23 +00:00

92 lines
4.0 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import { createRequire } from "node:module";
import {
effectiveDefaultAlgorithms,
SUPPORTED_ALGORITHMS_BY_CATEGORY,
} from "./sshAlgorithmList.ts";
const requireSsh2 = createRequire(import.meta.url);
// Anchor the UI editor's supported lists to what ssh2 will actually
// accept at connect time. If ssh2 drops a cipher / KEX / MAC at any
// point (OpenSSL 3 already removed blowfish / arcfour / cast128, for
// example) the editor must not offer it — picking it would throw
// "Unsupported algorithm" synchronously before negotiation.
const ssh2Constants = requireSsh2("ssh2/lib/protocol/constants.js");
const SSH2_SUPPORTED_BY_CATEGORY: Record<string, readonly string[]> = {
kex: ssh2Constants.SUPPORTED_KEX,
cipher: ssh2Constants.SUPPORTED_CIPHER,
hmac: ssh2Constants.SUPPORTED_MAC,
serverHostKey: ssh2Constants.SUPPORTED_SERVER_HOST_KEY,
compress: ssh2Constants.SUPPORTED_COMPRESSION,
};
test("effectiveDefaultAlgorithms (modern) never seeds legacy SHA-1 KEX", () => {
const result = effectiveDefaultAlgorithms(false);
assert.ok(!result.kex.includes("diffie-hellman-group1-sha1"));
assert.ok(!result.kex.includes("diffie-hellman-group14-sha1"));
assert.ok(!result.kex.includes("diffie-hellman-group-exchange-sha1"));
// Modern KEX still present.
assert.ok(result.kex.includes("curve25519-sha256"));
assert.ok(result.kex.includes("diffie-hellman-group14-sha256"));
});
test("effectiveDefaultAlgorithms (modern) never seeds CBC / arcfour / MD5", () => {
const result = effectiveDefaultAlgorithms(false);
for (const algo of result.cipher) {
assert.ok(!algo.endsWith("-cbc"), `${algo} is a CBC cipher and should not be in modern defaults`);
assert.ok(!algo.startsWith("arcfour"), `${algo} (arcfour) should not be in modern defaults`);
assert.ok(algo !== "3des-cbc", "3des-cbc is legacy");
}
for (const algo of result.hmac) {
assert.ok(!algo.includes("md5"), `${algo} should not be in modern defaults`);
}
});
test("effectiveDefaultAlgorithms (modern) includes chacha20-poly1305", () => {
const result = effectiveDefaultAlgorithms(false);
assert.ok(result.cipher.includes("chacha20-poly1305@openssh.com"));
});
test("effectiveDefaultAlgorithms (legacy) appends sha1 KEX, CBC, and ssh-dss", () => {
const modern = effectiveDefaultAlgorithms(false);
const legacy = effectiveDefaultAlgorithms(true);
// Every modern algorithm is still present.
for (const category of Object.keys(modern) as (keyof typeof modern)[]) {
for (const algo of modern[category]) {
assert.ok(legacy[category].includes(algo), `${algo} missing from legacy ${category}`);
}
}
assert.ok(legacy.kex.includes("diffie-hellman-group14-sha1"));
assert.ok(legacy.kex.includes("diffie-hellman-group1-sha1"));
assert.ok(legacy.cipher.includes("aes128-cbc"));
assert.ok(legacy.cipher.includes("3des-cbc"));
assert.ok(legacy.serverHostKey.includes("ssh-dss"));
});
test("SUPPORTED_ALGORITHMS_BY_CATEGORY only lists algorithms ssh2 will actually accept", () => {
for (const category of Object.keys(SUPPORTED_ALGORITHMS_BY_CATEGORY) as (keyof typeof SUPPORTED_ALGORITHMS_BY_CATEGORY)[]) {
const ssh2Supported = SSH2_SUPPORTED_BY_CATEGORY[category];
assert.ok(ssh2Supported, `unexpected category ${category}`);
for (const algo of SUPPORTED_ALGORITHMS_BY_CATEGORY[category]) {
assert.ok(
ssh2Supported.includes(algo),
`${algo} (${category}) is in the UI list but ssh2 would reject it`,
);
}
}
});
test("effectiveDefaultAlgorithms output is a subset of SUPPORTED_ALGORITHMS_BY_CATEGORY", () => {
for (const enabled of [false, true]) {
const result = effectiveDefaultAlgorithms(enabled);
for (const category of Object.keys(result) as (keyof typeof result)[]) {
const supported = SUPPORTED_ALGORITHMS_BY_CATEGORY[category];
for (const algo of result[category]) {
assert.ok(supported.includes(algo), `${algo} (${category}) not in supported list`);
}
}
}
});