techniques.test.mjs (3645B)
1 import { test } from 'node:test'; 2 import assert from 'node:assert/strict'; 3 import { loadTs } from './_loadts.mjs'; 4 5 const lib = await loadTs('src/lib/techniques.ts'); 6 const { 7 toolSlug, toolNamespace, routeSourceOf, toolRoute, 8 matches, filterTechniques, countActive, isEmpty, 9 filtersToSearch, filtersFromSearch, EMPTY_FILTERS, 10 } = lib; 11 12 const tech = (over = {}) => ({ 13 id: 'gtfo:vim:shell:0:sudo', toolId: 'gtfo:vim', toolName: 'vim', name: 'shell', 14 source: 'GTFOBins', platform: ['Linux'], capability: ['Execution', 'Privilege Escalation'], 15 nativeCategory: ['shell'], command: 'sudo vim -c \':!/bin/sh\'', mitre: ['T1059'], 16 references: ['https://gtfobins.github.io/gtfobins/vim/'], ...over, 17 }); 18 19 test('toolSlug: namespace stripped, lowercased, non-alnum collapsed', () => { 20 assert.equal(toolSlug('gtfo:vim'), 'vim'); 21 assert.equal(toolSlug('wadcoms:Impacket-GetUserSPNs'), 'impacket-getuserspns'); 22 assert.equal(toolSlug('lolbas:Microsoft.Workflow.Compiler'), 'microsoft-workflow-compiler'); 23 assert.equal(toolSlug('daemon:--weird--'), 'weird'); 24 }); 25 26 test('routing: namespace → deck route', () => { 27 assert.equal(toolNamespace('wadcoms:Certipy'), 'wadcoms'); 28 assert.equal(routeSourceOf('gtfo:vim'), 'GTFOBins'); 29 assert.equal(routeSourceOf('unknown:x'), 'DAEMON'); 30 assert.equal(toolRoute('lolbas:Certutil'), 'lolbas/certutil'); 31 assert.equal(toolRoute('wadcoms:Impacket-secretsdump'), 'wadcoms/impacket-secretsdump'); 32 }); 33 34 test('matches: OR within a facet, AND across facets', () => { 35 const t = tech(); 36 assert.equal(matches(t, EMPTY_FILTERS), true); 37 assert.equal(matches(t, { ...EMPTY_FILTERS, platform: ['Linux'] }), true); 38 assert.equal(matches(t, { ...EMPTY_FILTERS, platform: ['Windows'] }), false); 39 assert.equal(matches(t, { ...EMPTY_FILTERS, platform: ['Windows', 'Linux'] }), true); 40 assert.equal(matches(t, { ...EMPTY_FILTERS, platform: ['Linux'], capability: ['Discovery'] }), false); 41 assert.equal(matches(t, { ...EMPTY_FILTERS, source: ['LOLBAS'] }), false); 42 }); 43 44 test('matches: addedOnly excludes rows without the NEW flag; query is a case-insensitive substring', () => { 45 assert.equal(matches(tech(), { ...EMPTY_FILTERS, addedOnly: true }), false); 46 assert.equal(matches(tech({ added: true }), { ...EMPTY_FILTERS, addedOnly: true }), true); 47 assert.equal(matches(tech(), { ...EMPTY_FILTERS, query: 'BIN/SH' }), true); 48 assert.equal(matches(tech(), { ...EMPTY_FILTERS, query: 't1059' }), true); 49 assert.equal(matches(tech(), { ...EMPTY_FILTERS, query: 'powershell' }), false); 50 }); 51 52 test('filterTechniques / countActive / isEmpty', () => { 53 const list = [tech(), tech({ id: 'x', source: 'LOLBAS', platform: ['Windows'] })]; 54 assert.equal(filterTechniques(list, { ...EMPTY_FILTERS, source: ['LOLBAS'] }).length, 1); 55 assert.equal(countActive({ ...EMPTY_FILTERS, platform: ['Linux'], capability: ['a', 'b'] }), 3); 56 assert.equal(isEmpty(EMPTY_FILTERS), true); 57 assert.equal(isEmpty({ ...EMPTY_FILTERS, query: 'x' }), false); 58 assert.equal(isEmpty({ ...EMPTY_FILTERS, addedOnly: true }), false); 59 }); 60 61 test('filtersToSearch ↔ filtersFromSearch round-trips', () => { 62 const f = { platform: ['Linux', 'macOS'], capability: ['File Read'], source: ['GTFOBins'], query: 'tar ', addedOnly: true }; 63 const qs = filtersToSearch(f); 64 assert.deepEqual(filtersFromSearch(qs), { ...f, query: 'tar' }); 65 assert.deepEqual(filtersFromSearch('?' + qs), { ...f, query: 'tar' }); 66 assert.equal(filtersToSearch(EMPTY_FILTERS), ''); 67 assert.deepEqual(filtersFromSearch(''), EMPTY_FILTERS); 68 assert.deepEqual(filtersFromSearch('?p=Windows,,&new=0'), { ...EMPTY_FILTERS, platform: ['Windows'] }); 69 });