45 lines
3.3 KiB
TypeScript
45 lines
3.3 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { createReferenceRecognizer } from '../../src/domain/reference-recognition';
|
|
import { bundledReferenceCatalogs, referenceSnapshot } from '../../server/reference-data/reference-catalogs';
|
|
const recognize = createReferenceRecognizer(bundledReferenceCatalogs);
|
|
const field = (value: string, label = 'Description') => ({ key: 'source', label, value, path: '/original/source' });
|
|
|
|
describe('framework-wide reference recognition', () => {
|
|
it('distinguishes declared IDs, mentioned IDs, unknown IDs and exact declared titles', () => {
|
|
expect(recognize([field('T1059.001; TA0002', 'MITRE ATT&CK')]).map(ref => [ref.id, ref.evidence, ref.status]))
|
|
.toEqual([['T1059.001', 'declared', 'active'], ['TA0002', 'declared', 'active']]);
|
|
expect(recognize([field('Consider T1059.001 and D3-CH, plus T9999.')]).map(ref => [ref.id, ref.evidence, ref.status]))
|
|
.toEqual([['T1059.001', 'mentioned', 'active'], ['T9999', 'mentioned', 'not-in-catalog'], ['D3-CH', 'mentioned', 'active']]);
|
|
expect(recognize([field('PowerShell; Execution', 'MITRE ATT&CK')]).map(ref => [ref.id, ref.matchedBy]))
|
|
// PowerShell names both a historical technique and a current sub-technique: do not guess.
|
|
.toEqual([['TA0002', 'exact-title']]);
|
|
expect(recognize([field('PowerShell; Execution; Credential Hardening')])).toEqual([]);
|
|
expect(recognize([field('Credential Hardening', 'D3FEND')])[0]).toMatchObject({ id: 'D3-CH', evidence: 'declared', matchedBy: 'exact-title' });
|
|
});
|
|
it('retains every repeated original occurrence and UTF-16 offsets without rewriting source values', () => {
|
|
const original = field('😀 See t1059.001 then T1059.001.');
|
|
const before = structuredClone(original);
|
|
const refs = recognize([original]);
|
|
expect(refs).toHaveLength(2);
|
|
for (const ref of refs) expect(original.value.slice(ref.source.start, ref.source.end)).toBe(ref.source.text);
|
|
expect(refs[0].source).toMatchObject({ start: 7, end: 16, offsetUnit: 'utf16', text: 't1059.001', fieldPath: '/original/source' });
|
|
expect(original).toEqual(before);
|
|
});
|
|
it('uses rawValue when retained and traces nested JSON leaves otherwise', () => {
|
|
expect(recognize([{ ...field('T1003'), rawValue: 'T1059.001' }])[0]).toMatchObject({ id: 'T1059.001', source: { representation: 'rawValue' } });
|
|
const refs = recognize([{ ...field(''), value: { 'a/b': ['T1003', 'D3-CH'] } }]);
|
|
expect(refs.map(ref => ref.source.valuePath)).toEqual(['/a~1b/0', '/a~1b/1']);
|
|
});
|
|
it.each(['T10590', 'T1059.0001', 'T1059.01', 'hostT1059', 'T1059host', 'x_T1059', 'D3-CH-MORE', 'N/A', '-', 'Txxxx'])('does not guess from %s', value => {
|
|
expect(recognize([field(value, 'MITRE ATT&CK')])).toEqual([]);
|
|
});
|
|
it('preserves deprecated/revoked catalog status and refuses ambiguous titles', () => {
|
|
const entries = bundledReferenceCatalogs[0].entries;
|
|
const historical = entries.find(entry => entry.state !== 'active')!;
|
|
expect(recognize([field(historical.id)])[0].status).toBe(historical.state);
|
|
const { sha256: _, ...catalog } = bundledReferenceCatalogs[0];
|
|
const ambiguous = referenceSnapshot({ ...catalog, entries: [{ ...entries[0], id: 'T9998', title: 'Ambiguous' }, { ...entries[0], id: 'T9999', title: 'Ambiguous' }] });
|
|
expect(createReferenceRecognizer([ambiguous])([field('Ambiguous', 'MITRE ATT&CK')])).toEqual([]);
|
|
});
|
|
});
|