import test from 'node:test'; import assert from 'node:assert/strict'; import { mkdtempSync, readFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { SecurityProfileStore, sanitizeSecurityProfile } from '../lib/security/security-profile-store.mjs'; import { createTerminalToolRegistry } from '../lib/agent/terminal-tools.mjs'; const SECRET = 'test-only-security-profile-key-at-least-32-chars'; function profile() { return { language: 'de', preferredName: 'Test Operator', location: { country: 'Deutschland', region: 'NRW', city: 'Koeln' }, timezone: 'Europe/Berlin', household: { adults: 2, children: 1, pets: 1 }, mobility: ['car', 'rail'], riskPriorities: ['weather', 'cyber'], criticalDependencies: ['electricity', 'internet'], alertPreference: 'important', quietHours: '22:00-07:00', consentedAt: new Date().toISOString(), }; } test('security profile is encrypted at rest and reloads with the correct key', () => { const path = join(mkdtempSync(join(tmpdir(), 'security-profile-')), 'profile.enc'); const store = new SecurityProfileStore(path, SECRET).init(); store.save(profile()); const raw = readFileSync(path, 'utf8'); assert.equal(raw.includes('Test Operator'), false); assert.equal(raw.includes('Koeln'), false); assert.equal(JSON.parse(raw).algorithm, 'aes-256-gcm'); const restored = new SecurityProfileStore(path, SECRET).init(); assert.equal(restored.getProfile().preferredName, 'Test Operator'); assert.equal(restored.getProfile().location.city, 'Koeln'); }); test('wrong key fails closed and cannot overwrite an existing profile', () => { const path = join(mkdtempSync(join(tmpdir(), 'security-profile-')), 'profile.enc'); new SecurityProfileStore(path, SECRET).init().save(profile()); const wrong = new SecurityProfileStore(path, 'another-test-key-that-is-at-least-32-characters').init(); assert.equal(wrong.status().configured, true); assert.equal(wrong.status().available, false); assert.equal(wrong.getProfile(), null); assert.throws(() => wrong.save(profile())); }); test('profile sanitizer only retains allowlisted risk and dependency categories', () => { const value = sanitizeSecurityProfile({ ...profile(), riskPriorities: ['weather', 'passwords'], criticalDependencies: ['internet', 'bank-login'], household: { adults: 999, children: -4, pets: 2 }, }); assert.deepEqual(value.riskPriorities, ['weather']); assert.deepEqual(value.criticalDependencies, ['internet']); assert.deepEqual(value.household, { adults: 20, children: 0, pets: 2 }); }); test('agent can read only the approved profile through the allowlisted tool', async () => { const path = join(mkdtempSync(join(tmpdir(), 'security-profile-')), 'profile.enc'); const store = new SecurityProfileStore(path, SECRET).init(); store.save(profile()); const registry = createTerminalToolRegistry({ securityProfileStore: store }); const result = await registry.execute('get_security_profile'); assert.equal(result.available, true); assert.equal(result.profile.location.country, 'Deutschland'); assert.equal(Object.hasOwn(result.profile, 'completedAt'), false); }); test('deleting a security profile removes it from the store', () => { const path = join(mkdtempSync(join(tmpdir(), 'security-profile-')), 'profile.enc'); const store = new SecurityProfileStore(path, SECRET).init(); store.save(profile()); store.delete(); assert.equal(store.exists, false); assert.equal(store.getProfile(), null); });