import { describe, expect, it } from 'vitest'; import { buildFindingPathItems, extractFindingMitreFocus, sharedEntityIds, type MitreEntity, type MitreGraphData } from '../../src/features/mitre-graph/mitre-graph'; import type { FindingRecord } from '../../src/domain/finding'; const record = (value: string): FindingRecord => ({ id: value, index: 0, title: 'Finding', fields: [{ label: 'MITRE ATT&CK', value, index: 0 }], filled: [], meta: [] }); describe('MITRE graph', () => { it('erkennt Technik-IDs und Taktiknamen aus Findings', () => { const focus = extractFindingMitreFocus( [record('T1059.001; Persistence / Privilege Escalation')], ['Persistence', 'Privilege Escalation'] ); expect([...focus.techniqueIds]).toEqual(['T1059.001']); expect([...focus.tactics]).toEqual(['Persistence', 'Privilege Escalation']); expect(focus.findingCount).toBe(1); }); it('ordnet Findings als Pfadschritte einer primären Taktik zu', () => { const data: MitreGraphData = { version: 'test', tacticOrder: ['Execution', 'Persistence'], techniques: [{ id: 'T1059.001', name: 'PowerShell', tactics: ['Execution'], platforms: ['Windows'], parent: 'T1059', url: '', sourceCount: 1 }], entities: [] }; const item = buildFindingPathItems([record('T1059.001, Persistence')], data)[0]; expect(item.primaryTactic).toBe('Execution'); expect(item.tactics).toEqual(['Execution', 'Persistence']); expect(item.techniqueIds).toEqual(['T1059.001']); }); it('bewahrt mehrere Kategorien und D3FEND-Beziehungen für den Netzwerkgraphen', () => { const source = record('Execution / Persistence'); source.fields.push( { label: 'Category', value: 'Identity | Lateral Movement', index: 1 }, { label: 'MITRE D3FEND', value: 'Harden - Credential Hardening; Detect - Account Monitoring', index: 2 }, { label: 'Target', value: 'DC-01 | Tier 0', index: 3 } ); const data: MitreGraphData = { version: 'test', tacticOrder: ['Execution', 'Persistence'], techniques: [], entities: [] }; const item = buildFindingPathItems([source], data)[0]; expect(item.categories).toEqual(['Identity', 'Lateral Movement']); expect(item.defenses).toEqual(['Harden - Credential Hardening', 'Detect - Account Monitoring']); expect(item.targets).toEqual(['DC-01', 'Tier 0']); expect(item.tactics).toEqual(['Execution', 'Persistence']); }); it('erzeugt keine Kategorie aus dem Quellblatt oder All-Records-Kontext', () => { const source = { ...record('Execution'), sourceSheet: 'Dummy · All Records' }; const data: MitreGraphData = { version: 'test', tacticOrder: ['Execution'], techniques: [], entities: [] }; const item = buildFindingPathItems([source], data)[0]; expect(item.category).toBe(''); expect(item.categories).toEqual([]); expect(item.targets).toEqual([]); }); it('leitet CIS-Kategorien aus allen Eltern einer Benchmark-Regelnummer ab', () => { const source = record(''); source.fields.push({ label: 'Rule ID', value: '17.2.4', index: 1 }); const data: MitreGraphData = { version: 'test', tacticOrder: [], techniques: [], entities: [] }; const item = buildFindingPathItems([source], data, '', 'cis-cat')[0]; expect(item.categories).toEqual(['CIS 17', 'CIS 17.2']); }); it('ermittelt gemeinsame MITRE-Kontexte nach Typ', () => { const entities: MitreEntity[] = [ { id: 'G1', name: 'Group', type: 'group', techniques: ['T1', 'T2'] }, { id: 'S1', name: 'Software', type: 'software', techniques: ['T1', 'T2'] } ]; expect(sharedEntityIds('T1', 'T2', entities, 'all')).toEqual(['G1', 'S1']); expect(sharedEntityIds('T1', 'T2', entities, 'group')).toEqual(['G1']); }); });