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:
300
src/Framework/Waf/Rules/RuleEvaluationResult.php
Normal file
300
src/Framework/Waf/Rules/RuleEvaluationResult.php
Normal file
@@ -0,0 +1,300 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Waf\Rules;
|
||||
|
||||
use App\Framework\Core\ValueObjects\Duration;
|
||||
use App\Framework\Core\ValueObjects\Percentage;
|
||||
use App\Framework\Core\ValueObjects\Timestamp;
|
||||
use App\Framework\Waf\Rules\ValueObjects\RuleMatch;
|
||||
|
||||
/**
|
||||
* Result of rule engine evaluation
|
||||
*/
|
||||
final readonly class RuleEvaluationResult
|
||||
{
|
||||
public function __construct(
|
||||
public array $matches,
|
||||
public int $evaluatedRules,
|
||||
public int $skippedRules,
|
||||
public int $errorCount,
|
||||
public array $errors,
|
||||
public Duration $evaluationTime,
|
||||
public Timestamp $timestamp
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if any rules matched
|
||||
*/
|
||||
public function hasMatches(): bool
|
||||
{
|
||||
return ! empty($this->matches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of matches
|
||||
*/
|
||||
public function getMatchCount(): int
|
||||
{
|
||||
return count($this->matches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get matches by severity
|
||||
*/
|
||||
public function getMatchesBySeverity(string $severity): array
|
||||
{
|
||||
return array_filter(
|
||||
$this->matches,
|
||||
fn (RuleMatch $match) => $match->severity->value === $severity
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get critical matches
|
||||
*/
|
||||
public function getCriticalMatches(): array
|
||||
{
|
||||
return $this->getMatchesBySeverity('critical');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get high severity matches
|
||||
*/
|
||||
public function getHighSeverityMatches(): array
|
||||
{
|
||||
return $this->getMatchesBySeverity('high');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get blocking matches (matches that should block the request)
|
||||
*/
|
||||
public function getBlockingMatches(): array
|
||||
{
|
||||
return array_filter(
|
||||
$this->matches,
|
||||
fn (RuleMatch $match) => $match->shouldBlock()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get matches that should trigger alerts
|
||||
*/
|
||||
public function getAlertingMatches(): array
|
||||
{
|
||||
return array_filter(
|
||||
$this->matches,
|
||||
fn (RuleMatch $match) => $match->shouldAlert()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OWASP Top 10 matches
|
||||
*/
|
||||
public function getOwaspTop10Matches(): array
|
||||
{
|
||||
return array_filter(
|
||||
$this->matches,
|
||||
fn (RuleMatch $match) => $match->category->isOwaspTop10()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get matches by category
|
||||
*/
|
||||
public function getMatchesByCategory(string $category): array
|
||||
{
|
||||
return array_filter(
|
||||
$this->matches,
|
||||
fn (RuleMatch $match) => $match->category->value === $category
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if any blocking matches exist
|
||||
*/
|
||||
public function shouldBlock(): bool
|
||||
{
|
||||
return ! empty($this->getBlockingMatches());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if any alerting matches exist
|
||||
*/
|
||||
public function shouldAlert(): bool
|
||||
{
|
||||
return ! empty($this->getAlertingMatches());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if evaluation had errors
|
||||
*/
|
||||
public function hasErrors(): bool
|
||||
{
|
||||
return $this->errorCount > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get highest severity level from matches
|
||||
*/
|
||||
public function getHighestSeverity(): ?string
|
||||
{
|
||||
if (empty($this->matches)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$severityOrder = ['critical', 'high', 'medium', 'low', 'info'];
|
||||
|
||||
foreach ($severityOrder as $severity) {
|
||||
if (! empty($this->getMatchesBySeverity($severity))) {
|
||||
return $severity;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get categories of all matches
|
||||
*/
|
||||
public function getMatchedCategories(): array
|
||||
{
|
||||
$categories = [];
|
||||
|
||||
foreach ($this->matches as $match) {
|
||||
$categories[$match->category->value] = $match->category;
|
||||
}
|
||||
|
||||
return array_values($categories);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get rule IDs of all matches
|
||||
*/
|
||||
public function getMatchedRuleIds(): array
|
||||
{
|
||||
return array_map(
|
||||
fn (RuleMatch $match) => $match->ruleId->value,
|
||||
$this->matches
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate overall confidence score
|
||||
*/
|
||||
public function getOverallConfidence(): Percentage
|
||||
{
|
||||
if (empty($this->matches)) {
|
||||
return Percentage::from(0.0);
|
||||
}
|
||||
|
||||
$totalConfidence = 0.0;
|
||||
$confidenceCount = 0;
|
||||
|
||||
foreach ($this->matches as $match) {
|
||||
if ($match->confidence !== null) {
|
||||
$totalConfidence += $match->confidence->getValue();
|
||||
$confidenceCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($confidenceCount === 0) {
|
||||
return Percentage::from(0.0);
|
||||
}
|
||||
|
||||
return Percentage::from($totalConfidence / $confidenceCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate threat score based on matches
|
||||
*/
|
||||
public function getThreatScore(): Percentage
|
||||
{
|
||||
if (empty($this->matches)) {
|
||||
return Percentage::from(0.0);
|
||||
}
|
||||
|
||||
$score = 0.0;
|
||||
$maxScore = 0.0;
|
||||
|
||||
foreach ($this->matches as $match) {
|
||||
$matchScore = match ($match->severity->value) {
|
||||
'critical' => 25.0,
|
||||
'high' => 20.0,
|
||||
'medium' => 10.0,
|
||||
'low' => 5.0,
|
||||
'info' => 1.0,
|
||||
default => 0.0
|
||||
};
|
||||
|
||||
// Apply confidence modifier
|
||||
if ($match->confidence !== null) {
|
||||
$matchScore *= ($match->confidence->getValue() / 100.0);
|
||||
}
|
||||
|
||||
$score += $matchScore;
|
||||
$maxScore = max($maxScore, $matchScore);
|
||||
}
|
||||
|
||||
// Use weighted combination: 70% cumulative + 30% maximum
|
||||
$finalScore = ($score * 0.7) + ($maxScore * 0.3);
|
||||
|
||||
return Percentage::from(min(100.0, $finalScore));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get evaluation performance summary
|
||||
*/
|
||||
public function getPerformanceSummary(): array
|
||||
{
|
||||
$efficiency = 0.0;
|
||||
if ($this->evaluatedRules > 0) {
|
||||
$rulesPerMs = $this->evaluatedRules / max(1, $this->evaluationTime->toMilliseconds());
|
||||
$efficiency = $rulesPerMs * 1000; // Rules per second
|
||||
}
|
||||
|
||||
return [
|
||||
'evaluation_time_ms' => $this->evaluationTime->toMilliseconds(),
|
||||
'evaluated_rules' => $this->evaluatedRules,
|
||||
'skipped_rules' => $this->skippedRules,
|
||||
'error_count' => $this->errorCount,
|
||||
'rules_per_second' => round($efficiency, 2),
|
||||
'error_rate' => $this->evaluatedRules > 0
|
||||
? round(($this->errorCount / $this->evaluatedRules) * 100, 2)
|
||||
: 0.0,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to array for serialization
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'timestamp' => $this->timestamp->toIsoString(),
|
||||
'evaluation_time_ms' => $this->evaluationTime->toMilliseconds(),
|
||||
'evaluated_rules' => $this->evaluatedRules,
|
||||
'skipped_rules' => $this->skippedRules,
|
||||
'error_count' => $this->errorCount,
|
||||
'match_count' => $this->getMatchCount(),
|
||||
'has_matches' => $this->hasMatches(),
|
||||
'should_block' => $this->shouldBlock(),
|
||||
'should_alert' => $this->shouldAlert(),
|
||||
'highest_severity' => $this->getHighestSeverity(),
|
||||
'threat_score' => $this->getThreatScore()->getValue(),
|
||||
'overall_confidence' => $this->getOverallConfidence()->getValue(),
|
||||
'matched_categories' => array_map(fn ($cat) => $cat->value, $this->getMatchedCategories()),
|
||||
'matched_rule_ids' => $this->getMatchedRuleIds(),
|
||||
'owasp_top10_matches' => count($this->getOwaspTop10Matches()),
|
||||
'critical_matches' => count($this->getCriticalMatches()),
|
||||
'high_severity_matches' => count($this->getHighSeverityMatches()),
|
||||
'blocking_matches' => count($this->getBlockingMatches()),
|
||||
'alerting_matches' => count($this->getAlertingMatches()),
|
||||
'performance_summary' => $this->getPerformanceSummary(),
|
||||
'matches' => array_map(fn (RuleMatch $match) => $match->toArray(), $this->matches),
|
||||
'errors' => $this->errors,
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user