Files
TREK/server/tests/unit/nest/common/crypto/apiKeyCrypto.test.ts
T
Maurice 9bb5854dbb test(server): the suites left in tests/unit/services move next to the code they cover
src/services is gone, so the folder named a layer that no longer exists. The airtrail suites go to tests/unit/nest/integrations, the crypto, cookie, avatar, password policy, timezone and conflict helpers to tests/unit/nest/common, the kitinerary mapper to booking-import, the photo media suite to photos, the atlas bundle guard to tests/unit/nest and the two migration suites to tests/unit/db. Only the relative import paths change, and the format baseline follows the new paths.
2026-10-10 20:16:12 +02:00

89 lines
3.0 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import {
encrypt_api_key,
decrypt_api_key,
maybe_encrypt_api_key,
is_encrypted_api_key,
} from '../../../../../src/nest/common/crypto/apiKeyCrypto';
describe('apiKeyCrypto', () => {
const PLAINTEXT_KEY = 'my-secret-api-key-12345';
const ENC_PREFIX = 'enc:v1:';
// SEC-008 — Encrypted API keys not returned in plaintext
describe('encrypt_api_key', () => {
it('SEC-008: returns encrypted string with enc:v1: prefix', () => {
const encrypted = encrypt_api_key(PLAINTEXT_KEY);
expect(encrypted).toMatch(/^enc:v1:/);
});
it('different calls produce different ciphertext (random IV)', () => {
const enc1 = encrypt_api_key(PLAINTEXT_KEY);
const enc2 = encrypt_api_key(PLAINTEXT_KEY);
expect(enc1).not.toBe(enc2);
});
it('encrypted value does not contain the plaintext', () => {
const encrypted = encrypt_api_key(PLAINTEXT_KEY);
expect(encrypted).not.toContain(PLAINTEXT_KEY);
});
});
describe('decrypt_api_key', () => {
it('SEC-008: decrypts an encrypted key back to original', () => {
const encrypted = encrypt_api_key(PLAINTEXT_KEY);
const decrypted = decrypt_api_key(encrypted);
expect(decrypted).toBe(PLAINTEXT_KEY);
});
it('returns null for null input', () => {
expect(decrypt_api_key(null)).toBeNull();
});
it('returns null for empty string', () => {
expect(decrypt_api_key('')).toBeNull();
});
it('returns plaintext as-is if not prefixed (legacy)', () => {
expect(decrypt_api_key('plain-legacy-key')).toBe('plain-legacy-key');
});
it('returns null for tampered ciphertext', () => {
const encrypted = encrypt_api_key(PLAINTEXT_KEY);
const tampered = encrypted.replace(ENC_PREFIX, ENC_PREFIX) + 'TAMPER';
expect(decrypt_api_key(tampered)).toBeNull();
});
});
describe('maybe_encrypt_api_key', () => {
it('encrypts a new plaintext value', () => {
const result = maybe_encrypt_api_key('my-key');
expect(result).toMatch(/^enc:v1:/);
});
it('returns null for empty/falsy values', () => {
expect(maybe_encrypt_api_key('')).toBeNull();
expect(maybe_encrypt_api_key(null)).toBeNull();
expect(maybe_encrypt_api_key(undefined)).toBeNull();
});
it('returns already-encrypted value as-is (no double-encryption)', () => {
const encrypted = encrypt_api_key(PLAINTEXT_KEY);
const result = maybe_encrypt_api_key(encrypted);
expect(result).toBe(encrypted);
});
});
describe('is_encrypted_api_key', () => {
it('is true exactly for enc:v1: envelopes', () => {
expect(is_encrypted_api_key(encrypt_api_key('s'))).toBe(true);
expect(is_encrypted_api_key('enc:v1:AAAA')).toBe(true);
expect(is_encrypted_api_key('plaintext')).toBe(false);
expect(is_encrypted_api_key('')).toBe(false);
expect(is_encrypted_api_key(null)).toBe(false);
expect(is_encrypted_api_key(42)).toBe(false);
});
});
});