import { X509Certificate } from "node:crypto" ;
import { writeFile } from "node:fs/promises" ;
import path from "node:path" ;
import { afterEach, describe, expect, it } from "vitest" ;
import { createTrackedTempDirs } from "../../test-utils/tracked-temp-dirs.js" ;
import { normalizeFingerprint } from "./fingerprint.js" ;
import { loadGatewayTlsRuntime } from "./gateway.js" ;
const tempDirs = createTrackedTempDirs();
const createTempDir = () => tempDirs.make("openclaw-gateway-tls-test-" );
const KEY_PEM = [
"-----BEGIN PRIVATE KEY-----" , // pragma: allowlist secret
"MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDrur5CWp4psMMb" ,
"DTPY1aN46HPDxRchGgh8XedNkrlc4z1KFiyLUsXpVIhuyoXq1fflpTDz7++pGEDJ" ,
"Q5pEdChn3fuWgi7gC+pvd5VQ1eAX/7qVE72fhx14NxhaiZU3hCzXjG2SflTEEExk" ,
"UkQTm0rdHSjgLVMhTM3Pqm6Kzfdgtm9ZyXwlAsorE/pvgbUxG3Q4xKNBGzbirZ+1" ,
"EzPDwsjf3fitNtakZJkymu6Kg5lsUihQVXOP0U7f989FmevoTMvJmkvJzsoTRd7s" ,
"XNSOjzOwJr8da8C4HkXi21md1yEccyW0iSh7tWvDrpWDAgW6RMuMHC0tW4bkpDGr" ,
"FpbQOgzVAgMBAAECggEAIMhwf8Ve9CDVTWyNXpU9fgnj2aDOCeg3MGaVzaO/XCPt" ,
"KOHDEaAyDnRXYgMP0zwtFNafo3klnSBWmDbq3CTEXseQHtsdfkKh+J0KmrqXxval" ,
"YeikKSyvBEIzRJoYMqeS3eo1bddcXgT/Pr9zIL/qzivpPJ4JDttBzyTeaTbiNaR9" ,
"KphGNueo+MTQMLreMqw5VAyJ44gy7Z/2TMiMEc/d95wfubcOSsrIfpOKnMvWd/rl" ,
"vxIS33s95L7CjREkixskj5Yo5Wpt3Yf5b0Zi70YiEsCfAZUDrPW7YzMlylzmhMzm" ,
"MARZKfN1Tmo74SGpxUrBury+iPwf1sYcRnsHR+zO8QKBgQD6ISQHRzPboZ3J/60+" ,
"fRLETtrBa9WkvaH9c+woF7l47D4DIlvlv9D3N1KGkUmhMnp2jNKLIlalBNDxBdB+" ,
"iwZP1kikGz4629Ch3/KF/VYscLTlAQNPE42jOo7Hj7VrdQx9zQrK9ZBLteXmSvOh" ,
"bB3aXwXPF3HoTMt9gQ9thhXZJQKBgQDxQxUnQSw43dRlqYOHzPUEwnJkGkuW/qxn" ,
"aRc8eopP5zUaebiDFmqhY36x2Wd+HnXrzufy2o4jkXkWTau8Ns+OLhnIG3PIU9L/" ,
"LYzJMckGb75QYiK1YKMUUSQzlNCS8+TFVCTAvG2u2zCCk7oTIe8aT516BQNjWDjK" ,
"gWo2f87N8QKBgHoVANO4kfwJxszXyMPuIeHEpwquyijNEap2EPaEldcKXz4CYB4j" ,
"4Cc5TkM12F0gGRuRohWcnfOPBTgOYXPSATOoX+4RCe+KaCsJ9gIl4xBvtirrsqS+" ,
"42ue4h9O6fpXt9AS6sii0FnTnzEmtgC8l1mE9X3dcJA0I0HPYytOvY0tAoGAAYJj" ,
"7Xzw4+IvY/ttgTn9BmyY/ptTgbxSI8t6g7xYhStzH5lHWDqZrCzNLBuqFBXosvL2" ,
"bISFgx9z3Hnb6y+EmOUc8C2LyeMMXOBSEygmk827KRGUGgJiwsvHKDN0Ipc4BSwD" ,
"ltkW7pMceJSoA1qg/k8lMxA49zQkFtA8c97U0mECgYEAk2DDN78sRQI8RpSECJWy" ,
"l1O1ikVUAYVeh5HdZkpt++ddfpo695Op9OeD2Eq27Y5EVj8Xl58GFxNk0egLUnYq" ,
"YzSbjcNkR2SbVvuLaV1zlQKm6M5rfvhj4//YrzrrPUQda7Q4eR0as/3q91uzAO2O",
"++pfnSCVCyp/TxSkhEDEawU=" ,
"-----END PRIVATE KEY-----" ,
].join("\n" );
const CERT_PEM = `-----BEGIN CERTIFICATE-----
MIIDCTCCAfGgAwIBAgIUel0Lv05cjrViyI/H3tABBJxM7NgwDQYJKoZIhvcNAQEL
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI2MDEyMDEyMjEzMloXDTI2MDEy
MTEyMjEzMlowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEA67q+QlqeKbDDGw0z2NWjeOhzw8UXIRoIfF3nTZK5XOM9
ShYsi1LF6VSIbsqF6tX35aUw8+/vqRhAyUOaRHQoZ937loIu4Avqb3eVUNXgF/+6
lRO9n4cdeDcYWomVN4Qs14xtkn5UxBBMZFJEE5tK3R0o4C1TIUzNz6puis33YLZv
Wcl8JQLKKxP6b4G1MRt0OMSjQRs24q2ftRMzw8LI3934rTbWpGSZMpruioOZbFIo
UFVzj9FO3/fPRZnr6EzLyZpLyc7KE0Xe7FzUjo8zsCa/HWvAuB5F4ttZndchHHMl
tIkoe7Vrw66VgwIFukTLjBwtLVuG5KQxqxaW0DoM1QIDAQABo1MwUTAdBgNVHQ4E
FgQUwNdNkEQtd0n/aofzN7/EeYPPPbIwHwYDVR0jBBgwFoAUwNdNkEQtd0n/aofz
N7/EeYPPPbIwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAnOnw
o8Az/bL0A6bGHTYra3L9ArIIljMajT6KDHxylR4LhliuVNAznnhP3UkcZbUdjqjp
MNOM0lej2pNioondtQdXUskZtqWy6+dLbTm1RYQh1lbCCZQ26o7o/oENzjPksLAb
jRM47DYxRweTyRWQ5t9wvg/xL0Yi1tWq4u4FCNZlBMgdwAEnXNwVWTzRR9RHwy20
lmUzM8uQ/p42bk4EvPEV4PI1h5G0khQ6x9CtkadCTDs/ZqoUaJMwZBIDSrdJJSLw
4 Vh8Lqzia1CFB4um9J4S1Gm/VZMBjjeGGBJk7VSYn4ZmhPlbPM+6 z39lpQGEG0x4
r1USnb+wUdA7Zoj/mQ==
-----END CERTIFICATE-----`;
afterEach(async () => {
await tempDirs.cleanup();
});
describe("loadGatewayTlsRuntime" , () => {
it("disables tls when config is absent or disabled" , async () => {
await expect(loadGatewayTlsRuntime(undefined)).resolves.toEqual({
enabled: false ,
required: false ,
});
await expect(loadGatewayTlsRuntime({ enabled: false })).resolves.toEqual({
enabled: false ,
required: false ,
});
});
it("loads existing cert, key, and optional ca files" , async () => {
const dir = await createTempDir();
const certPath = path.join(dir, "gateway-cert.pem" );
const keyPath = path.join(dir, "gateway-key.pem" );
const caPath = path.join(dir, "gateway-ca.pem" );
await writeFile(certPath, CERT_PEM, "utf8" );
await writeFile(keyPath, KEY_PEM, "utf8" );
await writeFile(caPath, CERT_PEM, "utf8" );
const result = await loadGatewayTlsRuntime({
enabled: true ,
certPath,
keyPath,
caPath,
autoGenerate: false ,
});
expect(result).toMatchObject({
enabled: true ,
required: true ,
certPath,
keyPath,
caPath,
fingerprintSha256: normalizeFingerprint(new X509Certificate(CERT_PEM).fingerprint256 ?? "" ),
tlsOptions: {
cert: CERT_PEM,
key: KEY_PEM,
ca: CERT_PEM,
minVersion: "TLSv1.3" ,
},
});
expect(result.error).toBeUndefined();
});
it("fails closed when cert/key are missing and auto generation is disabled" , async () => {
const dir = await createTempDir();
const certPath = path.join(dir, "missing-cert.pem" );
const keyPath = path.join(dir, "missing-key.pem" );
await expect(
loadGatewayTlsRuntime({
enabled: true ,
certPath,
keyPath,
autoGenerate: false ,
}),
).resolves.toMatchObject({
enabled: false ,
required: true ,
certPath,
keyPath,
error: "gateway tls: cert/key missing" ,
});
});
it("reports load failures for invalid pem files" , async () => {
const dir = await createTempDir();
const certPath = path.join(dir, "gateway-cert.pem" );
const keyPath = path.join(dir, "gateway-key.pem" );
await writeFile(certPath, "not a certificate\n" , "utf8" );
await writeFile(keyPath, KEY_PEM, "utf8" );
const result = await loadGatewayTlsRuntime({
enabled: true ,
certPath,
keyPath,
autoGenerate: false ,
});
expect(result).toMatchObject({
enabled: false ,
required: true ,
certPath,
keyPath,
});
expect(result.error).toContain("gateway tls: failed to load cert" );
});
});
Messung V0.5 in Prozent C=94 H=100 G=96
¤ Dauer der Verarbeitung: 0.8 Sekunden
(vorverarbeitet am 2026-06-10)
¤
*© Formatika GbR, Deutschland