repository->getFeedbackStats(); // Get recent feedback $recentFeedback = $this->repository->getRecentFeedback(10); // Get time period from query parameters (default to last 30 days) $period = $request->queryParams['period'] ?? '30d'; $since = $this->getPeriodTimestamp($period); // Get feedback by type for the selected period $falsePositives = $this->repository->getFeedbackByFeedbackType( FeedbackType::FALSE_POSITIVE, $since ); $falseNegatives = $this->repository->getFeedbackByFeedbackType( FeedbackType::FALSE_NEGATIVE, $since ); $correctDetections = $this->repository->getFeedbackByFeedbackType( FeedbackType::CORRECT_DETECTION, $since ); $severityAdjustments = $this->repository->getFeedbackByFeedbackType( FeedbackType::SEVERITY_ADJUSTMENT, $since ); // Calculate accuracy metrics $totalFeedback = count($falsePositives) + count($falseNegatives) + count($correctDetections); $accuracy = $totalFeedback > 0 ? (count($correctDetections) / $totalFeedback) * 100 : 0; $falsePositiveRate = $totalFeedback > 0 ? (count($falsePositives) / $totalFeedback) * 100 : 0; $falseNegativeRate = $totalFeedback > 0 ? (count($falseNegatives) / $totalFeedback) * 100 : 0; // Get feedback by category $feedbackByCategory = $this->getFeedbackByCategory($since); // Get trend data $trendData = $stats['trend_data'] ?? []; return new ViewResult('admin/security/waf-feedback-dashboard', [ 'stats' => $stats, 'recent_feedback' => $recentFeedback, 'period' => $period, 'accuracy' => round($accuracy, 1), 'false_positive_rate' => round($falsePositiveRate, 1), 'false_negative_rate' => round($falseNegativeRate, 1), 'feedback_by_category' => $feedbackByCategory, 'trend_data' => $trendData, 'false_positives_count' => count($falsePositives), 'false_negatives_count' => count($falseNegatives), 'correct_detections_count' => count($correctDetections), 'severity_adjustments_count' => count($severityAdjustments), ]); } /** * Show detailed feedback for a specific category */ #[Route(path: '/admin/security/waf/feedback/category/{category}', method: Method::GET)] public function showCategoryFeedback(HttpRequest $request, string $category): ViewResult { try { $detectionCategory = DetectionCategory::from($category); } catch (\ValueError $e) { // If category is invalid, redirect to dashboard return new ViewResult('admin/security/waf-feedback-dashboard', [ 'error' => 'Invalid category: ' . $category, ]); } // Get time period from query parameters (default to last 30 days) $period = $request->queryParams['period'] ?? '30d'; $since = $this->getPeriodTimestamp($period); // Get feedback for this category $feedback = $this->repository->getFeedbackByCategory($detectionCategory, $since); // Group feedback by type $feedbackByType = []; foreach ($feedback as $item) { $type = $item->feedbackType->value; if (! isset($feedbackByType[$type])) { $feedbackByType[$type] = []; } $feedbackByType[$type][] = $item; } // Calculate accuracy metrics for this category $falsePositives = $feedbackByType[FeedbackType::FALSE_POSITIVE->value] ?? []; $falseNegatives = $feedbackByType[FeedbackType::FALSE_NEGATIVE->value] ?? []; $correctDetections = $feedbackByType[FeedbackType::CORRECT_DETECTION->value] ?? []; $totalFeedback = count($falsePositives) + count($falseNegatives) + count($correctDetections); $accuracy = $totalFeedback > 0 ? (count($correctDetections) / $totalFeedback) * 100 : 0; $falsePositiveRate = $totalFeedback > 0 ? (count($falsePositives) / $totalFeedback) * 100 : 0; $falseNegativeRate = $totalFeedback > 0 ? (count($falseNegatives) / $totalFeedback) * 100 : 0; return new ViewResult('admin/security/waf-feedback-category', [ 'category' => $detectionCategory, 'feedback' => $feedback, 'feedback_by_type' => $feedbackByType, 'period' => $period, 'accuracy' => round($accuracy, 1), 'false_positive_rate' => round($falsePositiveRate, 1), 'false_negative_rate' => round($falseNegativeRate, 1), 'total_feedback' => $totalFeedback, ]); } /** * Show feedback learning history */ #[Route(path: '/admin/security/waf/feedback/learning', method: Method::GET)] public function showLearningHistory(HttpRequest $request): ViewResult { // In a real implementation, this would retrieve learning history from a database // For now, we'll return a placeholder view return new ViewResult('admin/security/waf-feedback-learning', [ 'learning_history' => [], 'message' => 'Learning history not yet implemented', ]); } /** * Get a timestamp for the specified period * * @param string $period Period string (e.g., '7d', '30d', '90d', 'all') * @return Timestamp|null Timestamp for the start of the period, or null for 'all' */ private function getPeriodTimestamp(string $period): ?Timestamp { return match($period) { '7d' => Timestamp::fromString('-7 days'), '30d' => Timestamp::fromString('-30 days'), '90d' => Timestamp::fromString('-90 days'), 'all' => null, default => Timestamp::fromString('-30 days') }; } /** * Get feedback grouped by category * * @param Timestamp|null $since Optional timestamp to filter feedback after a certain date * @return array> Feedback data grouped by category */ private function getFeedbackByCategory(?Timestamp $since): array { $result = []; // Get all categories $categories = DetectionCategory::cases(); foreach ($categories as $category) { // Get feedback for this category $feedback = $this->repository->getFeedbackByCategory($category, $since); if (empty($feedback)) { continue; } // Group feedback by type $feedbackByType = []; foreach ($feedback as $item) { $type = $item->feedbackType->value; if (! isset($feedbackByType[$type])) { $feedbackByType[$type] = []; } $feedbackByType[$type][] = $item; } // Calculate metrics $falsePositives = $feedbackByType[FeedbackType::FALSE_POSITIVE->value] ?? []; $falseNegatives = $feedbackByType[FeedbackType::FALSE_NEGATIVE->value] ?? []; $correctDetections = $feedbackByType[FeedbackType::CORRECT_DETECTION->value] ?? []; $totalFeedback = count($falsePositives) + count($falseNegatives) + count($correctDetections); if ($totalFeedback === 0) { continue; } $accuracy = (count($correctDetections) / $totalFeedback) * 100; $result[$category->value] = [ 'category' => $category, 'total_feedback' => $totalFeedback, 'false_positives' => count($falsePositives), 'false_negatives' => count($falseNegatives), 'correct_detections' => count($correctDetections), 'accuracy' => round($accuracy, 1), ]; } // Sort by total feedback count (descending) uasort($result, fn ($a, $b) => $b['total_feedback'] <=> $a['total_feedback']); return $result; } }