169 lines
7.9 KiB
TypeScript
169 lines
7.9 KiB
TypeScript
import { createElement } from 'react';
|
|
import { renderToStaticMarkup } from 'react-dom/server';
|
|
import { readFileSync } from 'node:fs';
|
|
import { describe, expect, it } from 'vitest';
|
|
import type { FindingRecord } from '../../src/domain/finding';
|
|
import { compactTileBadges, tileBadges, TileCard } from '../../src/components/TileCard/TileCard';
|
|
import { policyFindingCategories } from '../../src/profiles/group-policy/categories';
|
|
|
|
const record: FindingRecord = {
|
|
id: 'tile-1',
|
|
index: 0,
|
|
title: 'Enterprise applications using SAML for SSO',
|
|
fields: [
|
|
{ index: 0, label: 'Score', value: '86' },
|
|
{ index: 1, label: 'Severity', value: 'Low' },
|
|
{ index: 2, label: 'Weight', value: '2' },
|
|
{ index: 3, label: 'Category', value: 'Entra ID' },
|
|
{ index: 4, label: 'Result', value: 'IOE Found' }
|
|
],
|
|
filled: [],
|
|
meta: []
|
|
};
|
|
|
|
describe('tileBadges', () => {
|
|
it('uses the same GPO categories as the detail view and also shows scope', () => {
|
|
const gpo: FindingRecord = { ...record, title: 'system access · minimumpasswordlength', fields: [
|
|
{ index: 0, label: 'Category', value: 'GPOs' }, { index: 1, label: 'Scope', value: 'computer' },
|
|
{ index: 2, label: 'Baseline', value: 'Windows Server 2022 Security Baseline' }, { index: 3, label: 'Status', value: 'Fail' }
|
|
] };
|
|
const badges = tileBadges(gpo, 'group-policy');
|
|
expect(badges.filter(b => b.label === 'GPO Category').map(b => b.text)).toEqual(policyFindingCategories(gpo, 'group-policy'));
|
|
expect(badges.map(b => b.text)).toEqual(['Fail', 'GPOs', 'Windows', 'Security', 'System Access', 'Password Policy', 'Scope: computer']);
|
|
const html = renderToStaticMarkup(createElement(TileCard, { record: gpo, title: gpo.title, bookmarked: false, toolId: 'group-policy', onOpen() {}, onBookmark() {} }));
|
|
for (const category of ['Windows', 'Security', 'System Access', 'Password Policy']) expect(html).toContain(`data-badge-role="category">${category}</span>`);
|
|
expect(html).toContain('data-badge-role="scope">Scope: computer</span>');
|
|
expect(html).not.toContain('tile-badge-overflow');
|
|
});
|
|
|
|
it('recognizes GPOs in a mixed-tool tile grid', () => {
|
|
const gpo: FindingRecord = { ...record, title: 'privilege rights · senetworklogonright', fields: [
|
|
{ index: 0, label: 'Source: Scan', value: 'GPOs · Synthetic' }, { index: 1, label: 'Scope', value: 'user' }
|
|
] };
|
|
expect(tileBadges(gpo, 'generic').map(b => b.text)).toEqual(['Security', 'Privilege Rights', 'Logon Rights', 'Scope: user']);
|
|
});
|
|
|
|
it('deduplicates categories but keeps an explicitly named scope', () => {
|
|
const gpo: FindingRecord = { ...record, fields: [
|
|
{ index: 0, label: 'Category', value: 'Office' }, { index: 1, label: 'Baseline', value: 'Office 2016 Security Baseline' },
|
|
{ index: 2, label: 'Scope', value: 'computer' }, { index: 3, label: 'Scope', value: 'computer' }
|
|
] };
|
|
expect(tileBadges(gpo, 'group-policy').map(b => b.text)).toEqual(['Office', 'Scope: computer']);
|
|
});
|
|
|
|
it('does not add GPO badges to other tools or invent missing scope', () => {
|
|
const other: FindingRecord = { ...record, fields: [
|
|
{ index: 0, label: 'Scope', value: 'computer' }, { index: 1, label: 'Baseline', value: 'Windows 11 Security Baseline' }
|
|
] };
|
|
expect(tileBadges(other, 'purple-knight')).toEqual([]);
|
|
expect(tileBadges({ ...other, fields: [] }, 'group-policy')).toEqual([]);
|
|
});
|
|
|
|
it('uses the fixed legacy order independently of source-column order', () => {
|
|
expect(tileBadges(record).map(badge => badge.text)).toEqual([
|
|
'IOE Found',
|
|
'Entra ID',
|
|
'Low',
|
|
'Weight: 2',
|
|
'Score: 86'
|
|
]);
|
|
});
|
|
|
|
it('only prefixes weight and score and applies severity styling to severity', () => {
|
|
const badges = tileBadges(record);
|
|
expect(badges.find(badge => badge.label === 'Category')?.text).toBe('Entra ID');
|
|
expect(badges.find(badge => badge.label === 'Severity')?.severity).toBe('low');
|
|
});
|
|
|
|
it('filters out Maester result message from status badge', () => {
|
|
const maesterRecord: FindingRecord = {
|
|
id: 'maester-1',
|
|
index: 0,
|
|
title: 'Ensure all users can complete multifactor authentication',
|
|
fields: [
|
|
{ index: 0, label: 'Severity', value: 'High' },
|
|
{ index: 1, label: 'Status', value: 'Failed' },
|
|
{ index: 2, label: 'Result', value: 'You have 52 of 78 users that aren\'t registered for MFA | --- | --- | Fail | ...' }
|
|
],
|
|
filled: [],
|
|
meta: []
|
|
};
|
|
|
|
const badges = tileBadges(maesterRecord, 'maester');
|
|
expect(badges.find(badge => badge.label === 'Status')?.text).toBe('Failed');
|
|
});
|
|
|
|
it('zeigt bei Purple Knight Status statt des SI-Verweises aus Result', () => {
|
|
const projected: FindingRecord = {
|
|
...record,
|
|
fields: [
|
|
{ index: 0, label: 'Result', value: 'SI000067' },
|
|
{ index: 1, label: 'Status', value: 'Pass' },
|
|
{ index: 2, label: 'ShortName', value: 'SI000067' }
|
|
]
|
|
};
|
|
|
|
expect(tileBadges(projected, 'purple-knight').find(badge => badge.label === 'Status')?.text).toBe('Passed');
|
|
});
|
|
|
|
it('zeigt abgeleitete CIS-Kategorien auch auf Finding-Kacheln', () => {
|
|
const cisRecord: FindingRecord = {
|
|
...record,
|
|
title: '2.2.2 Ensure a synthetic user right',
|
|
fields: [{ index: 0, label: 'Rule ID', value: '2.2.2' }]
|
|
};
|
|
|
|
expect(tileBadges(cisRecord, 'cis-cat').filter(badge => badge.label === 'CIS Category').map(badge => badge.text))
|
|
.toEqual(['CIS 2', 'CIS 2.2']);
|
|
});
|
|
|
|
it('keeps the CIS number on the tile and offers the run report section title on hover', () => {
|
|
const cisRecord: FindingRecord = {
|
|
...record,
|
|
title: '2.2.2 Ensure a synthetic user right',
|
|
fields: [{ index: 0, label: 'Rule ID', value: '2.2.2' }],
|
|
benchmarkSectionTitles: new Map([['2', 'Synthetic Local Policies'], ['2.2', 'Synthetic User Rights Assignment']])
|
|
};
|
|
expect(tileBadges(cisRecord, 'cis-cat').filter(badge => badge.label === 'CIS Category')).toEqual([
|
|
{ label: 'CIS Category', value: 'CIS 2', text: 'CIS 2', title: 'Synthetic Local Policies' },
|
|
{ label: 'CIS Category', value: 'CIS 2.2', text: 'CIS 2.2', title: 'Synthetic User Rights Assignment' }
|
|
]);
|
|
});
|
|
|
|
it('ordnet abgeleitete Kategorien vor Severity, Weight und Score ein', () => {
|
|
const cisRecord: FindingRecord = { ...record, title: '2.2.2 Ensure a synthetic user right' };
|
|
|
|
expect(tileBadges(cisRecord, 'cis-cat').map(badge => badge.text)).toEqual([
|
|
'IOE Found', 'Entra ID', 'CIS 2', 'CIS 2.2', 'Low', 'Weight: 2', 'Score: 86'
|
|
]);
|
|
});
|
|
|
|
it('verdichtet ueberzaehlige Graphbadges zu einem sichtbaren Zaehler', () => {
|
|
const badges = [
|
|
{ label: 'Status', value: 'Fail', text: 'Fail' },
|
|
{ label: 'Weight', value: '1.0', text: 'Weight: 1.0' },
|
|
{ label: 'CIS Category', value: 'CIS 18', text: 'CIS 18' },
|
|
{ label: 'CIS Category', value: 'CIS 18.10', text: 'CIS 18.10' },
|
|
{ label: 'CIS Category', value: 'CIS 18.10.18', text: 'CIS 18.10.18' }
|
|
];
|
|
|
|
expect(compactTileBadges(badges, 4).map(badge => badge.text)).toEqual(['Fail', 'Weight: 1.0', 'CIS 18.10.18', '+2']);
|
|
});
|
|
|
|
it('keeps long GPO registry titles inside the tile content column', () => {
|
|
const title = 'HKEY_LOCAL_MACHINE · SOFTWARE\\Policies\\Microsoft\\Windows\\Windows Error Reporting\\Disabled';
|
|
const html = renderToStaticMarkup(createElement(TileCard, {
|
|
record: { ...record, title }, title, bookmarked: false, toolId: 'group-policy',
|
|
onOpen() {}, onBookmark() {}
|
|
}));
|
|
const css = readFileSync(new URL('../../src/styles/legacy-views.css', import.meta.url), 'utf8');
|
|
const gpoRule = css.slice(css.indexOf('.data-tile[data-tool="group-policy"]')).split('}')[0];
|
|
|
|
expect(html).toContain('data-tool="group-policy"');
|
|
expect(html).toContain('HKEY_LOCAL_MACHINE');
|
|
expect(gpoRule).toContain('overflow-wrap: anywhere');
|
|
expect(gpoRule).toContain('max-width: 100%');
|
|
expect(gpoRule).not.toMatch(/overflow:\s*hidden|text-overflow|nowrap/);
|
|
});
|
|
});
|