import { describe, expect, it } from 'vitest'; import { buildFindingPathItems, defaultMitreGraphData, extractFindingMitreFocus, sharedEntityIds, validateMitreMatrixData, type MitreEntity, type MitreGraphData } from '../../src/features/mitre-graph/mitre-graph'; import { convertEnterpriseStix } from '../../src/features/mitre-graph/mitre-stix'; 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('ships the current Enterprise catalog with the new Stealth tactic', () => { expect(defaultMitreGraphData.version).toBe('19.2'); expect(defaultMitreGraphData.tacticOrder).toContain('Stealth'); expect(defaultMitreGraphData.techniques.find(technique => technique.id === 'T1055.011')?.tactics).toContain('Stealth'); }); it('converts an official-style STIX bundle, excluding revoked techniques and relations', () => { const raw = { type: 'bundle', objects: [ { type: 'x-mitre-collection', name: 'Enterprise ATT&CK', x_mitre_version: '19.2' }, { id: 'matrix', type: 'x-mitre-matrix', name: 'Enterprise ATT&CK', tactic_refs: ['tactic'] }, { id: 'tactic', type: 'x-mitre-tactic', name: 'Stealth', x_mitre_shortname: 'stealth' }, { id: 'pattern', type: 'attack-pattern', name: 'Synthetic technique', external_references: [{ source_name: 'mitre-attack', external_id: 'T9999', url: 'https://attack.mitre.org/techniques/T9999' }], kill_chain_phases: [{ kill_chain_name: 'mitre-attack', phase_name: 'stealth' }] }, { id: 'old', type: 'attack-pattern', name: 'Old', revoked: true, external_references: [{ source_name: 'mitre-attack', external_id: 'T9998' }], kill_chain_phases: [{ kill_chain_name: 'mitre-attack', phase_name: 'stealth' }] }, { id: 'group', type: 'intrusion-set', name: 'Synthetic group', external_references: [{ source_name: 'mitre-attack', external_id: 'G9999' }] }, { type: 'relationship', relationship_type: 'uses', source_ref: 'group', target_ref: 'pattern' }, { type: 'relationship', relationship_type: 'uses', source_ref: 'group', target_ref: 'old' } ] }; const converted = convertEnterpriseStix(raw); expect(converted).toEqual({ version: '19.2', tacticOrder: ['Stealth'], techniques: [{ id: 'T9999', name: 'Synthetic technique', tactics: ['Stealth'], platforms: [], parent: null, url: 'https://attack.mitre.org/techniques/T9999', sourceCount: 1 }], entities: [{ id: 'G9999', name: 'Synthetic group', type: 'group', techniques: ['T9999'] }] }); expect(validateMitreMatrixData(raw)).toEqual(converted); }); it('rejects unrelated STIX bundles and incomplete compact files', () => { expect(() => convertEnterpriseStix({ type: 'bundle', objects: [] })).toThrow('Enterprise ATT&CK'); expect(() => validateMitreMatrixData({ version: '19.2', tacticOrder: ['Stealth'], techniques: [] })).toThrow('techniques'); }); 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']); // Only exact catalog entries form relations; the old unchecked Account Monitoring text remains in the source field. expect(item.defenses).toEqual(['D3-CH · Credential Hardening']); expect(source.fields[2].value).toContain('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']); }); });