- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
219 lines
7.4 KiB
PHP
219 lines
7.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Framework\Core\ValueObjects\Duration;
|
|
use App\Framework\DateTime\SystemClock;
|
|
use App\Framework\DDoS\DDoSConfig;
|
|
use App\Framework\DDoS\ValueObjects\ThreatLevel;
|
|
|
|
require_once __DIR__ . '/Helpers/TestHelpers.php';
|
|
|
|
beforeEach(function () {
|
|
$this->clock = new SystemClock();
|
|
|
|
// Create DDoS config for testing
|
|
$this->config = new DDoSConfig(
|
|
enabled: true,
|
|
volumetricThreshold: 100.0, // Lower threshold for faster testing
|
|
distributedThreshold: 0.8,
|
|
analysisWindow: Duration::fromMinutes(5),
|
|
trustedIps: ['127.0.0.1', '::1'],
|
|
exemptPaths: ['/health', '/ping']
|
|
);
|
|
|
|
// Mock the engine (we'll need to set up proper DI for real tests)
|
|
$this->engine = $this->createMockEngine();
|
|
});
|
|
|
|
describe('DDoS Protection Engine', function () {
|
|
|
|
it('allows normal requests through', function () {
|
|
$request = createTestRequest('192.168.1.100', 'GET', '/api/users');
|
|
|
|
$assessment = $this->engine->assessRequest($request);
|
|
|
|
expect($assessment->threatLevel)->toBe(ThreatLevel::LOW);
|
|
expect($assessment->shouldBlock)->toBeFalse();
|
|
expect($assessment->threatScore)->toBeLessThan(0.3);
|
|
});
|
|
|
|
it('detects high volume attacks from single IP', function () {
|
|
$attackerIp = '10.0.0.1';
|
|
$assessment = null;
|
|
|
|
// Simulate rapid requests from same IP
|
|
for ($i = 1; $i <= 20; $i++) {
|
|
$request = createTestRequest($attackerIp, 'GET', "/page{$i}");
|
|
$assessment = $this->engine->assessRequest($request);
|
|
|
|
if ($assessment->shouldBlock) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
expect($assessment->threatLevel)->toBeIn([ThreatLevel::HIGH, ThreatLevel::CRITICAL]);
|
|
expect($assessment->shouldBlock)->toBeTrue();
|
|
expect($assessment->threatScore)->toBeGreaterThan(0.7);
|
|
});
|
|
|
|
it('detects distributed attacks', function () {
|
|
$attackIps = ['1.2.3.4', '5.6.7.8', '9.10.11.12', '13.14.15.16'];
|
|
$assessments = [];
|
|
|
|
foreach ($attackIps as $ip) {
|
|
for ($i = 1; $i <= 15; $i++) {
|
|
$request = createTestRequest($ip, 'POST', '/api/login');
|
|
$assessments[] = $this->engine->assessRequest($request);
|
|
}
|
|
}
|
|
|
|
// Should detect distributed pattern
|
|
$lastAssessment = end($assessments);
|
|
expect($lastAssessment->threatLevel)->toBeIn([ThreatLevel::MEDIUM, ThreatLevel::HIGH]);
|
|
expect($lastAssessment->threatScore)->toBeGreaterThan(0.4);
|
|
});
|
|
|
|
it('blocks suspicious bot traffic', function () {
|
|
$request = createTestRequest('192.168.1.200', 'GET', '/sensitive-data', [
|
|
'User-Agent' => 'BadBot/1.0 (automated scraper)',
|
|
]);
|
|
|
|
$assessment = $this->engine->assessRequest($request);
|
|
|
|
expect($assessment->threatLevel)->toBeIn([ThreatLevel::MEDIUM, ThreatLevel::HIGH]);
|
|
expect($assessment->threatScore)->toBeGreaterThan(0.5);
|
|
});
|
|
|
|
it('allows trusted IPs through', function () {
|
|
$request = createTestRequest('127.0.0.1', 'GET', '/admin/sensitive');
|
|
|
|
$assessment = $this->engine->assessRequest($request);
|
|
|
|
expect($assessment->threatLevel)->toBe(ThreatLevel::LOW);
|
|
expect($assessment->shouldBlock)->toBeFalse();
|
|
});
|
|
|
|
it('bypasses exempt paths', function () {
|
|
$request = createTestRequest('10.0.0.1', 'GET', '/health');
|
|
|
|
$assessment = $this->engine->assessRequest($request);
|
|
|
|
expect($assessment->threatLevel)->toBe(ThreatLevel::LOW);
|
|
expect($assessment->shouldBlock)->toBeFalse();
|
|
});
|
|
|
|
it('escalates threat level with repeated attacks', function () {
|
|
$attackerIp = '192.168.1.50';
|
|
$assessments = [];
|
|
|
|
// First wave - should be low threat
|
|
for ($i = 1; $i <= 5; $i++) {
|
|
$request = createTestRequest($attackerIp, 'GET', "/api/data{$i}");
|
|
$assessments[] = $this->engine->assessRequest($request);
|
|
}
|
|
|
|
expect($assessments[0]->threatLevel)->toBe(ThreatLevel::LOW);
|
|
|
|
// Second wave - should escalate
|
|
for ($i = 6; $i <= 25; $i++) {
|
|
$request = createTestRequest($attackerIp, 'GET', "/api/data{$i}");
|
|
$assessments[] = $this->engine->assessRequest($request);
|
|
}
|
|
|
|
$lastAssessment = end($assessments);
|
|
expect($lastAssessment->threatLevel)->toBeIn([ThreatLevel::MEDIUM, ThreatLevel::HIGH, ThreatLevel::CRITICAL]);
|
|
expect($lastAssessment->threatScore)->toBeGreaterThan($assessments[0]->threatScore);
|
|
});
|
|
|
|
it('handles malformed requests gracefully', function () {
|
|
$request = createTestRequest('invalid-ip', 'INVALID_METHOD', '');
|
|
|
|
$assessment = $this->engine->assessRequest($request);
|
|
|
|
// Should not crash and should treat as suspicious
|
|
expect($assessment)->not()->toBeNull();
|
|
expect($assessment->threatLevel)->toBeIn([ThreatLevel::MEDIUM, ThreatLevel::HIGH]);
|
|
});
|
|
|
|
it('provides detailed assessment information', function () {
|
|
$request = createTestRequest('192.168.1.100', 'GET', '/api/test');
|
|
|
|
$assessment = $this->engine->assessRequest($request);
|
|
|
|
expect($assessment)->toHaveProperties([
|
|
'threatLevel',
|
|
'threatScore',
|
|
'shouldBlock',
|
|
'detectedPatterns',
|
|
'responseRecommendation',
|
|
]);
|
|
|
|
expect($assessment->threatScore)->toBeBetween(0.0, 1.0);
|
|
});
|
|
|
|
});
|
|
|
|
describe('DDoS Configuration', function () {
|
|
|
|
it('respects custom thresholds', function () {
|
|
$strictConfig = new DDoSConfig(
|
|
volumetricThreshold: 10.0, // Very low threshold
|
|
highThreatThreshold: 0.3 // Lower threshold for high threat
|
|
);
|
|
|
|
$engine = createEngineWithConfig($strictConfig);
|
|
$request = createTestRequest('192.168.1.100', 'GET', '/api/test');
|
|
|
|
// With strict config, even normal requests might be flagged
|
|
$assessment = $engine->assessRequest($request);
|
|
expect($assessment)->not()->toBeNull();
|
|
});
|
|
|
|
it('can be disabled', function () {
|
|
$disabledConfig = new DDoSConfig(enabled: false);
|
|
$engine = createEngineWithConfig($disabledConfig);
|
|
|
|
$request = createTestRequest('10.0.0.1', 'GET', '/api/test');
|
|
$assessment = $engine->assessRequest($request);
|
|
|
|
expect($assessment->threatLevel)->toBe(ThreatLevel::LOW);
|
|
expect($assessment->shouldBlock)->toBeFalse();
|
|
});
|
|
|
|
});
|
|
|
|
describe('Performance', function () {
|
|
|
|
it('completes assessment within reasonable time', function () {
|
|
$start = microtime(true);
|
|
|
|
$request = createTestRequest('192.168.1.100', 'GET', '/api/test');
|
|
$assessment = $this->engine->assessRequest($request);
|
|
|
|
$duration = microtime(true) - $start;
|
|
|
|
expect($duration)->toBeLessThan(0.1); // Should complete within 100ms
|
|
expect($assessment)->not()->toBeNull();
|
|
});
|
|
|
|
it('handles high request volume efficiently', function () {
|
|
$start = microtime(true);
|
|
|
|
// Process 100 requests
|
|
for ($i = 1; $i <= 100; $i++) {
|
|
$ip = '192.168.1.' . ($i % 254 + 1);
|
|
$request = createTestRequest($ip, 'GET', "/api/test{$i}");
|
|
$this->engine->assessRequest($request);
|
|
}
|
|
|
|
$duration = microtime(true) - $start;
|
|
$avgPerRequest = $duration / 100;
|
|
|
|
expect($avgPerRequest)->toBeLessThan(0.01); // Average <10ms per request
|
|
});
|
|
|
|
});
|
|
|
|
// Helper functions are now in Helpers/TestHelpers.php
|