import { describe, expect, it } from 'vitest'; import { resolveRegistryPolicyIdentity } from '../../server/gpo/admx-policy-catalog'; import { buildPolicySnapshotAggregation } from '../../server/gpo/gpo-policy-snapshot-service'; import type { StoredGpoBackupInstance, StoredGpoPolicyAnalysis } from '../../src/domain/gpo-analysis-persistence'; import type { GpoBackupInstance } from '../../src/domain/gpo-ingestion'; import type { GpoPolicyObservation, GpoRegistryPolicyValue } from '../../src/domain/gpo-policy'; import type { EnvironmentProfile } from '../../src/domain/gpo-snapshot-aggregation'; const timestamp = '2026-08-04T12:00:00.000Z'; const projectId = 'policy-snapshot-project'; const artifactId = 'policy-snapshot-artifact'; const processingRunId = 'policy-snapshot-processing'; const analysisRunId = 'policy-snapshot-analysis'; const registryKey = 'Software\\Policies\\Synthetic'; const profile: EnvironmentProfile = { version: 1, id: 'environment-profile-synthetic', projectId, name: 'Synthetic Windows environment', revision: 1, contexts: [{ version: 1, id: 'context-windows', kind: 'operating-system', productFamily: 'Windows Server', productVersion: '2022', role: 'Member Server', scope: 'computer' }], createdAt: timestamp }; function registryValue(operation: GpoRegistryPolicyValue['operation'], value?: number): GpoRegistryPolicyValue { return { kind: 'registry-policy', operation, key: registryKey, valueName: 'Mode', typeCode: value === undefined ? undefined : 4, data: value === undefined ? undefined : { kind: 'uint32', value } }; } function observation( backupInstanceId: string, id: string, sequence: number, value: GpoRegistryPolicyValue ): GpoPolicyObservation { return { version: 1, id, backupInstanceId, sourceArtifactId: artifactId, artifactId: 'registry-entry-' + backupInstanceId, sourcePath: backupInstanceId + '/DomainSysvol/GPO/Machine/Registry.pol', sourceSha256: 'a'.repeat(64), scope: 'computer', deliveryMechanism: 'registry-policy', parser: { id: 'registry-policy-preg', version: '1.0.0' }, reducer: { id: 'registry-policy-reducer', version: '1.0.0' }, identity: { status: 'unresolved', namespace: 'registry-policy', section: 'HKEY_LOCAL_MACHINE', key: registryKey + '\\Mode' }, state: 'instruction', values: [value], registryPolicy: { sequence, operation: value.operation }, sourceRecords: [{ index: sequence, byteOffset: 8 + sequence * 16 }], effectiveState: 'unknown', diagnosticCodes: [] }; } function backupInstance( id: string, gpoId: string, observations: GpoPolicyObservation[], expectedGpoCount: number, completeness: 'complete' | 'selected' = 'complete' ): StoredGpoBackupInstance { const instance: GpoBackupInstance = { version: 1, id, projectId, sourceArtifactId: artifactId, identity: { status: 'resolved', gpoId, backupId: gpoId.replace(/^(.{8})/, match => match === '11111111' ? 'aaaaaaaa' : 'bbbbbbbb'), evidence: 'backup-metadata' }, domain: { status: 'resolved', key: 'synthetic.example', evidence: 'backup-metadata' }, collection: { status: 'observed', collectedAt: timestamp, evidence: 'collection-manifest' }, backupSet: completeness === 'complete' ? { id: 'synthetic-set', scope: 'complete-domain-export', evidence: 'collection-manifest', expectedGpoCount } : { id: 'synthetic-set', scope: 'selected-gpos', evidence: 'user-selection' }, backupCreatedAt: timestamp, metadataStatus: 'complete', sections: { computer: 'present', user: 'disabled' }, inventory: { version: 1, status: 'complete', entries: [ { version: 1, artifactId: 'metadata-' + id, relativePath: id + '/Backup.xml', role: 'backup-metadata', scope: 'shared', disposition: 'supported', secretIndicatorStatus: 'not-inspected' }, { version: 1, artifactId: 'registry-entry-' + id, relativePath: id + '/DomainSysvol/GPO/Machine/Registry.pol', role: 'registry-policy', scope: 'computer', disposition: 'supported', secretIndicatorStatus: 'not-detected' } ], diagnosticCodes: [] }, policyObservations: observations, discoveredAt: timestamp }; return { version: 1, id: 'stored-' + id, projectId, artifactId, processingRunId, instance, createdAt: timestamp }; } function analysis(instances: StoredGpoBackupInstance[], unresolved = false): StoredGpoPolicyAnalysis { const observations = instances.flatMap(instance => instance.instance.policyObservations ?? []); const resolutions = observations.map(item => ({ version: 1 as const, id: 'resolution-' + item.id, projectId, analysisRunId, sourceProcessingRunId: processingRunId, backupInstanceId: item.backupInstanceId, observationId: item.id, resolution: unresolved ? { version: 1 as const, status: 'unresolved' as const, reasonCode: 'identity-resolver-synthetic-gap' } : resolveRegistryPolicyIdentity(item) })); return { run: { version: 1, id: analysisRunId, projectId, artifactId, sourceProcessingRunId: processingRunId, analyzer: { id: 'gpo-policy-identity-analysis', version: '2.0.0' }, identityResolver: { id: 'gpo-technical-registry-target', version: '1.0.0' }, status: unresolved ? 'partial' : 'complete', observationCount: resolutions.length, resolvedCount: unresolved ? 0 : resolutions.length, diagnosticCodes: unresolved ? ['identity-resolver-synthetic-gap'] : [], startedAt: timestamp, completedAt: timestamp }, resolutions }; } function aggregate(instances: StoredGpoBackupInstance[], unresolved = false) { return buildPolicySnapshotAggregation({ profile, analyses: [analysis(instances, unresolved)], instances, startedAt: timestamp, completedAt: timestamp }); } describe('policy snapshot aggregation', () => { it('applies registry instruction order and preserves a final delete without claiming effectiveness', () => { const id = 'gpo-one'; const values = [ observation(id, 'observation-set', 0, registryValue('set-value', 1)), observation(id, 'observation-delete', 1, registryValue('delete-value')) ]; const record = aggregate([ backupInstance(id, '11111111-1111-4111-8111-111111111111', values, 1) ]); expect(record.run).toMatchObject({ status: 'complete', conflictCount: 0, permitsFindingGeneration: false }); expect(record.states).toEqual([expect.objectContaining({ status: 'deleted', ordering: 'single-gpo', effectiveState: 'unknown', observationIds: ['observation-delete', 'observation-set'] })]); }); it('keeps identical cross-GPO values and reports differing values as an explicit conflict', () => { const first = backupInstance('gpo-one', '11111111-1111-4111-8111-111111111111', [ observation('gpo-one', 'observation-one', 0, registryValue('set-value', 1)) ], 2); const same = backupInstance('gpo-two', '22222222-2222-4222-8222-222222222222', [ observation('gpo-two', 'observation-two', 0, registryValue('set-value', 1)) ], 2); expect(aggregate([first, same]).states[0]).toMatchObject({ status: 'configured', ordering: 'cross-gpo-identical' }); const different = backupInstance('gpo-two', '22222222-2222-4222-8222-222222222222', [ observation('gpo-two', 'observation-two-different', 0, registryValue('set-value', 2)) ], 2); const conflict = aggregate([first, different]); expect(conflict.run).toMatchObject({ status: 'complete', conflictCount: 1, permitsFindingGeneration: false }); expect(conflict.states[0]).toMatchObject({ status: 'conflicted', ordering: 'cross-gpo-conflict' }); expect(conflict.reviewHints).toEqual(expect.arrayContaining([ expect.objectContaining({ code: 'policy-aggregation-cross-gpo-conflict' }) ])); }); it('keeps prior-state-dependent instructions indeterminate', () => { const record = aggregate([ backupInstance('gpo-one', '11111111-1111-4111-8111-111111111111', [ observation('gpo-one', 'observation-soft', 0, registryValue('set-value-if-absent', 1)) ], 1) ]); expect(record.run).toMatchObject({ status: 'partial', permitsFindingGeneration: false }); expect(record.states[0]).toMatchObject({ status: 'indeterminate', completeness: 'partial', diagnosticCodes: expect.arrayContaining(['policy-aggregation-prior-state-required']) }); }); it('turns incomplete snapshots and unresolved identities into review hints without states or findings', () => { const instance = backupInstance('gpo-one', '11111111-1111-4111-8111-111111111111', [ observation('gpo-one', 'observation-unresolved', 0, registryValue('set-value', 1)) ], 1, 'selected'); const record = aggregate([instance], true); expect(record.run).toMatchObject({ status: 'partial', stateCount: 0, unresolvedIdentityCount: 1, permitsFindingGeneration: false }); expect(record.reviewHints).toEqual(expect.arrayContaining([ expect.objectContaining({ code: 'policy-aggregation-snapshot-incomplete' }), expect.objectContaining({ code: 'identity-resolver-synthetic-gap' }) ])); }); });