import { randomUUID } from 'node:crypto'; import { afterEach, describe, expect, it } from 'vitest'; import { db } from '../src/db/knex.js'; import { getApplication, listApplications } from '../src/services/admin.js'; import { decryptField, encryptField, maskSecret, panLookup } from '../src/services/fieldCrypto.js'; import { readPngDataUrl } from '../src/services/png.js'; import { truncateTables } from './truncate.js'; const ONE_PIXEL = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=='; describe('field protection', () => { afterEach(async () => { await truncateTables(); }); it('encrypts and masks PAN and account number', async () => { const pan = encryptField('ABCDE1234F'); const account = encryptField('123456789012'); expect(pan.startsWith('enc:v1:')).toBe(true); expect(decryptField(pan)).toBe('ABCDE1234F'); expect(maskSecret('ABCDE1234F')).toBe('******234F'); expect(panLookup('abcde1234f')).toBe(panLookup('ABCDE1234F')); const [id] = await db('applications').insert({ public_id: randomUUID(), email: 'mask@example.com', status: 'SUBMITTED', full_name: 'Asha Rao', pan, pan_lookup: panLookup('ABCDE1234F'), account_number: account, }); const list = await listApplications({}); expect(list.items.find((item) => item.email === 'mask@example.com')).toMatchObject({ pan: '******234F', account_number: '******9012', }); const detail = await getApplication(id); expect(detail.pan).toBe('ABCDE1234F'); expect(detail.account_number).toBe('123456789012'); const found = await listApplications({ q: 'ABCDE1234F' }); expect(found.total).toBe(1); }); it('rejects a PNG that is too large in pixels', () => { const header = Buffer.alloc(24); header[0] = 0x89; header[1] = 0x50; header[2] = 0x4e; header[3] = 0x47; header.write('IHDR', 12, 'ascii'); header.writeUInt32BE(2001, 16); header.writeUInt32BE(1, 20); const image = `data:image/png;base64,${header.toString('base64')}`; expect(() => readPngDataUrl(image, 500 * 1024, 'too big')).toThrow(/2000 pixels/); expect(readPngDataUrl(ONE_PIXEL, 500 * 1024, 'too big').length).toBeGreaterThan(8); }); });