Files
headlamp-sealed-secrets-plugin/src/lib/crypto.test.ts
T
DevContainer User 9d9bc5f22f fix: remove any types, dead code, unused exports; add comprehensive tests
- Fix handleRotate bug ignoring Result from rotateSealedSecret()
- Fix dead code branch in useControllerHealth
- Replace all `any` types with `unknown` + type guards
- Delete unused functions/exports (452 lines removed)
- Add 18 new test files covering all hooks, libs, and components
- 233 tests passing, zero tsc errors, zero lint issues

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 17:13:00 +00:00

298 lines
10 KiB
TypeScript

/**
* Unit tests for client-side encryption utilities
*/
import forge from 'node-forge';
import { beforeAll, describe, expect, it } from 'vitest';
import { PEMCertificate, PlaintextValue } from '../types';
import {
encryptKeyValues,
isCertificateExpiringSoon,
parseCertificateInfo,
parsePublicKeyFromCert,
} from './crypto';
// Generate a real self-signed cert for testing
let validPEM: PEMCertificate;
let expiredPEM: PEMCertificate;
let expiringSoonPEM: PEMCertificate;
beforeAll(() => {
// Generate RSA key pair
const keys = forge.pki.rsa.generateKeyPair(2048);
// Valid cert (expires in 365 days)
const validCert = forge.pki.createCertificate();
validCert.publicKey = keys.publicKey;
validCert.serialNumber = '01';
validCert.validity.notBefore = new Date();
validCert.validity.notAfter = new Date();
validCert.validity.notAfter.setFullYear(validCert.validity.notAfter.getFullYear() + 1);
validCert.setSubject([{ name: 'commonName', value: 'test-controller' }]);
validCert.setIssuer([{ name: 'commonName', value: 'test-issuer' }]);
validCert.sign(keys.privateKey, forge.md.sha256.create());
validPEM = PEMCertificate(forge.pki.certificateToPem(validCert));
// Expired cert
const expiredCert = forge.pki.createCertificate();
expiredCert.publicKey = keys.publicKey;
expiredCert.serialNumber = '02';
expiredCert.validity.notBefore = new Date('2020-01-01');
expiredCert.validity.notAfter = new Date('2021-01-01');
expiredCert.setSubject([{ name: 'commonName', value: 'expired-controller' }]);
expiredCert.setIssuer([{ name: 'commonName', value: 'test-issuer' }]);
expiredCert.sign(keys.privateKey, forge.md.sha256.create());
expiredPEM = PEMCertificate(forge.pki.certificateToPem(expiredCert));
// Expiring soon cert (15 days from now)
const expiringSoonCert = forge.pki.createCertificate();
expiringSoonCert.publicKey = keys.publicKey;
expiringSoonCert.serialNumber = '03';
expiringSoonCert.validity.notBefore = new Date();
const expiryDate = new Date();
expiryDate.setDate(expiryDate.getDate() + 15);
expiringSoonCert.validity.notAfter = expiryDate;
expiringSoonCert.setSubject([{ name: 'commonName', value: 'expiring-controller' }]);
expiringSoonCert.setIssuer([{ name: 'commonName', value: 'test-issuer' }]);
expiringSoonCert.sign(keys.privateKey, forge.md.sha256.create());
expiringSoonPEM = PEMCertificate(forge.pki.certificateToPem(expiringSoonCert));
});
describe('crypto', () => {
describe('parsePublicKeyFromCert', () => {
it('should parse valid PEM certificate', () => {
const result = parsePublicKeyFromCert(validPEM);
expect(result.ok).toBe(true);
if (result.ok) {
expect(result.value).toBeDefined();
expect(result.value.n).toBeDefined(); // RSA modulus
expect(result.value.e).toBeDefined(); // RSA exponent
}
});
it('should return error for invalid PEM', () => {
const result = parsePublicKeyFromCert(PEMCertificate('not a cert'));
expect(result.ok).toBe(false);
if (result.ok === false) {
expect(result.error).toContain('Failed to parse certificate');
}
});
it('should return error for empty string', () => {
const result = parsePublicKeyFromCert(PEMCertificate(''));
expect(result.ok).toBe(false);
});
it('should return error for malformed PEM markers', () => {
const result = parsePublicKeyFromCert(
PEMCertificate('-----BEGIN CERTIFICATE-----\ninvalid\n-----END CERTIFICATE-----')
);
expect(result.ok).toBe(false);
});
});
describe('encryptKeyValues', () => {
it('should encrypt key-value pairs with strict scope', () => {
const keyResult = parsePublicKeyFromCert(validPEM);
expect(keyResult.ok).toBe(true);
if (!keyResult.ok) return;
const result = encryptKeyValues(
keyResult.value,
[{ key: 'password', value: PlaintextValue('secret123') }],
'default',
'my-secret',
'strict'
);
expect(result.ok).toBe(true);
if (result.ok) {
expect(result.value).toHaveProperty('password');
// Should be base64 encoded
expect(() => forge.util.decode64(result.value.password)).not.toThrow();
}
});
it('should encrypt with namespace-wide scope', () => {
const keyResult = parsePublicKeyFromCert(validPEM);
if (!keyResult.ok) return;
const result = encryptKeyValues(
keyResult.value,
[{ key: 'token', value: PlaintextValue('abc') }],
'prod',
'my-secret',
'namespace-wide'
);
expect(result.ok).toBe(true);
});
it('should encrypt with cluster-wide scope', () => {
const keyResult = parsePublicKeyFromCert(validPEM);
if (!keyResult.ok) return;
const result = encryptKeyValues(
keyResult.value,
[{ key: 'key', value: PlaintextValue('val') }],
'ns',
'name',
'cluster-wide'
);
expect(result.ok).toBe(true);
});
it('should encrypt multiple keys', () => {
const keyResult = parsePublicKeyFromCert(validPEM);
if (!keyResult.ok) return;
const result = encryptKeyValues(
keyResult.value,
[
{ key: 'username', value: PlaintextValue('admin') },
{ key: 'password', value: PlaintextValue('secret') },
{ key: 'token', value: PlaintextValue('abc123') },
],
'default',
'my-secret',
'strict'
);
expect(result.ok).toBe(true);
if (result.ok) {
expect(Object.keys(result.value)).toHaveLength(3);
expect(result.value).toHaveProperty('username');
expect(result.value).toHaveProperty('password');
expect(result.value).toHaveProperty('token');
}
});
it('should produce different ciphertext for same plaintext (randomness)', () => {
const keyResult = parsePublicKeyFromCert(validPEM);
if (!keyResult.ok) return;
const encrypt = () =>
encryptKeyValues(
keyResult.value,
[{ key: 'key', value: PlaintextValue('same-value') }],
'ns',
'name',
'strict'
);
const result1 = encrypt();
const result2 = encrypt();
expect(result1.ok).toBe(true);
expect(result2.ok).toBe(true);
if (result1.ok && result2.ok) {
expect(result1.value.key).not.toBe(result2.value.key);
}
});
it('should handle empty key-value array', () => {
const keyResult = parsePublicKeyFromCert(validPEM);
if (!keyResult.ok) return;
const result = encryptKeyValues(keyResult.value, [], 'ns', 'name', 'strict');
expect(result.ok).toBe(true);
if (result.ok) {
expect(Object.keys(result.value)).toHaveLength(0);
}
});
});
describe('parseCertificateInfo', () => {
it('should parse valid certificate info', () => {
const result = parseCertificateInfo(validPEM);
expect(result.ok).toBe(true);
if (result.ok) {
expect(result.value.validFrom).toBeInstanceOf(Date);
expect(result.value.validTo).toBeInstanceOf(Date);
expect(result.value.isExpired).toBe(false);
expect(result.value.daysUntilExpiry).toBeGreaterThan(0);
expect(result.value.subject).toContain('test-controller');
expect(result.value.issuer).toContain('test-issuer');
expect(result.value.fingerprint).toBeDefined();
expect(result.value.serialNumber).toBeDefined();
}
});
it('should detect expired certificate', () => {
const result = parseCertificateInfo(expiredPEM);
expect(result.ok).toBe(true);
if (result.ok) {
expect(result.value.isExpired).toBe(true);
expect(result.value.daysUntilExpiry).toBeLessThan(0);
}
});
it('should calculate days until expiry for expiring-soon cert', () => {
const result = parseCertificateInfo(expiringSoonPEM);
expect(result.ok).toBe(true);
if (result.ok) {
expect(result.value.isExpired).toBe(false);
expect(result.value.daysUntilExpiry).toBeLessThanOrEqual(15);
expect(result.value.daysUntilExpiry).toBeGreaterThanOrEqual(14);
}
});
it('should return error for invalid PEM', () => {
const result = parseCertificateInfo(PEMCertificate('not a cert'));
expect(result.ok).toBe(false);
if (result.ok === false) {
expect(result.error).toContain('Failed to parse certificate info');
}
});
it('should compute SHA-256 fingerprint', () => {
const result = parseCertificateInfo(validPEM);
expect(result.ok).toBe(true);
if (result.ok) {
// Fingerprint should be uppercase hex
expect(result.value.fingerprint).toMatch(/^[0-9A-F]+$/);
expect(result.value.fingerprint.length).toBe(64); // SHA-256 = 32 bytes = 64 hex chars
}
});
});
describe('isCertificateExpiringSoon', () => {
it('should return true when within threshold', () => {
const result = parseCertificateInfo(expiringSoonPEM);
expect(result.ok).toBe(true);
if (result.ok) {
expect(isCertificateExpiringSoon(result.value, 30)).toBe(true);
}
});
it('should return false when not within threshold', () => {
const result = parseCertificateInfo(validPEM);
expect(result.ok).toBe(true);
if (result.ok) {
expect(isCertificateExpiringSoon(result.value, 30)).toBe(false);
}
});
it('should return false for expired certificate', () => {
const result = parseCertificateInfo(expiredPEM);
expect(result.ok).toBe(true);
if (result.ok) {
expect(isCertificateExpiringSoon(result.value, 30)).toBe(false);
}
});
it('should work with custom thresholds', () => {
const result = parseCertificateInfo(expiringSoonPEM);
expect(result.ok).toBe(true);
if (result.ok) {
// 15-day cert should be within 20-day threshold
expect(isCertificateExpiringSoon(result.value, 20)).toBe(true);
// But not within 10-day threshold
expect(isCertificateExpiringSoon(result.value, 10)).toBe(false);
}
});
});
});