Assessor_2.0/tests/features/gpo-category-badges.test.tsx

149 lines
9.8 KiB
TypeScript

import { createElement } from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import { describe, expect, it } from 'vitest';
import type { FindingRecord } from '../../src/domain/finding';
import { policyFindingCategories } from '../../src/profiles/group-policy/categories';
import { FindingReader, selectFindingFields } from '../../src/features/finding-reader/FindingReader';
import { FindingCard } from '../../src/components/FindingCard/FindingCard';
function fixture(values: Record<string, string>, title = 'Synthetic setting'): FindingRecord {
return { id: 'synthetic', index: 0, title, filled: [], meta: [],
fields: Object.entries(values).map(([label, value], index) => ({ index, label, value })) };
}
describe('GPO category badges', () => {
it.each([
['system access · lockoutbadcount', 'System Access', 'Account Lockout'],
['system access · passwordhistorysize', 'System Access', 'Password Policy'],
['privilege rights · senetworklogonright', 'Privilege Rights', 'Logon Rights'],
['privilege rights · sebackupprivilege', 'Privilege Rights', 'Security'],
['registry values · machine\\software\\synthetic', 'Registry Values', 'Security'],
['system services · synthetic', 'System Services', 'Security'],
])('uses native section evidence from legacy headings: %s', (title, section, detail) => {
const badges = policyFindingCategories(fixture({}, title), 'group-policy');
expect(badges).toContain(section);
expect(badges).toContain(detail);
});
it('uses structural registry branches only when no more specific family is known', () => {
expect(policyFindingCategories(fixture({ 'Registry path': 'SOFTWARE\\Synthetic' }), 'group-policy')).toEqual(['Registry', 'Software']);
expect(policyFindingCategories(fixture({ 'Registry path': 'SYSTEM\\CurrentControlSet\\Synthetic' }), 'group-policy')).toEqual(['Registry', 'System']);
expect(policyFindingCategories(fixture({ 'Registry path': 'SOFTWARE\\Policies\\Microsoft\\Office\\16.0' }), 'group-policy')).toEqual(['Office']);
expect(policyFindingCategories(fixture({ Description: 'privilege rights · sebackupprivilege' }), 'group-policy')).toEqual([]);
});
it('derives the Office badge from a retained Registry Preference title when Registry path is unavailable', () => {
const title = 'registry · HKEY_CURRENT_USER\\Software\\Microsoft\\Office\\16.0\\Outlook\\AutoDiscover\\ExcludeScpLookup';
const record = fixture({ Category: 'GPOs', Status: 'Not comparable', 'Policy type': 'group-policy-preference' }, title);
expect(policyFindingCategories(record, 'group-policy')).toEqual(['Office']);
const html = renderToStaticMarkup(createElement(FindingReader, { record, title, linkedSheets: [], resultReferences: [],
search: '', position: 0, recordCount: 1, toolId: 'group-policy', onPrevious() {}, onNext() {}, onReview() {} }));
expect(html).toContain('data-derived-category="gpo">Office</span>');
});
it.each(['computer', 'user'])('adds the %s scope badge and retains the report field', scope => {
const record = fixture({ Category: 'GPOs', Scope: scope });
const html = renderToStaticMarkup(createElement(FindingReader, { record, title: record.title, linkedSheets: [], resultReferences: [], search: '', position: 0, recordCount: 1, toolId: 'group-policy', onPrevious() {}, onNext() {}, onReview() {} }));
expect(html).toContain(`data-policy-scope="true">Scope: ${scope}</span>`);
expect(selectFindingFields(record, record.title, false, 'group-policy').summary).toContainEqual({ index: 1, label: 'Scope', value: scope });
expect(html).toContain('<h4>Scope</h4>');
});
it('does not invent a scope badge when scope is absent or blank', () => {
const examples: Record<string, string>[] = [{}, { Scope: ' ' }];
for (const values of examples) {
const record = fixture(values);
const html = renderToStaticMarkup(createElement(FindingReader, { record, title: record.title, linkedSheets: [], resultReferences: [], search: '', position: 0, recordCount: 1, toolId: 'group-policy', onPrevious() {}, onNext() {}, onReview() {} }));
expect(html).not.toContain('data-policy-scope');
}
});
it('contains a long Registry Values MACHINE path in both reader headings', () => {
const path = 'MACHINE\\System\\CurrentControlSet\\Services\\NTDS\\Parameters\\SyntheticChannelBinding';
const title = `Registry Values \u00b7 ${path}`;
const record = fixture({ Category: 'GPOs', Status: 'Not configured', Scope: 'computer' }, title);
const html = renderToStaticMarkup(createElement(FindingReader, { record, title, linkedSheets: [], resultReferences: [],
search: 'SyntheticChannelBinding', position: 0, recordCount: 1, toolId: 'group-policy',
onPrevious() {}, onNext() {}, onReview() {} }));
expect(html.match(/<code class="technical-path">/g)).toHaveLength(2);
// Both headings; the Policy Analyzer search key is no longer shown (user decision 2026-09-30).
expect(html.match(/<mark class="search-highlight">SyntheticChannelBinding<\/mark>/g)).toHaveLength(2);
expect(html).toContain(`<h2>Registry Values \u00b7 <code class="technical-path">MACHINE\\System\\CurrentControlSet`);
expect(html).toContain(`<h3>Registry Values \u00b7 <code class="technical-path">MACHINE\\System\\CurrentControlSet`);
});
it('labels audit settings even when the customer setting is absent', () => {
const record = fixture({ Baseline: 'Windows Server 2022 Security Baseline', 'Policy type': 'advanced-audit-policy', 'Configured Value': 'Not configured' });
expect(policyFindingCategories(record, 'group-policy')).toEqual(['Windows', 'Audit']);
expect(selectFindingFields(record, record.title, false, 'group-policy').summary.map(field => field.label)).not.toContain('Policy type');
});
it('supports the previous generated audit heading and legacy audit templates', () => {
expect(policyFindingCategories(fixture({}, 'system audit subcategory · synthetic-id'), 'group-policy')).toEqual(['Audit']);
expect(policyFindingCategories(fixture({ 'Policy type': 'security-template', 'Policy section': 'Event Audit' }), 'group-policy')).toEqual(['Audit']);
});
it.each([
['Microsoft 365 Apps for enterprise Security Baseline', 'Office'],
['Office 2016 Security Baseline', 'Office'],
['Microsoft Edge Security Baseline', 'Edge']
])('identifies the product family of %s', (baseline, category) => {
expect(policyFindingCategories(fixture({ Baseline: baseline }), 'group-policy')).toEqual([category]);
});
it.each([
['SOFTWARE\\Policies\\Microsoft\\Windows Defender', 'Defender'],
['SOFTWARE\\Policies\\Microsoft\\WindowsFirewall\\DomainProfile', 'Firewall'],
['SOFTWARE\\Policies\\Microsoft\\FVE', 'BitLocker'],
['SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\LAPS', 'LAPS']
])('identifies a known registry family %s', (path, category) => {
expect(policyFindingCategories(fixture({ 'Registry path': path, Baseline: 'Windows 11 Security Baseline' }), 'group-policy')).toEqual(['Windows', category]);
});
it('prefers explicit Office or Edge paths over the package product', () => {
for (const product of ['Office', 'Edge']) {
expect(policyFindingCategories(fixture({ 'Registry path': `HKEY_LOCAL_MACHINE\\SOFTWARE\\Policies\\Microsoft\\${product}\\16.0`, Baseline: 'Windows 11 Security Baseline' }), 'group-policy')).toEqual([product]);
}
});
it('uses a conservative fallback and does not search arbitrary finding text', () => {
expect(policyFindingCategories(fixture({ Baseline: 'Windows 11 Security Baseline' }), 'group-policy')).toEqual(['Windows', 'Security']);
expect(policyFindingCategories(fixture({ Description: 'Office Audit Firewall' }), 'group-policy')).toEqual([]);
expect(policyFindingCategories(fixture({ Baseline: 'Windows 11 Security Baseline' }), 'cis-cat')).toEqual([]);
});
it('renders compact badges alongside GPOs without changing the outcome', () => {
const record = fixture({ Category: 'GPOs', Status: 'Fail', Baseline: 'Windows Server 2022 Security Baseline', 'Policy type': 'advanced-audit-policy' });
const html = renderToStaticMarkup(createElement(FindingReader, { record, title: record.title, linkedSheets: [], resultReferences: [], search: '', position: 0, recordCount: 1, toolId: 'group-policy', onPrevious() {}, onNext() {}, onReview() {} }));
expect(html).toContain('Category: GPOs');
expect(html).toContain('data-derived-category="gpo">Windows</span>');
expect(html).toContain('data-derived-category="gpo">Audit</span>');
expect(html).toContain('data-outcome="fail"');
});
it.each([
['Duplicates', 'Duplicate'],
['Conflict', 'Conflict']
])('keeps Fail as the verdict and adds a %s diagnostic badge in list and reader', (field, badge) => {
const record = fixture({ Category: 'GPOs', Status: 'Fail', [field]: '1 setting · 2 sources' });
const card = renderToStaticMarkup(createElement(FindingCard, { record, title: record.title, bookmarked: false,
toolId: 'group-policy', onOpen() {} }));
const reader = renderToStaticMarkup(createElement(FindingReader, { record, title: record.title, linkedSheets: [],
resultReferences: [], search: '', position: 0, recordCount: 1, toolId: 'group-policy',
onPrevious() {}, onNext() {}, onReview() {} }));
for (const html of [card, reader]) {
expect(html).toContain('data-outcome="fail"');
expect(html).toContain(`data-policy-diagnostic="${badge.toLowerCase()}"`);
expect(html).toContain(`>${badge}</span>`);
}
});
it('does not turn explicit negative diagnostic fields into badges', () => {
const record = fixture({ Category: 'GPOs', Status: 'Fail', Conflict: 'No', Duplicates: 'No' });
const html = renderToStaticMarkup(createElement(FindingCard, { record, title: record.title, bookmarked: false,
toolId: 'group-policy', onOpen() {} }));
expect(html).toContain('data-outcome="fail"');
expect(html).not.toContain('data-policy-diagnostic');
});
});