Assessor_2.0/tests/features/cis-cat-results-window.test.ts

219 lines
9.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// @vitest-environment jsdom
import { createElement } from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import { describe, expect, it } from 'vitest';
import cisCatFixture from '../../fixtures/cis-cat/assessment.synthetic.html?raw';
import futureControlsFixture from '../../fixtures/cis-cat/assessment.future-controls.synthetic.html?raw';
import type { FindingRecord } from '../../src/domain/finding';
import { importFile } from '../../src/importers/importer';
import { makeSheet } from '../../src/importers/sheet-factory';
import { toolIdForRecord } from '../../src/importers/status-normalization';
import { genericProfile } from '../../src/profiles/generic/generic-profile';
import { FloatingResultsWindow } from '../../src/features/linked-results/FloatingResultsWindow';
import { resultSectionsFor } from '../../src/features/linked-results/result-message';
import { linkResultSheets } from '../../src/profiles/purple-knight/result-linker';
const options = { profiles: [genericProfile] };
/**
* Vertrag des Results-Fensters fuer CIS-CAT, von der importierten Datei bis zur
* gerenderten Ausgabe: die Belegdaten des Berichts - Assessment Evidence und
* die Rahmenwerk-Zuordnungen hinter "More" - stehen im Fenster, mit der
* Ueberschrift des Berichts. Das Rule Result XML gehoert nicht dorthin,
* sondern bleibt ein aufklappbares Detail im Finding.
*/
async function resultsWindowMarkup(html: string, fileName: string) {
const imported = await importFile(new File([html], fileName, { type: 'text/html' }), options);
const findings = imported.sheets.find(sheet => sheet.name === 'assessmentResultTable')!;
const candidate = imported.extraction!.candidates[0];
const record: FindingRecord = {
id: 'cis-cat-record',
index: 0,
title: String(candidate.fields.find(field => field.label === 'Benchmark Item')?.decodedValue ?? ''),
fields: candidate.fields.map((field, index) => ({ index, label: field.label, value: String(field.decodedValue) })),
filled: [],
meta: []
};
record.fields.push({ index: record.fields.length, label: 'Source: Scan', value: 'CIS-CAT Pro \u00b7 2026-08-02T00:00:00.000Z' });
return renderToStaticMarkup(createElement(FloatingResultsWindow, {
id: 'cis-cat-results',
title: 'Results (1) \u2013 ' + record.title,
sheets: linkResultSheets(record, imported.sheets, findings.id),
toolId: toolIdForRecord(record, 'generic'),
sections: resultSectionsFor(record, 'cis-cat'),
color: '#7c3aed',
zIndex: 2100,
offset: 0,
search: '',
onActivate: () => undefined,
onClose: () => undefined,
onToggleMinimize: () => undefined
}));
}
describe('CIS-CAT Results-Fenster', () => {
it('zeigt versteckte Belegtabellen und Rahmenwerk-Zuordnungen gemeinsam', async () => {
const markup = await resultsWindowMarkup(cisCatFixture, 'CIS-CAT-SYNTHETIC.html');
// Hinter "Show Assessment Evidence" versteckte Tabellen.
expect(markup.match(/>Assessment<\/h4>/g)).toHaveLength(1);
// Hinter "More" versteckte Steuerungstabellen, je Rahmenwerk benannt.
expect(markup).toContain('>CIS Controls V7.0</h4>');
expect(markup).toContain('>CIS Critical Security Controls V8.0</h4>');
// Die Zuordnungsliste wird in Tabellenueberschriften ueberfuehrt.
expect(markup).not.toContain('>Control Mappings</h4>');
expect(markup).toContain('>Control 1: Synthetic Inventory</h5>');
expect(markup).toContain('>Control 2: Synthetic Assets</h5>');
expect(markup).toContain('Control 1: Synthetic Inventory');
expect(markup).not.toContain('Keine passende Ergebnisdatei automatisch gefunden.');
});
/**
* Nutzerkorrektur vom 2026-07-31: Das Rule Result XML fuellte das Fenster vor
* den eigentlichen Belegtabellen. Es bleibt ein aufklappbares Detail im
* Finding und darf im Fenster an keiner Stelle auftauchen.
*/
it('markiert den Assessment-Hinweis als ausgewertete blaue Zeile', () => {
const noteSheet = makeSheet(
'Assessment Evidence \u00b7 Hinweis',
[['No matching system items were found.']],
'',
'synthetic-cis-cat.html'
);
const markup = renderToStaticMarkup(createElement(FloatingResultsWindow, {
id: 'cis-cat-note',
title: 'Results (1) \u2013 Synthetic assessment',
sheets: [noteSheet],
toolId: 'cis-cat',
sections: [],
color: '#7c3aed',
zIndex: 2100,
offset: 0,
search: '',
onActivate: () => undefined,
onClose: () => undefined,
onToggleMinimize: () => undefined
}));
expect(markup).toContain('<tr class="rich-text-table-row--highlight"><td>No matching system items were found.</td>');
});
it('markiert Criterion gruen und die ausgewertete Abweichung rot', () => {
const evidenceSheet = makeSheet(
'Assessment Evidence · Registry Item',
[
['Field', 'Value'],
['Criterion:', "Ensure 'Start' is 'Windows: Registry Value' to '4'"],
['Value', '2']
],
'',
'synthetic-cis-cat.html'
);
evidenceSheet.analysis.records[1].highlighted = true;
const markup = renderToStaticMarkup(createElement(FloatingResultsWindow, {
id: 'cis-cat-colors',
title: 'Results (1) – Synthetic registry check',
sheets: [evidenceSheet],
toolId: 'cis-cat',
outcome: 'fail',
sections: [],
color: '#7c3aed',
zIndex: 2100,
offset: 0,
search: '',
onActivate: () => undefined,
onClose: () => undefined,
onToggleMinimize: () => undefined
}));
expect(markup).toContain('<tr class="rich-text-table-row--criterion"><td>Criterion:</td>');
expect(markup).toContain('<tr class="rich-text-table-row--violation"><td>Value</td><td>2</td>');
});
it('markiert den bestaetigten Ist-Wert eines bestandenen Results gruen statt rot', () => {
const evidenceSheet = makeSheet(
'Assessment Evidence · Registry Item',
[
['Field', 'Value'],
['Criterion:', "Ensure 'ForceGuest' is 'Windows: Registry Value' to '0'"],
['Type', 'reg_dword'],
['Value', '0']
],
'',
'synthetic-cis-cat.html'
);
evidenceSheet.analysis.records[1].highlighted = true;
evidenceSheet.analysis.records[2].highlighted = true;
const markup = renderToStaticMarkup(createElement(FloatingResultsWindow, {
id: 'cis-cat-pass-colors',
title: 'Results (1) – Passing registry check',
sheets: [evidenceSheet],
toolId: 'cis-cat',
outcome: 'pass',
sections: [],
color: '#7c3aed',
zIndex: 2100,
offset: 0,
search: '',
onActivate: () => undefined,
onClose: () => undefined,
onToggleMinimize: () => undefined
}));
expect(markup).toContain('<tr class="rich-text-table-row--match"><td>Type</td><td>reg_dword</td>');
expect(markup).toContain('<tr class="rich-text-table-row--match"><td>Value</td><td>0</td>');
expect(markup).not.toContain('rich-text-table-row--violation');
});
it('zeigt das Rule Result XML nicht im Fenster', async () => {
const markup = await resultsWindowMarkup(cisCatFixture, 'CIS-CAT-SYNTHETIC.html');
expect(markup).not.toContain('Rule Result XML');
expect(markup).not.toContain('xccdf:rule-result');
expect(markup).not.toContain('synthetic-1.1.1');
});
it('zeigt auch ein neues und ein unbekanntes Rahmenwerk vollstaendig an', async () => {
const markup = await resultsWindowMarkup(futureControlsFixture, 'CIS-CAT-FUTURE.html');
expect(markup).toContain('>Assessment</h4>');
expect(markup).toContain('>CIS Critical Security Controls V8.1</h4>');
expect(markup).toContain('>Synthetic Framework V1.0</h4>');
expect(markup).toContain('>Control 8: Synthetic Audit Log Management</h5>');
expect(markup).toContain('>SF-1: Synthetic Framework Requirement</h5>');
expect(markup).toContain('Control 8: Synthetic Audit Log Management');
expect(markup).toContain('SF-1: Synthetic Framework Requirement');
// Der Rule-Result-XML-Block bleibt vollstaendig aussen vor, samt seiner
// internen Tabelle.
expect(markup).not.toContain('XML Evidence');
expect(markup).not.toContain('synthetic-2.1.1');
});
/**
* Regression: Die Steuerungstabelle ist eine Schluessel/Wert-Tabelle ohne
* Kopfzeile. Die Kopfzeilensuche waehlte darin die Zeile mit den zwei
* laengsten Beschriftungen und verwarf alles darueber - im Fenster blieb von
* vier Angaben eine einzige Spalte "Label:" mit der Zeile "Description:"
* uebrig, ohne einen einzigen Wert.
*/
it('zeigt jede Angabe der Steuerungstabelle mit ihrem Wert', async () => {
const markup = await resultsWindowMarkup(cisCatFixture, 'CIS-CAT-SYNTHETIC.html');
for (const label of ['Control:', 'Subcontrol:', 'Label:', 'Description:']) {
expect(markup).toContain(`<td>${label}</td>`);
}
expect(markup).toContain('Maintain a synthetic inventory.');
expect(markup).toContain('Synthetic Inventory');
expect(markup).toContain('IG-1');
// Kein Wert darf als Spaltenueberschrift enden.
expect(markup).not.toContain('<th>Synthetic Inventory</th>');
});
/**
* "More" ist Bedienelement des Berichts, kein Inhalt - im Fenster gibt es
* nichts mehr aufzuklappen.
*/
it('uebernimmt die Aufklappbeschriftung des Berichts nicht in den Ergebnistext', async () => {
const markup = await resultsWindowMarkup(cisCatFixture, 'CIS-CAT-SYNTHETIC.html');
expect(markup).not.toMatch(/Synthetic Inventory[\s–-]*More/);
});
});