krona-staff/tests/unit/security-boundaries.test.ts

58 lines
3.1 KiB
TypeScript

import { readFileSync } from 'node:fs';
import { describe, expect, it } from 'vitest';
describe('security boundary regressions', () => {
it('requires section-specific permissions before owner workbook writes', () => {
const source = readFileSync('src/operations.ts', 'utf8');
const permissionCheck = source.indexOf("requiredPermissions.add('staff:org_unit:write')");
const transaction = source.indexOf(
'const counts = await database.tenantTransaction',
permissionCheck,
);
expect(permissionCheck).toBeGreaterThan(0);
expect(source.slice(permissionCheck, transaction)).toContain('staff:employee:write');
expect(source.slice(permissionCheck, transaction)).toContain('staff:metric:write');
expect(source.slice(permissionCheck, transaction)).toContain('staff:shift:confirm');
expect(source.slice(permissionCheck, transaction)).toContain('staff:payout:write');
expect(source.slice(permissionCheck, transaction)).toContain('await auth.require');
});
it('does not grant broad authenticated-user access to state or IPC', () => {
const store = readFileSync('apps/KronaStaffTerminal/Services/TerminalStore.cs', 'utf8');
const ipc = readFileSync('apps/KronaStaffTerminal/Services/TerminalIpc.cs', 'utf8');
expect(store).not.toContain('WellKnownSidType.AuthenticatedUserSid');
expect(store).not.toContain('catch\n {\n Directory.CreateDirectory');
expect(ipc).not.toContain('WellKnownSidType.AuthenticatedUserSid');
expect(ipc).toContain('IpcRequestAuthenticator');
expect(store).toContain('return await InitializeFreshStateAsync()');
expect(store).toContain('FileShare.None');
expect(store).toContain('await SaveAsync(state)');
});
it('enforces active employee status in standalone token resolution', () => {
const portal = readFileSync('src/portal.ts', 'utf8');
expect(portal).toContain("employee.status = 'active'");
const patchRoute = portal.indexOf('api.patch<{ Params: { employeeId: string }');
const accessRoute = portal.indexOf("'/employees/:employeeId/access-token'", patchRoute);
expect(portal.slice(patchRoute, accessRoute)).toContain(
'DELETE FROM staff_standalone_employee_access',
);
});
it('versions payroll formulas independently per organization unit', () => {
const service = readFileSync('src/domain/payroll/service.ts', 'utf8');
const migration = readFileSync('migrations/0009_professions.up.sql', 'utf8');
expect(service.match(/org_unit_id IS NOT DISTINCT FROM \$4::varchar\(26\)/g)).toHaveLength(3);
expect(service).toContain('pg_advisory_xact_lock');
expect(migration).toContain('staff_pay_assignments_effective_version_idx');
});
it('uses stable external identities for repeatable workbook imports', () => {
const importer = readFileSync('scripts/import-owner-workbook.py', 'utf8');
const migration = readFileSync('migrations/0009_professions.up.sql', 'utf8');
expect(importer).toContain('xlsx:staff:');
expect(importer).not.toContain('enumerate(names');
expect(importer).not.toContain('{row}|{date_text}');
expect(migration).toContain('staff_employees_external_ref_idx');
});
});