import { renderToStaticMarkup } from 'react-dom/server'; import { describe, expect, it } from 'vitest'; import type { UnifiedResultProjection } from '../../src/domain/unified-result'; import type { JsonValue } from '../../src/domain/canonical-model'; import type { GpoPolicyValue } from '../../src/domain/gpo-policy'; import type { PolicyBaselineVariantAssessment } from '../../src/domain/policy-evaluation'; import { policySourceEvidence, policySourceGroups, policySourceValueLines } from '../../src/profiles/group-policy/source-evidence'; import { PolicySourceEvidence } from '../../src/features/finding-reader/PolicySourceEvidence'; function source(id: string, backupId: string | undefined, sequence?: number): UnifiedResultProjection { return { sourceResultId: id, projectId: 'synthetic', assessmentId: 'slot', policyBackupInstanceId: backupId, policyGpoId: backupId ? `gpo-${backupId}` : undefined, policyNativeBackupId: backupId ? `native-${backupId}` : undefined, sourceLocator: { objectPath: 'C:/Private/{1667E9C3-865B-4ABA-A288-71C8A18AD5E0}/DomainSysvol/GPO/Machine/registry.pol#records=' + sequence }, policySourceSequence: sequence, policyValues: [{ kind: 'registry-policy', operation: 'set-value', key: 'Software\\Policies\\Synthetic', valueName: 'Mode', typeCode: 4, data: { kind: 'uint32', value: 1 } }] } as unknown as UnifiedResultProjection; } describe('Original GPO evidence attribution', () => { it('counts backup identities, not observations; retains source order only within each GPO file', () => { const original = [source('later', 'a', 9), source('other', 'b', 0), source('earlier', 'a', 0)]; const before = JSON.stringify(original); const evidence = policySourceEvidence(original); const groups = policySourceGroups(evidence); expect(groups).toHaveLength(2); expect(groups[0].observations.map(item => item.id)).toEqual([evidence[2].id, evidence[0].id]); expect(groups[0]).toMatchObject({ label: 'GPO gpo-a', backupLabel: 'Backup: native-a' }); expect(policySourceGroups(evidence)).toBe(groups); expect(policySourceGroups(policySourceEvidence(original.slice(0, 1)))).toHaveLength(1); expect(JSON.stringify(original)).toBe(before); const html = renderToStaticMarkup(); expect(html).toContain('2 GPO backup sources'); expect(html).toContain('
'); expect((html.match(/
); expect(html).toContain('2 GPO backup sources ยท 2 unassigned'); }); it('distinguishes delete, typed empty strings, whitespace, zero and unsupported instructions as inert original values', () => { const values: JsonValue[] = [ { kind: 'registry-policy', operation: 'delete-value', key: 'Software\\Policies\\Synthetic', valueName: 'Mode' }, { kind: 'registry-policy', operation: 'set-value', key: 'Software\\Policies\\Synthetic', typeCode: 1, data: { kind: 'string', value: '' } }, { kind: 'registry-policy', operation: 'set-value', typeCode: 1, data: { kind: 'string', value: ' ' } }, { kind: 'registry-policy', operation: 'set-value', typeCode: 4, data: { kind: 'uint32', value: 0 } }, { kind: 'registry-policy', operation: 'unsupported-special', targets: [''] } ]; const before = JSON.stringify(values); expect(policySourceValueLines(values[0]).filter(line => line.emphasized)).toEqual([{ text: 'Operation: delete-value', emphasized: true }]); expect(policySourceValueLines(values[1])).toContainEqual({ text: 'Data: ""', emphasized: true }); expect(policySourceValueLines(values[2])).toContainEqual({ text: 'Data: " "', emphasized: true }); expect(policySourceValueLines(values[3])).toContainEqual({ text: 'Data: 0', emphasized: true }); const evidence = policySourceEvidence([{ ...source('unsafe', 'a'), policyValues: values }]); const html = renderToStaticMarkup(); expect(html).toContain('<img'); expect(html).not.toContain('Data: 0'); expect(html).not.toContain('Not set'); expect(JSON.stringify(values)).toBe(before); }); it('marks a source only when the retained current baseline variants prove its individual relation', () => { const set = source('set', 'set-backup', 1); const deleted = { ...source('delete', 'delete-backup', 2), policyValues: [{ kind: 'registry-policy', operation: 'delete-value', key: 'Software\\Policies\\Synthetic', valueName: 'Mode' }] } as UnifiedResultProjection; const assessment = { version: 1, scope: 'backup-configuration', decision: 'all-fail', actualValues: [...set.policyValues!, ...deleted.policyValues!] as GpoPolicyValue[], variants: [{ settingId: 'baseline', baselineValues: [{ kind: 'registry-policy', operation: 'set-value', key: 'Software\\Policies\\Synthetic', valueName: 'Mode', typeCode: 4, data: { kind: 'uint32', value: 1 } }], comparator: { version: 1, comparator: { kind: 'equality' }, relation: 'different', baseline: { kind: 'integer', value: '1' }, diagnosticCodes: [] } }], diagnosticCodes: [] } as PolicyBaselineVariantAssessment; const evidence = policySourceEvidence([set, deleted], assessment); const html = renderToStaticMarkup(); expect(evidence.map(item => item.baselineRelation)).toEqual(['aligned', 'conflicting']); expect(html).toContain('data-source-relation="aligned"'); expect(html).toContain('data-source-relation="conflicting"'); expect(html).toContain('class="policy-source-group is-conflicting"'); expect(evidence[1].deviationFields).toEqual([['operation']]); expect(html).toContain('class="policy-source-deviation-value">Operation: delete-value'); expect((html.match(/policy-source-deviation-value/g) ?? [])).toHaveLength(1); expect(html).not.toContain('Conflicting contribution'); expect(html).toContain('Type code: 4'); expect(html).toContain('Operation: delete-value'); const wrongType = { ...source('wrong-type', 'wrong-type-backup', 3), policyValues: [{ kind: 'registry-policy', operation: 'set-value', key: 'Software\\Policies\\Synthetic', valueName: 'Mode', typeCode: 1, data: { kind: 'uint32', value: 1 } }] } as UnifiedResultProjection; const typeEvidence = policySourceEvidence([wrongType], assessment); expect(typeEvidence[0].deviationFields).toEqual([['typeCode']]); expect(renderToStaticMarkup()) .toContain('class="policy-source-deviation-value">Type code: 1 (REG_SZ)'); const wrongDataAndType = { ...source('wrong-data-type', 'wrong-data-type-backup', 4), policyValues: [{ kind: 'registry-policy', operation: 'set-value', key: 'Software\\Policies\\Synthetic', valueName: 'Mode', typeCode: 1, data: { kind: 'uint32', value: 0 } }] } as UnifiedResultProjection; const combinedEvidence = policySourceEvidence([wrongDataAndType], assessment); expect(combinedEvidence[0].deviationFields).toEqual([['data', 'typeCode']]); const combinedHtml = renderToStaticMarkup(); expect(combinedHtml).toContain('class="policy-source-deviation-value">Data: 0'); expect(combinedHtml).toContain('class="policy-source-deviation-value">Type code: 1 (REG_SZ)'); const scalarSource = { ...source('scalar', 'scalar-backup', 5), policyValues: [{ kind: 'scalar', raw: 'disabled' }] } as UnifiedResultProjection; const scalarAssessment = { ...assessment, actualValues: scalarSource.policyValues, variants: [{ ...assessment.variants[0], baselineValues: [{ kind: 'scalar', raw: 'enabled' }] }] } as PolicyBaselineVariantAssessment; const scalarEvidence = policySourceEvidence([scalarSource], scalarAssessment); expect(scalarEvidence[0].deviationFields).toEqual([['value']]); expect(renderToStaticMarkup()) .toContain('class="policy-source-deviation-value">Raw: disabled'); const principalSource = { ...source('principals', 'principal-backup', 6), policyValues: [{ kind: 'principal-set', principals: ['principal-extra-a', 'principal-extra-b', 'principal-required'] }] } as UnifiedResultProjection; const principalAssessment = { ...assessment, actualValues: principalSource.policyValues, variants: [{ ...assessment.variants[0], baselineValues: [{ kind: 'principal-set', principals: ['principal-required'] }] }] } as PolicyBaselineVariantAssessment; const principalEvidence = policySourceEvidence([principalSource], principalAssessment); expect(principalEvidence[0].deviationFields).toBeUndefined(); expect(principalEvidence[0].deviationItems).toEqual([['principal-extra-a', 'principal-extra-b']]); const principalHtml = renderToStaticMarkup(); expect((principalHtml.match(/policy-source-deviation-value/g) ?? [])).toHaveLength(2); expect(principalHtml).toContain('class="policy-source-deviation-value"> principal-extra-a'); expect(principalHtml).toContain('class="policy-source-deviation-value"> principal-extra-b'); expect(principalHtml).not.toContain('class="policy-source-deviation-value"> principal-required'); const missingOnlyAssessment = { ...principalAssessment, variants: [{ ...principalAssessment.variants[0], baselineValues: [{ kind: 'principal-set', principals: ['principal-extra-a', 'principal-extra-b', 'principal-required', 'principal-missing'] }] }] } as PolicyBaselineVariantAssessment; const missingOnlyEvidence = policySourceEvidence([principalSource], missingOnlyAssessment); expect(missingOnlyEvidence[0].deviationFields).toBeUndefined(); expect(missingOnlyEvidence[0].deviationItems).toBeUndefined(); expect(renderToStaticMarkup()) .not.toContain('policy-source-deviation-value'); expect(policySourceEvidence([set], undefined)[0].baselineRelation).toBeUndefined(); }); });