92 lines
4.0 KiB
TypeScript
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`);
|
|
}
|
|
}
|
|
}
|
|
});
|