141 lines
8.1 KiB
TypeScript
141 lines
8.1 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import type { UnifiedResultProjection } from '../../src/domain/unified-result';
|
|
import { aiOverviewInputJson, complianceAiExportJson, resultsToPrivacySafeExportJson, type ComplianceAiExport } from '../../src/compliance/privacy-safe-export';
|
|
import { buildRegulatoryFindingQuery } from '../../src/compliance/regulatory-query';
|
|
|
|
function result(overrides: Partial<UnifiedResultProjection>): UnifiedResultProjection {
|
|
return {
|
|
sourceKind: 'scanner-finding', title: 'Synthetic result', severityNormalized: 'medium',
|
|
outcomeNormalized: 'pass', resultType: 'exposure_finding', isIoe: false, isDeviation: false,
|
|
sourceTool: 'synthetic-tool', ...overrides
|
|
} as UnifiedResultProjection;
|
|
}
|
|
|
|
describe('Result DB compact privacy-safe JSON export', () => {
|
|
it('uses source rule IDs and includes every available actionable narrative', () => {
|
|
const text = resultsToPrivacySafeExportJson([
|
|
result({
|
|
sourceRuleId: 'PK-CRITICAL-1',
|
|
title: 'Critical deviation', description: 'Important evidence', recommendation: 'Urgent remediation',
|
|
severityNormalized: 'critical', outcomeNormalized: 'fail', isIoe: true, isDeviation: true,
|
|
affectedAsset: 'PRIVATE ASSET', reviewOwner: 'PRIVATE OWNER', processingStatus: 'current', reviewStatus: 'open'
|
|
}),
|
|
result({
|
|
sourceRuleId: 'PK-MEDIUM-1', title: 'Medium deviation', description: 'Medium evidence',
|
|
recommendation: 'Medium remediation', outcomeNormalized: 'warning', isDeviation: true
|
|
}),
|
|
result({ title: 'Passed control' }),
|
|
result({
|
|
sourceRuleId: 'PK-NOT-RUN-1', title: 'Unavailable control for alex@example.test',
|
|
outcomeNormalized: 'not_run', severityNormalized: 'unknown'
|
|
})
|
|
]);
|
|
const copied = JSON.parse(text);
|
|
|
|
expect(copied.schemaVersion).toBe(1);
|
|
expect(copied.summary).toMatchObject({
|
|
totalResults: 4, findingResults: 4, environmentResults: 0, actionableFindings: 2, notRunChecks: 1,
|
|
findingOutcomes: { fail: 1, warning: 1, pass: 1, not_run: 1 }
|
|
});
|
|
expect(copied.findings).toEqual([{
|
|
id: 'PK-CRITICAL-1', title: 'Critical deviation', description: 'Important evidence', recommendation: 'Urgent remediation',
|
|
severity: 'critical', outcome: 'fail', resultType: 'exposure_finding', isIoe: true, deviation: true, tool: 'synthetic-tool'
|
|
}, {
|
|
id: 'PK-MEDIUM-1', title: 'Medium deviation', description: 'Medium evidence', recommendation: 'Medium remediation',
|
|
severity: 'medium', outcome: 'warning',
|
|
resultType: 'exposure_finding', isIoe: false, deviation: true, tool: 'synthetic-tool'
|
|
}]);
|
|
expect(copied.notRunChecks).toEqual([{
|
|
id: 'PK-NOT-RUN-1', title: 'Unavailable control for [redacted-email]', severity: 'unknown', resultType: 'exposure_finding', tool: 'synthetic-tool'
|
|
}]);
|
|
expect(text).not.toMatch(/Passed control|PRIVATE|alex@example\.test/);
|
|
});
|
|
|
|
it('uses null when the source has no rule ID instead of inventing one', () => {
|
|
const copied = JSON.parse(resultsToPrivacySafeExportJson([
|
|
result({ title: 'Unidentified deviation', outcomeNormalized: 'fail', isDeviation: true })
|
|
]));
|
|
|
|
expect(copied.findings[0].id).toBeNull();
|
|
});
|
|
|
|
it('adds the GPO comparison to a policy finding without the customer value', () => {
|
|
const copied = JSON.parse(resultsToPrivacySafeExportJson([
|
|
result({
|
|
sourceKind: 'policy-evaluation', sourceTool: 'GPOs', title: 'Synthetic policy mode',
|
|
sourceRuleId: 'registry:machine:software\\policies\\synthetic:mode',
|
|
outcomeNormalized: 'fail', isDeviation: true, policyClassification: 'misconfigured',
|
|
policyBaselineLabel: 'DUMMY Microsoft Security Baseline Windows Server 2022 v1',
|
|
policyBaselineValue: { operation: 'set-value', data: { kind: 'dword', value: 1 } },
|
|
policyActualValue: { operation: 'set-value', data: { kind: 'dword', value: 0 }, principals: ['DUMMY\\named.user'] },
|
|
policyComparisonRelation: 'different', policyCustomerState: 'configured',
|
|
policyScope: 'computer', policyDeliveryMechanism: 'registry-policy',
|
|
policyGpoId: '{A1B2C3D4-1111-2222-3333-444455556666}', policyIdentityKey: 'private-policy-hash'
|
|
}),
|
|
result({ sourceKind: 'policy-evaluation', sourceTool: 'GPOs', title: 'Passed policy', policyClassification: 'match' }),
|
|
result({ sourceKind: 'policy-observation', sourceTool: 'GPOs', title: 'Raw observation', policyValues: ['PRIVATE'] })
|
|
]));
|
|
|
|
expect(copied.summary).toMatchObject({
|
|
totalResults: 3, findingResults: 2, policyObservationResults: 1, actionableFindings: 1,
|
|
policyStatuses: { Deviation: 1, Passed: 1 }
|
|
});
|
|
expect(copied.findings).toHaveLength(1);
|
|
expect(copied.findings[0].policy).toEqual({
|
|
status: 'Deviation', baseline: 'Microsoft Security Baseline - Windows Server',
|
|
expectedValue: { operation: 'set-value', data: { kind: 'dword', value: 1 } },
|
|
comparison: 'different', customerState: 'configured', scope: 'computer', delivery: 'registry-policy'
|
|
});
|
|
expect(JSON.stringify(copied)).not.toMatch(/named\.user|A1B2C3D4|private-policy-hash|PRIVATE|"value":0/);
|
|
});
|
|
|
|
it('groups approved environment facts and excludes identities and arbitrary values', () => {
|
|
const os = result({
|
|
sourceKind: 'environment-observation', title: 'Operating System', environmentState: 'observed',
|
|
environmentProperty: 'operating_system.name', environmentValueType: 'string',
|
|
environmentSubjectIdentity: 'PRIVATE COMPUTER', environmentNormalizedValue: 'Windows Server 2022'
|
|
});
|
|
const privateDomain = result({
|
|
sourceKind: 'environment-observation', title: 'Domain', environmentState: 'observed',
|
|
environmentProperty: 'domain.fqdn', environmentValueType: 'string', environmentNormalizedValue: 'PRIVATE.EXAMPLE'
|
|
});
|
|
const copied = JSON.parse(resultsToPrivacySafeExportJson([os, os, privateDomain]));
|
|
|
|
expect(copied.summary).toMatchObject({ totalResults: 3, findingResults: 0, environmentResults: 3 });
|
|
expect(copied.environmentFacts).toEqual([{
|
|
title: 'Operating System', property: 'operating_system.name', value: 'Windows Server 2022',
|
|
state: 'observed', tool: 'synthetic-tool', count: 2
|
|
}]);
|
|
expect(JSON.stringify(copied)).not.toMatch(/PRIVATE/);
|
|
});
|
|
});
|
|
|
|
describe('Compliance Engine AI export', () => {
|
|
const gpo = (id: string) => result({
|
|
resultProjectionId: `policy-evaluation-comparison:${id}`, sourceResultId: `comparison-${id}`, sourceKind: 'policy-evaluation',
|
|
sourceTool: 'GPOs', title: 'System Access · MinimumPasswordLength', isDeviation: true, outcomeNormalized: 'fail',
|
|
policyScope: 'computer', policyDeliveryMechanism: 'security-template', policyCustomerState: 'configured',
|
|
policyComparisonRelation: 'different', policyBaselineLabel: 'Microsoft Security Baseline - Windows Server',
|
|
policyBaselineValue: { kind: 'integer', value: '14' }
|
|
});
|
|
const scanner = result({
|
|
resultProjectionId: 'scanner-finding:one', sourceResultId: 'one', sourceRuleId: 'PK-1', title: 'Scanner deviation',
|
|
description: 'Scanner evidence', outcomeNormalized: 'fail', isDeviation: true
|
|
});
|
|
|
|
it('contains exactly the AI overview input and every ISO query the model receives', () => {
|
|
const items = [gpo('b'), scanner, gpo('a')];
|
|
const exported = JSON.parse(complianceAiExportJson(items)) as ComplianceAiExport;
|
|
expect(exported.schemaVersion).toBe(2);
|
|
// The AI overview hashes and sends this same JSON; the order of the rows does not change it.
|
|
expect(exported.aiOverviewInput).toEqual(JSON.parse(aiOverviewInputJson(items)));
|
|
expect(aiOverviewInputJson([...items].reverse())).toBe(aiOverviewInputJson(items));
|
|
// The two baseline comparisons of one GPO setting produce one query, as the model receives it.
|
|
expect(exported.isoMappingQueries).toEqual([
|
|
{ query: buildRegulatoryFindingQuery(gpo('a')), resultProjectionIds: ['policy-evaluation-comparison:a', 'policy-evaluation-comparison:b'] },
|
|
{ query: buildRegulatoryFindingQuery(scanner), resultProjectionIds: ['scanner-finding:one'] }
|
|
]);
|
|
expect(exported.isoMappingQueries[0].query.query).toContain('Baseline recommended value: 14.');
|
|
});
|
|
});
|