10-layer defense pipeline with kill chain mapping, self-healing, self-learning, and compliance reporting. Local-first, zero cloud deps. - 72 detection rules across 7 kill chain phases - 294 unit tests, 500+ attack corpus samples - Management dashboard (Next.js 15, 10 pages) - Automated resistance testing (2x daily, 31 probes) - MITRE ATLAS, OWASP LLM Top 10, EU AI Act compliance - Integrations: Next.js middleware, Ollama, n8n - PostgreSQL 17 + pgvector for persistent learning
159 lines
5.9 KiB
TypeScript
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()
|
|
})
|
|
})
|
|
})
|