Files
intelligence-terminal/test/security-profile.test.mjs
MrSphay 0c7ddc53b4
All checks were successful
Codex Template Compliance / template-compliance (pull_request) Successful in 7s
Build / test-and-image (pull_request) Successful in 52s
feat: add privacy-aware security manager onboarding
2026-07-05 21:39:55 +02:00

83 lines
3.5 KiB
JavaScript

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);
});