clock = new SystemClock(); $this->config = new DDoSConfig(); $this->responseSystem = new AdaptiveResponseSystem($this->clock, $this->config); }); describe('AdaptiveResponseSystem', function () { it('allows low threat requests through', function () { $assessment = createThreatAssessment(ThreatLevel::LOW, 0.1); $request = createTestRequest('192.168.1.100', 'GET', '/api/users'); $response = $this->responseSystem->executeResponse($assessment, $request); expect($response->action)->toBe('allow'); expect($response->shouldBlock)->toBeFalse(); expect($response->httpStatusCode)->toBe(200); }); it('applies rate limiting for medium threats', function () { $assessment = createThreatAssessment(ThreatLevel::MEDIUM, 0.5, [AttackPattern::VOLUMETRIC_ATTACK]); $request = createTestRequest('192.168.1.200', 'GET', '/api/data'); $response = $this->responseSystem->executeResponse($assessment, $request); expect($response->action)->toBe('rate_limit'); expect($response->shouldBlock)->toBeFalse(); expect($response->rateLimitHeaders)->toHaveKeys(['X-RateLimit-Limit', 'X-RateLimit-Remaining']); expect($response->httpStatusCode)->toBe(429); }); it('blocks high threat requests', function () { $assessment = createThreatAssessment(ThreatLevel::HIGH, 0.8, [AttackPattern::BOT_ATTACK]); $request = createTestRequest('192.168.1.300', 'POST', '/api/sensitive'); $response = $this->responseSystem->executeResponse($assessment, $request); expect($response->action)->toBe('block'); expect($response->shouldBlock)->toBeTrue(); expect($response->httpStatusCode)->toBe(403); expect($response->blockDuration)->toBeInstanceOf(Duration::class); }); it('immediately blocks critical threats', function () { $assessment = createThreatAssessment(ThreatLevel::CRITICAL, 0.95, [ AttackPattern::VOLUMETRIC_ATTACK, AttackPattern::APPLICATION_LAYER_ATTACK, ]); $request = createTestRequest('192.168.1.400', 'POST', '/admin/delete'); $response = $this->responseSystem->executeResponse($assessment, $request); expect($response->action)->toBe('block'); expect($response->shouldBlock)->toBeTrue(); expect($response->httpStatusCode)->toBe(403); expect($response->blockDuration->toMinutes())->toBeGreaterThan(60); // Long block }); it('issues captcha challenges for suspicious requests', function () { $assessment = createThreatAssessment(ThreatLevel::MEDIUM, 0.6, [AttackPattern::BOT_ATTACK]); $request = createTestRequest('192.168.1.500', 'GET', '/api/search'); $response = $this->responseSystem->executeResponse($assessment, $request); expect($response->action)->toBe('captcha_challenge'); expect($response->shouldBlock)->toBeFalse(); expect($response->challengeData)->toHaveKey('captcha_token'); expect($response->httpStatusCode)->toBe(202); }); it('requires proof of work for sustained attacks', function () { $assessment = createThreatAssessment(ThreatLevel::HIGH, 0.75, [AttackPattern::COORDINATED_ATTACK]); $request = createTestRequest('192.168.1.600', 'GET', '/api/expensive-operation'); // Enable proof of work in config $config = new DDoSConfig(enableProofOfWork: true); $responseSystem = new AdaptiveResponseSystem($this->clock, $config); $response = $responseSystem->executeResponse($assessment, $request); expect($response->action)->toBe('proof_of_work'); expect($response->challengeData)->toHaveKeys(['difficulty', 'challenge', 'algorithm']); expect($response->httpStatusCode)->toBe(202); }); it('adapts response based on attack patterns', function () { // Volumetric attack should trigger rate limiting $volumetricAssessment = createThreatAssessment(ThreatLevel::MEDIUM, 0.6, [AttackPattern::VOLUMETRIC_ATTACK]); $request1 = createTestRequest('192.168.1.700', 'GET', '/api/data'); $response1 = $this->responseSystem->executeResponse($volumetricAssessment, $request1); expect($response1->action)->toBe('rate_limit'); // Application layer attack should trigger blocking $appLayerAssessment = createThreatAssessment(ThreatLevel::MEDIUM, 0.6, [AttackPattern::APPLICATION_LAYER_ATTACK]); $request2 = createTestRequest('192.168.1.701', 'POST', '/api/upload'); $response2 = $this->responseSystem->executeResponse($appLayerAssessment, $request2); expect($response2->action)->toBe('block'); }); it('escalates responses for repeated offenses', function () { $clientIp = '192.168.1.800'; // First offense - rate limit $assessment1 = createThreatAssessment(ThreatLevel::MEDIUM, 0.5); $request1 = createTestRequest($clientIp, 'GET', '/api/data1'); $response1 = $this->responseSystem->executeResponse($assessment1, $request1); expect($response1->action)->toBe('rate_limit'); // Record the offense $this->responseSystem->recordOffense($clientIp, $assessment1); // Second offense - should escalate to block $assessment2 = createThreatAssessment(ThreatLevel::MEDIUM, 0.5); $request2 = createTestRequest($clientIp, 'GET', '/api/data2'); $response2 = $this->responseSystem->executeResponse($assessment2, $request2); expect($response2->action)->toBe('block'); }); it('applies geographic blocking when enabled', function () { $config = new DDoSConfig( enableGeographicBlocking: true, blockedCountries: ['CN', 'RU'] ); $responseSystem = new AdaptiveResponseSystem($this->clock, $config); $assessment = createThreatAssessment(ThreatLevel::LOW, 0.2); $request = createTestRequest('203.0.113.195', 'GET', '/api/test'); // Assume this IP is from CN // Mock geographic detection $responseSystem->setGeographicInfo('203.0.113.195', 'CN'); $response = $responseSystem->executeResponse($assessment, $request); expect($response->action)->toBe('block'); expect($response->blockReason)->toContain('geographic'); }); it('allows trusted IPs through regardless of threat level', function () { $config = new DDoSConfig(trustedIps: ['192.168.1.999']); $responseSystem = new AdaptiveResponseSystem($this->clock, $config); $assessment = createThreatAssessment(ThreatLevel::CRITICAL, 0.95); $request = createTestRequest('192.168.1.999', 'GET', '/admin/critical'); $response = $responseSystem->executeResponse($assessment, $request); expect($response->action)->toBe('allow'); expect($response->shouldBlock)->toBeFalse(); }); it('bypasses protection for exempt paths', function () { $config = new DDoSConfig(exemptPaths: ['/health', '/monitoring']); $responseSystem = new AdaptiveResponseSystem($this->clock, $config); $assessment = createThreatAssessment(ThreatLevel::HIGH, 0.8); $request = createTestRequest('192.168.1.100', 'GET', '/health/check'); $response = $responseSystem->executeResponse($assessment, $request); expect($response->action)->toBe('allow'); expect($response->shouldBlock)->toBeFalse(); }); it('provides detailed response metrics', function () { $assessment = createThreatAssessment(ThreatLevel::MEDIUM, 0.6); $request = createTestRequest('192.168.1.100', 'GET', '/api/test'); $response = $this->responseSystem->executeResponse($assessment, $request); expect($response->metrics)->toHaveKeys([ 'processing_time_ms', 'decision_confidence', 'escalation_level', 'historical_offenses', ]); expect($response->metrics['processing_time_ms'])->toBeLessThan(100); expect($response->metrics['decision_confidence'])->toBeBetween(0.0, 1.0); }); it('handles circuit breaker integration', function () { $config = new DDoSConfig(enableCircuitBreakerIntegration: true); $responseSystem = new AdaptiveResponseSystem($this->clock, $config); // Simulate circuit breaker open state $responseSystem->setCircuitBreakerState('open'); $assessment = createThreatAssessment(ThreatLevel::LOW, 0.1); $request = createTestRequest('192.168.1.100', 'GET', '/api/test'); $response = $responseSystem->executeResponse($assessment, $request); expect($response->action)->toBe('rate_limit'); // Fallback during circuit breaker open expect($response->responseHeaders)->toHaveKey('X-Circuit-Breaker-State'); }); it('generates adaptive rate limits based on system load', function () { $assessment = createThreatAssessment(ThreatLevel::MEDIUM, 0.5); $request = createTestRequest('192.168.1.100', 'GET', '/api/data'); // Simulate high system load $this->responseSystem->setSystemLoad(0.9); $response = $this->responseSystem->executeResponse($assessment, $request); expect($response->action)->toBe('rate_limit'); expect($response->rateLimitHeaders['X-RateLimit-Limit'])->toBeLessThan(60); // Stricter limits under load }); it('logs security events for blocked requests', function () { $assessment = createThreatAssessment(ThreatLevel::HIGH, 0.8); $request = createTestRequest('192.168.1.100', 'POST', '/api/sensitive'); $response = $this->responseSystem->executeResponse($assessment, $request); expect($response->securityEventLogged)->toBeTrue(); expect($response->logContext)->toHaveKeys([ 'client_ip', 'threat_level', 'attack_patterns', 'response_action', ]); }); it('provides response recommendations', function () { $assessment = createThreatAssessment(ThreatLevel::MEDIUM, 0.6, [AttackPattern::VOLUMETRIC_ATTACK]); $request = createTestRequest('192.168.1.100', 'GET', '/api/data'); $recommendations = $this->responseSystem->getResponseRecommendations($assessment, $request); expect($recommendations)->toBeArray(); expect($recommendations)->toContain('implement_rate_limiting'); expect($recommendations)->toContain('monitor_traffic_patterns'); }); }); describe('Response Strategy Selection', function () { it('selects appropriate strategy for different attack types', function () { $strategies = [ [AttackPattern::VOLUMETRIC_ATTACK, 'rate_limit'], [AttackPattern::BOT_ATTACK, 'captcha_challenge'], [AttackPattern::APPLICATION_LAYER_ATTACK, 'block'], [AttackPattern::DISTRIBUTED_ATTACK, 'rate_limit'], [AttackPattern::COORDINATED_ATTACK, 'block'], ]; foreach ($strategies as [$pattern, $expectedAction]) { $assessment = createThreatAssessment(ThreatLevel::MEDIUM, 0.6, [$pattern]); $request = createTestRequest('192.168.1.100', 'GET', '/api/test'); $response = $this->responseSystem->executeResponse($assessment, $request); expect($response->action)->toBe($expectedAction); } }); it('combines multiple strategies for complex attacks', function () { $assessment = createThreatAssessment(ThreatLevel::HIGH, 0.8, [ AttackPattern::VOLUMETRIC_ATTACK, AttackPattern::BOT_ATTACK, AttackPattern::APPLICATION_LAYER_ATTACK, ]); $request = createTestRequest('192.168.1.100', 'POST', '/api/critical'); $response = $this->responseSystem->executeResponse($assessment, $request); expect($response->action)->toBe('block'); // Most restrictive action expect($response->additionalMeasures)->toContain('enhanced_logging'); expect($response->additionalMeasures)->toContain('alert_security_team'); }); it('adjusts strategy based on request context', function () { // Same threat level but different paths should get different responses $assessment = createThreatAssessment(ThreatLevel::MEDIUM, 0.6); $publicRequest = createTestRequest('192.168.1.100', 'GET', '/api/public-data'); $adminRequest = createTestRequest('192.168.1.100', 'GET', '/admin/users'); $publicResponse = $this->responseSystem->executeResponse($assessment, $publicRequest); $adminResponse = $this->responseSystem->executeResponse($assessment, $adminRequest); // Admin endpoint should be more strictly protected expect($adminResponse->action)->toBeIn(['block', 'captcha_challenge']); expect($publicResponse->action)->toBeIn(['allow', 'rate_limit']); }); }); describe('Performance', function () { it('completes response execution within time limit', function () { $assessment = createThreatAssessment(ThreatLevel::MEDIUM, 0.5); $request = createTestRequest('192.168.1.100', 'GET', '/api/test'); $start = microtime(true); $response = $this->responseSystem->executeResponse($assessment, $request); $duration = microtime(true) - $start; expect($duration)->toBeLessThan(0.05); // Should complete within 50ms expect($response)->not()->toBeNull(); }); it('handles high request volume efficiently', function () { $start = microtime(true); // Process 100 responses for ($i = 1; $i <= 100; $i++) { $assessment = createThreatAssessment(ThreatLevel::LOW, 0.1); $ip = '192.168.1.' . ($i % 254 + 1); $request = createTestRequest($ip, 'GET', "/api/test{$i}"); $this->responseSystem->executeResponse($assessment, $request); } $duration = microtime(true) - $start; $avgPerResponse = $duration / 100; expect($avgPerResponse)->toBeLessThan(0.01); // Average <10ms per response }); }); // Helper functions are now in ../Helpers/TestHelpers.php