155 lines
10 KiB
TypeScript
155 lines
10 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import type { UnifiedResultProjection } from '../../src/domain/unified-result';
|
|
import type { ImportedDataset } from '../../src/domain/dataset';
|
|
import type { PolicyEvaluationResult, StoredPolicyEvaluation } from '../../src/domain/policy-evaluation';
|
|
import { makeSheet } from '../../src/importers/sheet-factory';
|
|
import { appendPolicyObservationsToDataset } from '../../src/features/project-import/policy-observations-to-dataset';
|
|
import { buildPolicyWorkResults, countPolicyWorkResults, policyMisconfigurationImpact } from '../../src/evaluation/policy-work-results';
|
|
import { genericProfile } from '../../src/profiles/generic/generic-profile';
|
|
import { outcomeForStatusValue, toolIdForRecord } from '../../src/importers/status-normalization';
|
|
|
|
function source(id: string, value = 1, patch: Partial<UnifiedResultProjection> = {}): UnifiedResultProjection {
|
|
return {
|
|
sourceKind: 'policy-observation', sourceResultId: id, resultProjectionId: 'policy-observation:' + id,
|
|
projectId: 'synthetic', assessmentId: 'slot', title: 'Mode', importedAt: '2026-09-06T00:00:00Z',
|
|
policyScope: 'computer', policyDeliveryMechanism: 'registry-policy', policyState: 'configured',
|
|
policyIdentityStatus: 'resolved', policyIdentityKey: 'identity-mode', policyBackupInstanceId: 'backup-' + id,
|
|
policyValues: [{ kind: 'registry-policy', key: 'Software\\Policies\\Synthetic', valueName: 'Mode',
|
|
operation: 'set-value', typeCode: 4, data: { kind: 'uint32', value } }], ...patch
|
|
} as UnifiedResultProjection;
|
|
}
|
|
function comparison(id: string, ids: string[], patch: Partial<PolicyEvaluationResult> = {}): PolicyEvaluationResult {
|
|
return {
|
|
id, observationIds: ids, baselineId: 'ms-baseline', baselineLabel: 'Synthetic Microsoft Baseline',
|
|
baselineState: 'explicit-recommendation', classification: 'configured_match', customerState: 'configured',
|
|
comparator: { version: 1, comparator: { kind: 'equality' }, relation: 'match',
|
|
actual: { kind: 'integer', value: '1' }, baseline: { kind: 'integer', value: '1' }, diagnosticCodes: [] },
|
|
identity: { id: 'identity-mode', scope: 'computer', target: {
|
|
kind: 'registry-value', hive: 'HKEY_LOCAL_MACHINE', key: 'Software\\Policies\\Synthetic', valueName: 'Mode'
|
|
} }, diagnosticCodes: [], ...patch
|
|
} as PolicyEvaluationResult;
|
|
}
|
|
const run = (results: PolicyEvaluationResult[]) => ({
|
|
run: { completedAt: '2026-09-06T00:00:00Z' }, results
|
|
}) as StoredPolicyEvaluation;
|
|
const statuses = (sources: UnifiedResultProjection[], comparisons: PolicyEvaluationResult[] = []) =>
|
|
buildPolicyWorkResults(sources, [run(comparisons)]).map(result => result.status);
|
|
|
|
describe('Policy Analyzer outcome contract in Work', () => {
|
|
it('omits unevaluated drive maps and unsupported comparisons', () => {
|
|
const drive = source('drive', 1, { policyDeliveryMechanism: 'group-policy-preference', policyIdentityStatus: 'unresolved' });
|
|
expect(appendPolicyObservationsToDataset(null, 'Synthetic', [drive])).toBeNull();
|
|
expect(statuses([drive, { ...drive, sourceResultId: 'drive2' }])).toEqual([]);
|
|
expect(statuses([source('a')], [comparison('r', ['a'], { classification: 'review_required' })])).toEqual([]);
|
|
expect(statuses([source('a')])).toEqual([]);
|
|
});
|
|
|
|
it('requires an explicit nonempty baseline and retains explicit zero and empty principal sets', () => {
|
|
const good = comparison('r', ['a']);
|
|
for (const baseline of [undefined, { kind: 'none' }, { kind: 'text', value: '' }] as const) {
|
|
expect(statuses([source('a')], [{ ...good, comparator: { ...good.comparator, baseline } }])).toEqual([]);
|
|
}
|
|
expect(statuses([source('a')], [{ ...good, baselineState: 'catalog-gap' }])).toEqual([]);
|
|
expect(statuses([source('a')], [{ ...good, comparator: { ...good.comparator, baseline: { kind: 'integer', value: '0' } } }])).toEqual(['Pass']);
|
|
expect(statuses([source('a')], [{ ...good, comparator: { ...good.comparator, baseline: { kind: 'string-set', values: [] } } }])).toEqual(['Pass']);
|
|
});
|
|
|
|
it('shows one Pass/Fail per comparison with all sources and one Duplicate group', () => {
|
|
const pass = comparison('pass', ['a', 'b']);
|
|
const fail = comparison('fail', ['a', 'b'], { baselineLabel: 'Another baseline', classification: 'confirmed_configured_deviation',
|
|
comparator: { ...pass.comparator, relation: 'different', baseline: { kind: 'integer', value: '2' } } });
|
|
const results = buildPolicyWorkResults([source('a'), source('b')], [run([pass, fail, pass])]);
|
|
expect(countPolicyWorkResults(results)).toEqual({ Fail: 1, Pass: 1, Conflicts: 0, Duplicates: 1, 'Not configured': 0 });
|
|
expect(policyMisconfigurationImpact([...results, ...results])).toEqual({ settings: 1, backups: 2 });
|
|
expect(results.find(result => result.status === 'Pass')?.sources).toHaveLength(2);
|
|
});
|
|
|
|
it('keeps baseline-only missing settings separate from Fail and Deviation', () => {
|
|
const missing = comparison('missing', [], { classification: 'confirmed_configured_deviation', customerState: 'missing' });
|
|
missing.comparator = { ...missing.comparator, actual: undefined, relation: 'different' };
|
|
const dataset = appendPolicyObservationsToDataset(null, 'Synthetic', [], [run([missing])])!;
|
|
const [headers, row] = dataset.sheets[0].rows;
|
|
expect(row[headers.indexOf('Status')]).toBe('Not configured');
|
|
expect(row[headers.indexOf('Configured Value')]).toBe('Not configured');
|
|
const record = dataset.sheets[0].analysis.records[0];
|
|
expect(genericProfile.matchesStatus(record, 'deviation')).toBe(false);
|
|
expect(genericProfile.matchesStatus(record, 'not-configured')).toBe(true);
|
|
expect(genericProfile.matchesStatus(record, 'all')).toBe(true);
|
|
expect(outcomeForStatusValue('group-policy', 'Not configured')).toBe('informational');
|
|
});
|
|
|
|
it('keeps only configured mismatches in Deviation even when other fields contain Fail or Open', () => {
|
|
for (const status of ['Pass', 'Not configured', 'Conflicts', 'Duplicates']) {
|
|
const sheet = makeSheet('GPOs', [['Category', 'Status', 'Description'], ['GPOs', status, 'Fail']], '', 'Synthetic');
|
|
expect(genericProfile.matchesStatus(sheet.analysis.records[0], 'deviation')).toBe(false);
|
|
}
|
|
const fail = makeSheet('GPOs', [['Category', 'Status', 'Configured Value'], ['GPOs', 'Fail', '0']], '', 'Synthetic');
|
|
expect(genericProfile.matchesStatus(fail.analysis.records[0], 'deviation')).toBe(true);
|
|
});
|
|
|
|
it('classifies different values for one target as Conflicts without guessing a winning GPO', () => {
|
|
expect(statuses([source('a', 0), source('b', 1)], [comparison('r', ['a', 'b'])])).toEqual(['Conflicts']);
|
|
});
|
|
|
|
it('compares registry address case-insensitively while preserving value data and type', () => {
|
|
const lower = source('b', 1, { policyValues: [{ kind: 'registry-policy', key: 'software\\policies\\synthetic',
|
|
valueName: 'MODE', operation: 'set-value', typeCode: 4, data: { kind: 'uint32', value: 1 } }] });
|
|
expect(statuses([source('a'), lower])).toEqual(['Duplicates']);
|
|
const text = source('b', 1, { policyValues: [{ kind: 'registry-policy', key: 'Software\\Policies\\Synthetic',
|
|
valueName: 'Mode', operation: 'set-value', typeCode: 1, data: { kind: 'string', value: '1' } }] });
|
|
expect(statuses([source('a'), text])).toEqual(['Conflicts']);
|
|
const expanded = { ...text, sourceResultId: 'c', policyValues: [{ ...(text.policyValues![0] as object), typeCode: 2 }] } as UnifiedResultProjection;
|
|
expect(statuses([text, expanded])).toEqual(['Conflicts']);
|
|
});
|
|
|
|
it('keeps distinct value names, user/computer scopes, and timeslots separate', () => {
|
|
const otherName = source('b', 1, { policyValues: [{ kind: 'registry-policy', key: 'Software\\Policies\\Synthetic',
|
|
valueName: 'Another', operation: 'set-value', typeCode: 4, data: { kind: 'uint32', value: 1 } }] });
|
|
expect(statuses([source('a'), otherName])).toEqual([]);
|
|
expect(statuses([source('a'), source('b', 1, { policyScope: 'user' })])).toEqual([]);
|
|
expect(statuses([source('a'), source('b', 1, { assessmentId: 'another-slot' })])).toEqual([]);
|
|
expect(statuses([source('a')], [comparison('mixed', ['a', 'absent-source'])])).toEqual([]);
|
|
});
|
|
|
|
it('deduplicates repeated read rows without counting baseline comparisons as duplicate customer settings', () => {
|
|
const a = source('a');
|
|
expect(statuses([a, a])).toEqual([]);
|
|
expect(statuses([a], [comparison('r1', ['a']), comparison('r2', ['a'])])).toEqual(['Pass', 'Pass']);
|
|
});
|
|
|
|
it('surfaces explicitly conflicting baseline recommendations', () => {
|
|
expect(statuses([source('a')], [comparison('r', ['a'], { classification: 'review_required',
|
|
diagnosticCodes: ['policy-evaluation-baseline-consensus-conflict'] })])).toEqual(['Conflicts']);
|
|
});
|
|
|
|
it('does not repeat stale aggregation conflicts when the source evidence proves identical values', () => {
|
|
expect(statuses([source('a'), source('b')], [comparison('old', ['a', 'b'], {
|
|
classification: 'review_required', customerState: 'conflicted'
|
|
})])).toEqual(['Duplicates']);
|
|
});
|
|
|
|
it('handles a 2300-GPO source set with one deterministic row per duplicate group', () => {
|
|
const sources = Array.from({ length: 2300 }, (_, index) => source(String(index)));
|
|
const result = buildPolicyWorkResults(sources, []);
|
|
expect(result).toHaveLength(1);
|
|
expect(result[0].status).toBe('Duplicates');
|
|
expect(result[0].sources).toHaveLength(2300);
|
|
});
|
|
|
|
it('preserves scanner sheets and exposes GPO warning statuses in the project aggregate', () => {
|
|
const aggregate = makeSheet('All Records', [['Source: Scan', 'Title', 'Status'],
|
|
['PurpleKnight · 2026-09-06', 'Synthetic scanner finding', 'Failed']], '', 'Synthetic', { isProjectSheet: true });
|
|
const scanner = makeSheet('Scanner', [['Title'], ['Synthetic']], '', 'Synthetic', { toolId: 'purple-knight' });
|
|
const dataset = { fileName: 'Synthetic', fileSize: 0, lastModified: 0, format: 'Project', rawText: '',
|
|
sheets: [aggregate, scanner], profileId: 'generic' } as ImportedDataset;
|
|
const result = appendPolicyObservationsToDataset(dataset, 'Synthetic', [source('a'), source('b')])!;
|
|
expect(result.sheets).toHaveLength(3);
|
|
expect(result.sheets[2]).toBe(scanner);
|
|
const record = result.sheets[0].analysis.records.find(record => record.fields.some(field => field.value === 'Duplicates'))!;
|
|
expect(toolIdForRecord(record, 'generic')).toBe('group-policy');
|
|
expect(outcomeForStatusValue('group-policy', 'Duplicates')).toBe('warning');
|
|
expect(outcomeForStatusValue('group-policy', 'Conflicts')).toBe('warning');
|
|
expect(appendPolicyObservationsToDataset(dataset, 'Synthetic', [source('a')])).toBe(dataset);
|
|
});
|
|
});
|