shieldx/tests/unit/healing/HealingOrchestrator.test.ts
Rene Fichtmueller 1c4c034483 feat: ShieldX v0.3.0 — UnicodeScanner (L5), DNS Covert Channel rules, ATLAS v5.4 mappings
- Layer 4 EntropyScanner: Shannon entropy, Base32/Base64 detection, CVE-2025-55284
  ping/nslookup exfil, EchoLeak markdown pattern, DNS tunneling (iodine/dnscat)
- Layer 5 UnicodeScanner: ASCII Smuggling (U+E0000 Tags Block), Variant Selectors,
  Zero-Width steganography, CamoLeak image-ordering (CVE-2025-53773), homoglyphs,
  BiDi override, high-entropy URL params
- 30 DNS covert channel rules (dns-001 to dns-030)
- ATLASMapper: 29 techniques (ATLAS v5.4.0 Feb 2026), added AML.T0062 (Agent Tool
  Invocation), AML.TA0015 (C2 tactic), memory poisoning, multi-agent trust,
  CamoLeak, Unicode steganography mappings
- Rule count: 72 → 102
- Build: tsup 316ms, zero TypeScript errors
2026-03-31 16:32:16 +02:00

159 lines
5.9 KiB
TypeScript

import { describe, it, expect, beforeEach } from 'vitest'
import { HealingOrchestrator } from '../../../src/healing/HealingOrchestrator.js'
import type { KillChainPhase, ThreatLevel, HealingAction } from '../../../src/types/detection.js'
describe('HealingOrchestrator', () => {
let orchestrator: HealingOrchestrator
beforeEach(() => {
orchestrator = new HealingOrchestrator()
})
describe('determineAction()', () => {
describe('default action matrix', () => {
it('should return "allow" for no threat on any phase', () => {
const phases: KillChainPhase[] = [
'none', 'initial_access', 'privilege_escalation',
'reconnaissance', 'persistence', 'command_and_control',
'lateral_movement', 'actions_on_objective',
]
for (const phase of phases) {
const action = orchestrator.determineAction('none', phase)
expect(action).toBe('allow')
}
})
it('should return "sanitize" for initial_access + medium threat', () => {
const action = orchestrator.determineAction('medium', 'initial_access')
expect(action).toBe('sanitize')
})
it('should return "block" for initial_access + critical threat', () => {
const action = orchestrator.determineAction('critical', 'initial_access')
expect(action).toBe('block')
})
it('should return "block" for privilege_escalation + high threat', () => {
const action = orchestrator.determineAction('high', 'privilege_escalation')
expect(action).toBe('block')
})
it('should return "reset" for persistence + medium threat', () => {
const action = orchestrator.determineAction('medium', 'persistence')
expect(action).toBe('reset')
})
it('should return "reset" for persistence + critical threat', () => {
const action = orchestrator.determineAction('critical', 'persistence')
expect(action).toBe('reset')
})
it('should return "incident" for command_and_control + high threat', () => {
const action = orchestrator.determineAction('high', 'command_and_control')
expect(action).toBe('incident')
})
it('should return "incident" for lateral_movement + medium threat', () => {
const action = orchestrator.determineAction('medium', 'lateral_movement')
expect(action).toBe('incident')
})
it('should return "incident" for actions_on_objective + critical threat', () => {
const action = orchestrator.determineAction('critical', 'actions_on_objective')
expect(action).toBe('incident')
})
it('should return "warn" for low threats on most phases', () => {
expect(orchestrator.determineAction('low', 'initial_access')).toBe('warn')
expect(orchestrator.determineAction('low', 'privilege_escalation')).toBe('warn')
expect(orchestrator.determineAction('low', 'reconnaissance')).toBe('warn')
expect(orchestrator.determineAction('low', 'persistence')).toBe('warn')
})
it('should return "block" for low command_and_control', () => {
expect(orchestrator.determineAction('low', 'command_and_control')).toBe('block')
})
})
describe('phase 1 = sanitize (initial_access)', () => {
it('should default to sanitize for initial_access medium', () => {
expect(orchestrator.determineAction('medium', 'initial_access')).toBe('sanitize')
})
})
describe('phase 7 = incident (actions_on_objective)', () => {
it('should escalate to incident for actions_on_objective high/critical', () => {
expect(orchestrator.determineAction('high', 'actions_on_objective')).toBe('incident')
expect(orchestrator.determineAction('critical', 'actions_on_objective')).toBe('incident')
})
})
})
describe('custom phase strategies override defaults', () => {
it('should use custom strategy when provided', () => {
const customOrchestrator = new HealingOrchestrator({
healing: {
phaseStrategies: {
initial_access: 'block',
},
},
})
const action = customOrchestrator.determineAction('low', 'initial_access')
expect(action).toBe('block')
})
it('should fall back to default when custom strategy not defined for phase', () => {
const customOrchestrator = new HealingOrchestrator({
healing: {
phaseStrategies: {
initial_access: 'block',
},
},
})
// persistence has no custom override
const action = customOrchestrator.determineAction('high', 'persistence')
expect(action).toBe('reset')
})
})
describe('session reset trigger for persistence phase', () => {
it('should return reset for persistence phase at medium+', () => {
expect(orchestrator.determineAction('medium', 'persistence')).toBe('reset')
expect(orchestrator.determineAction('high', 'persistence')).toBe('reset')
expect(orchestrator.determineAction('critical', 'persistence')).toBe('reset')
})
})
describe('incident reporting for critical phases', () => {
const criticalPhases: KillChainPhase[] = [
'command_and_control',
'lateral_movement',
'actions_on_objective',
]
for (const phase of criticalPhases) {
it(`should trigger incident for ${phase} at high threat`, () => {
expect(orchestrator.determineAction('high', phase)).toBe('incident')
})
it(`should trigger incident for ${phase} at critical threat`, () => {
expect(orchestrator.determineAction('critical', phase)).toBe('incident')
})
}
})
describe('getSessionManager()', () => {
it('should return a SessionManager instance', () => {
const sm = orchestrator.getSessionManager()
expect(sm).toBeDefined()
})
})
describe('getIncidentReporter()', () => {
it('should return an IncidentReporter instance', () => {
const ir = orchestrator.getIncidentReporter()
expect(ir).toBeDefined()
})
})
})