// @vitest-environment jsdom import { regulatoryMappingResultId } from '../../src/features/finding-reader/use-regulatory-mapping'; import { renderToStaticMarkup } from 'react-dom/server'; import { readFileSync } from 'node:fs'; import { describe, expect, it } from 'vitest'; import type { UnifiedResultProjection } from '../../src/domain/unified-result'; import { POLICY_EVALUATION_ENGINE, type PolicyEvaluationResult, type StoredPolicyEvaluation } from '../../src/domain/policy-evaluation'; import { appendPolicyObservationsToDataset } from '../../src/features/project-import/policy-observations-to-dataset'; import { FindingReader } from '../../src/features/finding-reader/FindingReader'; import { wellKnownPrincipalNames } from '../../src/profiles/group-policy/well-known-principals'; /** Synthetic privilege-right setting compared against several baseline packages. */ function fixture(packages: number[], configured = ['*S-1-5-32-544', '*S-1-5-32-549'], expectedFor: (index: number) => string[] = () => ['*S-1-5-32-544']) { const actual = { kind: 'principal-set', section: 'Privilege Rights', key: 'SeSyntheticPrivilege', principals: configured } as const; const observation = { sourceKind: 'policy-observation', sourceResultId: 'synthetic-observation', projectId: 'synthetic', assessmentId: 'slot', policyBackupInstanceId: 'synthetic-backup', policyIdentityKey: 'synthetic-right', policyIdentityStatus: 'resolved', policyScope: 'computer', policyState: 'configured', policyDeliveryMechanism: 'security-template', importedAt: '2026-09-01T00:00:00Z', title: 'Privilege Rights · SeSyntheticPrivilege', policyValues: [actual], sourceLocator: { objectPath: 'Synthetic/DomainSysvol/GPO/Machine/microsoft/windows nt/SecEdit/GptTmpl.inf#lines=1' } } as unknown as UnifiedResultProjection; const comparator = (baseline: string[]) => ({ version: 1, comparator: { kind: 'set', mode: 'exact' }, relation: configured.length === baseline.length && configured.every(item => baseline.includes(item)) ? 'match' : 'different', diagnosticCodes: [], actual: { kind: 'string-set', values: configured }, baseline: { kind: 'string-set', values: baseline } }); const results = packages.map((variants, index) => { const expected = { ...actual, principals: expectedFor(index) }; const variantList = Array.from({ length: variants }, (_, variant) => ({ settingId: `setting-${index}-${variant}`, baselineValues: [expected], comparator: comparator(expected.principals) })); const decision = variantList.every(item => item.comparator.relation === 'match') ? 'all-pass' : 'all-fail'; return { id: `comparison-${index}`, baselineId: `baseline-${index}`, baselineLabel: `Synthetic baseline ${index}`, baselineAnalysisId: `analysis-${index}`, observationIds: [observation.sourceResultId], baselineState: 'explicit-recommendation', customerState: 'configured', classification: decision === 'all-pass' ? 'configured_match' : 'confirmed_configured_deviation', identity: { id: 'synthetic-right', scope: 'computer', target: { kind: 'security-template', section: 'Privilege Rights', key: 'SeSyntheticPrivilege' } }, diagnosticCodes: [], comparator: comparator(expected.principals), baselineVariantAssessment: { version: 1, scope: 'backup-configuration', decision, diagnosticCodes: [], actualValues: [actual], variants: variantList } }; }) as unknown as PolicyEvaluationResult[]; const evaluation = { run: { id: 'evaluation', projectId: 'synthetic', aggregationRunId: 'aggregation', evaluator: POLICY_EVALUATION_ENGINE, completedAt: '2026-09-02T00:00:00Z' }, results } as StoredPolicyEvaluation; return { observation, evaluation }; } function read(observation: UnifiedResultProjection, evaluations: StoredPolicyEvaluation[]) { const dataset = appendPolicyObservationsToDataset(null, 'Synthetic', [observation], evaluations)!; const record = dataset.sheets[0].analysis.records[0]; const html = renderToStaticMarkup( {}} onNext={() => {}} onReview={() => {}}/>); const container = document.createElement('div'); container.innerHTML = html; return { record, container }; } const count = (text: string, part: string) => text.split(part).length - 1; describe('GPO Finding Reader baseline comparison', () => { it('keeps source, baseline and following field spacing when opening and closing comparisons', () => { const { observation, evaluation } = fixture([1, 2]); const { container } = read(observation, [evaluation]); const tokens = readFileSync('src/styles/ui-tokens.css', 'utf8'); const spacing = tokens.match(/--space-4:\s*([^;]+);/)![1]; const style = document.createElement('style'); // jsdom does not resolve custom properties; retain the actual production rules. style.textContent = readFileSync('src/styles/components.css', 'utf8') .replaceAll('var(--space-4)', spacing).replaceAll('var(--border-hair)', '1px') .replaceAll('var(--line)', 'gray').replaceAll('var(--line-strong)', 'gray'); document.head.append(style); document.body.append(container); try { const sources = container.querySelector('.policy-source-evidence')!; const baseline = container.querySelector('.policy-baseline-comparison')!; const details = baseline.querySelector('details')!; const checkSpacing = () => { for (const section of [sources, baseline]) { expect(getComputedStyle(section).paddingBottom).toBe(spacing); expect(getComputedStyle(section).marginBottom).toBe(spacing); } expect(getComputedStyle(sources.querySelector('.policy-source-group:last-child')!).borderBottomWidth).toBe('0px'); const last = container.querySelector('.reader-summary > :last-child')!; expect(getComputedStyle(last).borderBottomWidth).toBe('0px'); }; checkSpacing(); (details.querySelector('summary') as HTMLElement).click(); expect(details.open).toBe(true); checkSpacing(); (details.querySelector('summary') as HTMLElement).click(); expect(details.open).toBe(false); checkSpacing(); } finally { container.remove(); style.remove(); } }); it('states identical variant outcomes once and keeps every stored comparison in a closed disclosure', () => { const { observation, evaluation } = fixture([1, 2, 3]); const { record, container } = read(observation, [evaluation]); const section = container.querySelector('.policy-baseline-comparison')!; expect(section.getAttribute('data-consistent')).toBe('true'); // The first block states the problem; the comparison section does not repeat it. const problem = container.querySelector('.policy-problem')!; expect(problem.querySelector('h4')?.textContent).toBe('Problem'); const values = Object.fromEntries([...problem.querySelectorAll('.policy-problem__values > div')] .map(row => [row.querySelector('dt')!.textContent, row.querySelector('dd')!.textContent])); // Red marks the member absent from every variant, green the member every variant expects; // the separate Additional entries row is then redundant. expect(values).toEqual({ Configured: '*S-1-5-32-544 (Administrators)*S-1-5-32-549 (Server Operators)', Expected: 'Values: *S-1-5-32-544 (Administrators)' }); expect([...problem.querySelectorAll('.policy-problem__entry[data-state]')].map(entry => [entry.textContent, entry.getAttribute('data-state')])) .toEqual([['*S-1-5-32-544 (Administrators)', 'aligned'], ['*S-1-5-32-549 (Server Operators)', 'deviating']]); // The source section carries the same proof: green for the expected member, red for the extra one. expect(container.querySelector('.policy-source-aligned-value')?.textContent?.trim()).toBe('*S-1-5-32-544 (Administrators)'); expect(container.querySelector('.policy-source-deviation-value')?.textContent?.trim()).toBe('*S-1-5-32-549 (Server Operators)'); expect(problem.querySelector('.policy-problem__context')?.textContent) .toBe('1 GPO backup source · differs from all 6 assessed baseline variants in 3 baseline packages'); expect(problem.nextElementSibling?.matches('.policy-next-step')).toBe(true); expect(container.querySelector('.policy-next-step')?.textContent).toContain('Expected: Values: *S-1-5-32-544'); expect(problem.compareDocumentPosition(container.querySelector('.policy-source-evidence')!) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy(); expect(section.querySelector('.policy-baseline-comparison__verdict')).toBeNull(); expect(section.querySelector('.policy-baseline-comparison__facts')).toBeNull(); const text = container.querySelector('.finding-reader')!.textContent!; expect(count(text, 'target role is not established')).toBe(1); expect(count(text, 'not effective device security')).toBe(1); const details = section.querySelector('details')!; expect(details.hasAttribute('open')).toBe(false); expect(details.querySelector('summary')?.textContent).toBe('All baseline comparisons (3 packages, 6 variants)'); expect(details.querySelectorAll('.policy-baseline-package')).toHaveLength(3); expect(details.querySelectorAll('.policy-baseline-variant')).toHaveLength(6); // User correction 2026-09-25: every variant shows its own expected value and result, not only the package verdict. const firstVariant = details.querySelector('.policy-baseline-variant')!; expect(firstVariant.querySelector('.policy-baseline-variant__values dd')?.textContent).toBe('Values: *S-1-5-32-544 (Administrators)'); expect(firstVariant.querySelector('.policy-baseline-variant__result')?.textContent).toBe('Different'); expect(firstVariant.querySelector('.policy-baseline-variant__result')?.getAttribute('data-outcome')).toBe('fail'); expect(details.querySelectorAll('.policy-baseline-variant__values')).toHaveLength(6); expect(details.textContent).not.toContain('Every variant below has the outcome shown above.'); // Expected value and remediation appear once instead of once per package. expect(container.querySelector('.policy-value-block[data-value="expected"]')).toBeNull(); expect(container.querySelectorAll('.policy-problem__step')).toHaveLength(1); expect(container.querySelector('.reader-remediation')).toBeNull(); expect(details.querySelector('table')).toBeNull(); for (const id of ['setting-0-0', 'setting-1-1', 'setting-2-2']) expect(details.textContent).toContain(`Setting reference: ${id}`); // The repeated per-package fields are not rendered a second time in the reader. expect([...container.querySelectorAll('.reader-summary h4')].map(item => item.textContent)).not.toContain('Comparison'); // Exports and search keep the complete original text. const description = record.fields.find(field => field.label === 'Description')!.value; expect(count(description, 'target role is not established')).toBe(3); expect(count(description, 'Setting reference:')).toBe(6); }); it('opens the disclosure and does not claim a shared outcome when package verdicts differ', () => { const { observation, evaluation } = fixture([1, 1]); const pass = evaluation.results[1]; const variant = pass.baselineVariantAssessment!.variants[0]; variant.baselineValues = observation.policyValues as never; variant.comparator = { ...variant.comparator, relation: 'match', baseline: { kind: 'string-set', values: ['*S-1-5-32-544', '*S-1-5-32-549'] } } as never; pass.comparator = variant.comparator; pass.classification = 'configured_match'; pass.baselineVariantAssessment!.decision = 'all-pass'; const { container } = read(observation, [evaluation]); const section = container.querySelector('.policy-baseline-comparison')!; expect(section.getAttribute('data-consistent')).toBe('false'); expect(section.querySelector('.policy-baseline-comparison__verdict')?.textContent).toMatch(/^Outcomes differ between 2 assessed baseline variants/); expect(section.querySelector('.policy-baseline-comparison__statuses')?.textContent).toBe('Fail: 1 · Pass: 1'); expect(section.querySelectorAll('.policy-baseline-value-group')).toHaveLength(2); expect(section.querySelector('details')?.hasAttribute('open')).toBe(true); expect(section.querySelectorAll('.policy-baseline-variant__values')).toHaveLength(2); expect([...section.querySelectorAll('.policy-baseline-package__header .outcome-mark')].map(item => item.getAttribute('data-outcome'))).toEqual(['fail', 'pass']); }); it('groups differing expected values of a uniform Fail and names only the exception packages', () => { const { observation, evaluation } = fixture([1, 1, 1, 1], ['*S-1-5-32-544', '*S-1-5-32-549'], index => index === 3 ? ['*S-1-5-32-551'] : ['*S-1-5-32-544']); const { record, container } = read(observation, [evaluation]); const section = container.querySelector('.policy-baseline-comparison')!; expect(section.getAttribute('data-uniform-outcome')).toBe('true'); expect(section.getAttribute('data-consistent')).toBe('false'); const problem = container.querySelector('.policy-problem')!; expect(problem.querySelector('.policy-problem__context')?.textContent) .toBe('1 GPO backup source · differs from all 4 assessed baseline variants in 4 baseline packages'); const expected = [...problem.querySelectorAll('.policy-problem__values > div')].find(row => row.querySelector('dt')?.textContent === 'Expected'); expect([...expected!.querySelectorAll('.policy-problem__entry')].map(item => item.textContent)).toEqual([ 'Values: *S-1-5-32-544 (Administrators) — 3 baseline packages', 'Values: *S-1-5-32-551 (Backup Operators) — Synthetic baseline 3' ]); // Package-specific differences have no single shared entry list. expect(problem.textContent).not.toContain('Additional entries'); expect(section.querySelector('.policy-baseline-comparison__statuses')).toBeNull(); expect(section.querySelector('.policy-baseline-value-group')).toBeNull(); // A uniform result keeps the per-package list closed; its values stay available per variant. expect(section.querySelector('details')?.hasAttribute('open')).toBe(false); expect(section.querySelectorAll('.policy-baseline-variant__values')).toHaveLength(4); expect(container.querySelector('.policy-value-block[data-value="expected"]')).toBeNull(); const next = container.querySelector('.policy-next-step')!; expect(next.textContent).toContain('Establish the applicable baseline template'); expect([...next.querySelectorAll('.policy-problem__packages')].map(item => item.textContent)) .toEqual(['3 baseline packages', 'Synthetic baseline 3']); expect(next.querySelector('.policy-problem__sources')?.textContent).toBe('In: GPO backup synthetic-backup'); expect(record.fields.find(field => field.label === 'Remediation')!.value.split('Computer policy:').length - 1).toBe(4); }); it('names a current stored comparison of the setting for ISO control mapping', () => { const { observation, evaluation } = fixture([1, 1]); const { record } = read(observation, [evaluation]); expect(record.findingOccurrenceId).toBeUndefined(); expect(evaluation.results.map(result => `policy-evaluation-comparison:${result.id}`)).toContain(regulatoryMappingResultId(record)); }); it('keeps the complete stored text for a historical comparison', () => { const { observation, evaluation } = fixture([1, 1]); evaluation.run.evaluator = { ...POLICY_EVALUATION_ENGINE, version: '1.8.0' }; const { record, container } = read(observation, [evaluation]); expect(record.policyBaselineComparisons).toBeUndefined(); // A historical comparison is not a current Result DB row, so ISO mapping has nothing to name. expect(regulatoryMappingResultId(record)).toBe(''); expect(container.querySelector('.policy-baseline-comparison')).toBeNull(); }); it('names only documented well-known SIDs next to the unchanged original value', () => { expect(wellKnownPrincipalNames('Values: *S-1-5-32-544, *S-1-5-32-549')).toEqual(['Administrators', 'Server Operators']); // Standardized domain RIDs are named; the domain part itself stays unresolved. expect(wellKnownPrincipalNames('*S-1-5-21-1000-2000-3000-512')).toEqual(['Domain Admins']); expect(wellKnownPrincipalNames('*S-1-5-21-1000-2000-3000-1105')).toEqual([]); expect(wellKnownPrincipalNames('*S-1-5-32-5440')).toEqual([]); }); });