Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
278
tests/Framework/DDoS/ValueObjects/DDoSAssessmentTest.php
Normal file
278
tests/Framework/DDoS/ValueObjects/DDoSAssessmentTest.php
Normal file
@@ -0,0 +1,278 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Framework\Core\ValueObjects\Duration;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\DDoS\ValueObjects\AttackPattern;
|
||||
use App\Framework\DDoS\ValueObjects\DDoSAssessment;
|
||||
use App\Framework\DDoS\ValueObjects\ThreatLevel;
|
||||
use App\Framework\Http\IpAddress;
|
||||
|
||||
require_once __DIR__ . '/../Helpers/TestHelpers.php';
|
||||
|
||||
beforeEach(function () {
|
||||
$this->clock = new SystemClock();
|
||||
});
|
||||
|
||||
describe('DDoSAssessment', function () {
|
||||
|
||||
it('creates valid assessment with all required data', function () {
|
||||
$assessment = new DDoSAssessment(
|
||||
threatLevel: ThreatLevel::MEDIUM,
|
||||
attackPatterns: [AttackPattern::VOLUMETRIC_ATTACK],
|
||||
clientIp: IpAddress::from('192.168.1.100'),
|
||||
analysisResults: ['traffic_score' => 0.6],
|
||||
confidence: 0.8,
|
||||
recommendedAction: 'rate_limit',
|
||||
processingTime: Duration::fromMilliseconds(50),
|
||||
timestamp: $this->clock->time()
|
||||
);
|
||||
|
||||
expect($assessment->threatLevel)->toBe(ThreatLevel::MEDIUM);
|
||||
expect($assessment->attackPatterns)->toContain(AttackPattern::VOLUMETRIC_ATTACK);
|
||||
expect($assessment->clientIp->value)->toBe('192.168.1.100');
|
||||
expect($assessment->confidence)->toBe(0.8);
|
||||
expect($assessment->recommendedAction)->toBe('rate_limit');
|
||||
});
|
||||
|
||||
it('creates safe assessment for normal traffic', function () {
|
||||
$assessment = DDoSAssessment::createSafe($this->clock);
|
||||
|
||||
expect($assessment->threatLevel)->toBe(ThreatLevel::LOW);
|
||||
expect($assessment->attackPatterns)->toBeEmpty();
|
||||
expect($assessment->confidence)->toBeLessThan(0.3);
|
||||
expect($assessment->recommendedAction)->toBe('allow');
|
||||
});
|
||||
|
||||
it('creates critical assessment for severe threats', function () {
|
||||
$assessment = DDoSAssessment::createCritical(
|
||||
clientIp: IpAddress::from('10.0.0.1'),
|
||||
attackPatterns: [AttackPattern::VOLUMETRIC_ATTACK, AttackPattern::BOT_ATTACK],
|
||||
analysisResults: ['threat_score' => 0.95],
|
||||
clock: $this->clock
|
||||
);
|
||||
|
||||
expect($assessment->threatLevel)->toBe(ThreatLevel::CRITICAL);
|
||||
expect($assessment->attackPatterns)->toHaveCount(2);
|
||||
expect($assessment->confidence)->toBeGreaterThan(0.9);
|
||||
expect($assessment->recommendedAction)->toBe('block');
|
||||
});
|
||||
|
||||
it('determines if request should be blocked', function () {
|
||||
$lowThreat = new DDoSAssessment(
|
||||
threatLevel: ThreatLevel::LOW,
|
||||
attackPatterns: [],
|
||||
clientIp: IpAddress::from('192.168.1.100'),
|
||||
analysisResults: [],
|
||||
confidence: 0.2,
|
||||
recommendedAction: 'allow',
|
||||
processingTime: Duration::fromMilliseconds(10),
|
||||
timestamp: $this->clock->time()
|
||||
);
|
||||
|
||||
$highThreat = new DDoSAssessment(
|
||||
threatLevel: ThreatLevel::HIGH,
|
||||
attackPatterns: [AttackPattern::APPLICATION_LAYER_ATTACK],
|
||||
clientIp: IpAddress::from('192.168.1.100'),
|
||||
analysisResults: [],
|
||||
confidence: 0.8,
|
||||
recommendedAction: 'block',
|
||||
processingTime: Duration::fromMilliseconds(10),
|
||||
timestamp: $this->clock->time()
|
||||
);
|
||||
|
||||
expect($lowThreat->shouldBlock())->toBeFalse();
|
||||
expect($highThreat->shouldBlock())->toBeTrue();
|
||||
});
|
||||
|
||||
it('calculates threat score from analysis results', function () {
|
||||
$assessment = new DDoSAssessment(
|
||||
threatLevel: ThreatLevel::MEDIUM,
|
||||
attackPatterns: [],
|
||||
clientIp: IpAddress::from('192.168.1.100'),
|
||||
analysisResults: [
|
||||
'traffic_patterns' => ['threat_score' => 0.6],
|
||||
'geo_anomalies' => ['threat_score' => 0.4],
|
||||
'waf_analysis' => ['threat_score' => 0.8],
|
||||
],
|
||||
confidence: 0.7,
|
||||
recommendedAction: 'rate_limit',
|
||||
processingTime: Duration::fromMilliseconds(25),
|
||||
timestamp: $this->clock->time()
|
||||
);
|
||||
|
||||
$threatScore = $assessment->getThreatScore();
|
||||
|
||||
expect($threatScore)->toBeBetween(0.0, 1.0);
|
||||
expect($threatScore)->toBeGreaterThan(0.5); // Should be influenced by high scores
|
||||
});
|
||||
|
||||
it('provides human readable summary', function () {
|
||||
$assessment = new DDoSAssessment(
|
||||
threatLevel: ThreatLevel::HIGH,
|
||||
attackPatterns: [AttackPattern::VOLUMETRIC_ATTACK, AttackPattern::BOT_ATTACK],
|
||||
clientIp: IpAddress::from('192.168.1.100'),
|
||||
analysisResults: ['threat_score' => 0.8],
|
||||
confidence: 0.9,
|
||||
recommendedAction: 'block',
|
||||
processingTime: Duration::fromMilliseconds(75),
|
||||
timestamp: $this->clock->time()
|
||||
);
|
||||
|
||||
$summary = $assessment->getSummary();
|
||||
|
||||
expect($summary)->toBeString();
|
||||
expect($summary)->toContain('HIGH');
|
||||
expect($summary)->toContain('192.168.1.100');
|
||||
expect($summary)->toContain('VOLUMETRIC_ATTACK');
|
||||
expect($summary)->toContain('BOT_ATTACK');
|
||||
});
|
||||
|
||||
it('exports to array for serialization', function () {
|
||||
$assessment = new DDoSAssessment(
|
||||
threatLevel: ThreatLevel::MEDIUM,
|
||||
attackPatterns: [AttackPattern::DISTRIBUTED_ATTACK],
|
||||
clientIp: IpAddress::from('192.168.1.100'),
|
||||
analysisResults: ['geo_score' => 0.6],
|
||||
confidence: 0.75,
|
||||
recommendedAction: 'captcha_challenge',
|
||||
processingTime: Duration::fromMilliseconds(30),
|
||||
timestamp: $this->clock->time()
|
||||
);
|
||||
|
||||
$array = $assessment->toArray();
|
||||
|
||||
expect($array)->toHaveKeys([
|
||||
'threat_level',
|
||||
'attack_patterns',
|
||||
'client_ip',
|
||||
'analysis_results',
|
||||
'confidence',
|
||||
'recommended_action',
|
||||
'processing_time_ms',
|
||||
'timestamp',
|
||||
]);
|
||||
|
||||
expect($array['threat_level'])->toBe('MEDIUM');
|
||||
expect($array['client_ip'])->toBe('192.168.1.100');
|
||||
expect($array['confidence'])->toBe(0.75);
|
||||
});
|
||||
|
||||
it('creates assessment from array data', function () {
|
||||
$data = [
|
||||
'threat_level' => 'HIGH',
|
||||
'attack_patterns' => ['VOLUMETRIC_ATTACK'],
|
||||
'client_ip' => '10.0.0.1',
|
||||
'analysis_results' => ['traffic_score' => 0.8],
|
||||
'confidence' => 0.85,
|
||||
'recommended_action' => 'block',
|
||||
'processing_time_ms' => 40,
|
||||
'timestamp' => $this->clock->time()->format('c'),
|
||||
];
|
||||
|
||||
$assessment = DDoSAssessment::fromArray($data, $this->clock);
|
||||
|
||||
expect($assessment->threatLevel)->toBe(ThreatLevel::HIGH);
|
||||
expect($assessment->attackPatterns)->toContain(AttackPattern::VOLUMETRIC_ATTACK);
|
||||
expect($assessment->clientIp->value)->toBe('10.0.0.1');
|
||||
expect($assessment->confidence)->toBe(0.85);
|
||||
});
|
||||
|
||||
it('validates confidence values', function () {
|
||||
expect(fn () => new DDoSAssessment(
|
||||
threatLevel: ThreatLevel::LOW,
|
||||
attackPatterns: [],
|
||||
clientIp: IpAddress::from('192.168.1.100'),
|
||||
analysisResults: [],
|
||||
confidence: 1.5, // Invalid confidence > 1.0
|
||||
recommendedAction: 'allow',
|
||||
processingTime: Duration::fromMilliseconds(10),
|
||||
timestamp: $this->clock->time()
|
||||
))->toThrow(InvalidArgumentException::class);
|
||||
|
||||
expect(fn () => new DDoSAssessment(
|
||||
threatLevel: ThreatLevel::LOW,
|
||||
attackPatterns: [],
|
||||
clientIp: IpAddress::from('192.168.1.100'),
|
||||
analysisResults: [],
|
||||
confidence: -0.1, // Invalid confidence < 0.0
|
||||
recommendedAction: 'allow',
|
||||
processingTime: Duration::fromMilliseconds(10),
|
||||
timestamp: $this->clock->time()
|
||||
))->toThrow(InvalidArgumentException::class);
|
||||
});
|
||||
|
||||
it('compares threat levels correctly', function () {
|
||||
$lowAssessment = DDoSAssessment::createSafe($this->clock);
|
||||
$highAssessment = DDoSAssessment::createCritical(
|
||||
clientIp: IpAddress::from('10.0.0.1'),
|
||||
attackPatterns: [AttackPattern::VOLUMETRIC_ATTACK],
|
||||
analysisResults: [],
|
||||
clock: $this->clock
|
||||
);
|
||||
|
||||
expect($lowAssessment->isLessThreatThan($highAssessment))->toBeTrue();
|
||||
expect($highAssessment->isMoreThreatThan($lowAssessment))->toBeTrue();
|
||||
expect($lowAssessment->isMoreThreatThan($highAssessment))->toBeFalse();
|
||||
});
|
||||
|
||||
it('identifies coordinated attacks', function () {
|
||||
$coordinatedAssessment = new DDoSAssessment(
|
||||
threatLevel: ThreatLevel::HIGH,
|
||||
attackPatterns: [
|
||||
AttackPattern::VOLUMETRIC_ATTACK,
|
||||
AttackPattern::DISTRIBUTED_ATTACK,
|
||||
AttackPattern::COORDINATED_ATTACK,
|
||||
],
|
||||
clientIp: IpAddress::from('192.168.1.100'),
|
||||
analysisResults: [],
|
||||
confidence: 0.9,
|
||||
recommendedAction: 'block',
|
||||
processingTime: Duration::fromMilliseconds(50),
|
||||
timestamp: $this->clock->time()
|
||||
);
|
||||
|
||||
expect($coordinatedAssessment->isCoordinatedAttack())->toBeTrue();
|
||||
expect($coordinatedAssessment->getAttackComplexity())->toBe('high');
|
||||
});
|
||||
|
||||
it('calculates assessment age', function () {
|
||||
$pastTime = $this->clock->time()->subtract(Duration::fromMinutes(5));
|
||||
|
||||
$assessment = new DDoSAssessment(
|
||||
threatLevel: ThreatLevel::MEDIUM,
|
||||
attackPatterns: [],
|
||||
clientIp: IpAddress::from('192.168.1.100'),
|
||||
analysisResults: [],
|
||||
confidence: 0.5,
|
||||
recommendedAction: 'rate_limit',
|
||||
processingTime: Duration::fromMilliseconds(20),
|
||||
timestamp: $pastTime
|
||||
);
|
||||
|
||||
$age = $assessment->getAge($this->clock->time());
|
||||
|
||||
expect($age->toMinutes())->toBeGreaterThan(4);
|
||||
expect($age->toMinutes())->toBeLessThan(6);
|
||||
});
|
||||
|
||||
it('determines if assessment is stale', function () {
|
||||
$oldTime = $this->clock->time()->subtract(Duration::fromMinutes(10));
|
||||
|
||||
$assessment = new DDoSAssessment(
|
||||
threatLevel: ThreatLevel::MEDIUM,
|
||||
attackPatterns: [],
|
||||
clientIp: IpAddress::from('192.168.1.100'),
|
||||
analysisResults: [],
|
||||
confidence: 0.5,
|
||||
recommendedAction: 'rate_limit',
|
||||
processingTime: Duration::fromMilliseconds(20),
|
||||
timestamp: $oldTime
|
||||
);
|
||||
|
||||
expect($assessment->isStale($this->clock->time(), Duration::fromMinutes(5)))->toBeTrue();
|
||||
expect($assessment->isStale($this->clock->time(), Duration::fromMinutes(15)))->toBeFalse();
|
||||
});
|
||||
|
||||
});
|
||||
84
tests/Framework/DDoS/ValueObjects/ThreatScoreTest.php
Normal file
84
tests/Framework/DDoS/ValueObjects/ThreatScoreTest.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Framework\DDoS\ValueObjects\ThreatScore;
|
||||
|
||||
describe('ThreatScore Value Object', function () {
|
||||
|
||||
it('creates threat score from float', function () {
|
||||
$threatScore = ThreatScore::fromFloat(0.8);
|
||||
|
||||
expect($threatScore->getScore()->value())->toBe(0.8);
|
||||
expect($threatScore->requiresBlocking())->toBeTrue();
|
||||
});
|
||||
|
||||
it('creates safe threat score', function () {
|
||||
$safe = ThreatScore::safe();
|
||||
|
||||
expect($safe->getScore()->value())->toBe(0.0);
|
||||
expect($safe->requiresBlocking())->toBeFalse();
|
||||
expect($safe->getRecommendedAction())->toBe('allow');
|
||||
});
|
||||
|
||||
it('creates critical threat score', function () {
|
||||
$critical = ThreatScore::critical();
|
||||
|
||||
expect($critical->getScore()->isCritical())->toBeTrue();
|
||||
expect($critical->requiresBlocking())->toBeTrue();
|
||||
expect($critical->getRecommendedAction())->toBe('block_immediately');
|
||||
});
|
||||
|
||||
it('determines correct actions based on level', function () {
|
||||
$low = ThreatScore::fromFloat(0.1);
|
||||
$medium = ThreatScore::fromFloat(0.5);
|
||||
$high = ThreatScore::fromFloat(0.8);
|
||||
$critical = ThreatScore::fromFloat(0.95);
|
||||
|
||||
expect($low->getRecommendedAction())->toBe('allow');
|
||||
expect($medium->getRecommendedAction())->toBe('rate_limit');
|
||||
expect($high->getRecommendedAction())->toBe('enhanced_monitoring');
|
||||
expect($critical->getRecommendedAction())->toBe('block_immediately');
|
||||
});
|
||||
|
||||
it('creates from multiple analyses', function () {
|
||||
$analyses = [
|
||||
'traffic_patterns' => ['threat_score' => 0.8, 'indicators' => ['high_volume']],
|
||||
'geo_anomalies' => ['threat_score' => 0.6, 'indicators' => ['unusual_location']],
|
||||
'waf_analysis' => ['threat_score' => 0.9, 'indicators' => ['malicious_payload']],
|
||||
];
|
||||
|
||||
$threatScore = ThreatScore::fromAnalyses($analyses);
|
||||
|
||||
expect($threatScore->getScore()->value())->toBeGreaterThan(0.7);
|
||||
expect($threatScore->getIndicators())->toContain('high_volume');
|
||||
expect($threatScore->getSources())->toContain('traffic_patterns');
|
||||
});
|
||||
|
||||
it('combines threat scores correctly', function () {
|
||||
$score1 = ThreatScore::fromFloat(0.8);
|
||||
$score2 = ThreatScore::fromFloat(0.6);
|
||||
|
||||
$combined = $score1->combineWith($score2, 0.7);
|
||||
|
||||
expect($combined->getScore()->value())->toBe(0.74); // 0.8 * 0.7 + 0.6 * 0.3
|
||||
});
|
||||
|
||||
it('provides detailed description', function () {
|
||||
$threatScore = ThreatScore::fromFloat(0.75);
|
||||
$description = $threatScore->getDescription();
|
||||
|
||||
expect($description)->toContain('High threat level');
|
||||
expect($description)->toContain('75.0%');
|
||||
});
|
||||
|
||||
it('serializes and deserializes correctly', function () {
|
||||
$original = ThreatScore::fromFloat(0.8);
|
||||
$array = $original->toArray();
|
||||
$restored = ThreatScore::fromArray($array);
|
||||
|
||||
expect($restored->getScore()->value())->toBe($original->getScore()->value());
|
||||
expect($restored->requiresBlocking())->toBe($original->requiresBlocking());
|
||||
});
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user