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:
271
tests/Framework/Waf/Feedback/FeedbackIntegrationTest.php
Normal file
271
tests/Framework/Waf/Feedback/FeedbackIntegrationTest.php
Normal file
@@ -0,0 +1,271 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Framework\Waf\Feedback;
|
||||
|
||||
use App\Framework\Logging\Logger;
|
||||
use App\Framework\Waf\DetectionCategory;
|
||||
use App\Framework\Waf\DetectionSeverity;
|
||||
use App\Framework\Waf\Feedback\FeedbackLearningService;
|
||||
use App\Framework\Waf\Feedback\FeedbackService;
|
||||
use App\Framework\Waf\MachineLearning\ValueObjects\ModelAdjustment;
|
||||
use App\Framework\Waf\ValueObjects\Detection;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Integration tests for the WAF feedback system
|
||||
*/
|
||||
class FeedbackIntegrationTest extends TestCase
|
||||
{
|
||||
private TestClock $clock;
|
||||
|
||||
private TestMachineLearningEngine $mlEngine;
|
||||
|
||||
private InMemoryFeedbackRepository $repository;
|
||||
|
||||
private Logger $logger;
|
||||
|
||||
private FeedbackService $feedbackService;
|
||||
|
||||
private FeedbackLearningService $learningService;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->clock = new TestClock('2025-08-04 18:39:00');
|
||||
$this->mlEngine = new TestMachineLearningEngine();
|
||||
$this->repository = new InMemoryFeedbackRepository();
|
||||
$this->logger = $this->createMock(Logger::class);
|
||||
|
||||
$this->feedbackService = new FeedbackService(
|
||||
$this->repository,
|
||||
$this->clock,
|
||||
$this->logger
|
||||
);
|
||||
|
||||
$this->learningService = new FeedbackLearningService(
|
||||
$this->repository,
|
||||
$this->mlEngine,
|
||||
$this->clock,
|
||||
$this->logger,
|
||||
3, // Lower threshold for testing
|
||||
0.5 // Higher learning rate for testing
|
||||
);
|
||||
|
||||
// Set up ML engine to return success for adjustments
|
||||
$this->mlEngine->withApplyFeedbackAdjustmentsResult([
|
||||
'success' => true,
|
||||
'applied_count' => 0,
|
||||
'failed_count' => 0,
|
||||
'results' => [],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the complete feedback loop:
|
||||
* 1. Submit feedback
|
||||
* 2. Learn from feedback
|
||||
* 3. Apply model adjustments
|
||||
*/
|
||||
public function testCompleteFeedbackLoop(): void
|
||||
{
|
||||
// 1. Submit feedback
|
||||
$this->submitTestFeedback();
|
||||
|
||||
// Verify feedback was stored
|
||||
$this->assertCount(5, $this->repository->getAllFeedback());
|
||||
|
||||
// 2. Learn from feedback
|
||||
$learningResult = $this->learningService->learnFromFeedback();
|
||||
|
||||
// Verify learning result
|
||||
$this->assertTrue($learningResult['success']);
|
||||
$this->assertGreaterThan(0, $learningResult['total_adjustments_applied']);
|
||||
|
||||
// 3. Verify model adjustments were applied
|
||||
$receivedAdjustments = $this->mlEngine->getReceivedAdjustments();
|
||||
$this->assertNotEmpty($receivedAdjustments);
|
||||
|
||||
// Verify SQL_INJECTION adjustments
|
||||
$sqlInjectionAdjustment = $this->findAdjustmentForCategory($receivedAdjustments, DetectionCategory::SQL_INJECTION);
|
||||
$this->assertNotNull($sqlInjectionAdjustment);
|
||||
|
||||
// For false positives, threshold should be increased (positive adjustment)
|
||||
$this->assertGreaterThan(0, $sqlInjectionAdjustment->thresholdAdjustment->getValue());
|
||||
|
||||
// For false positives, confidence should be decreased (negative adjustment)
|
||||
$this->assertLessThan(0, $sqlInjectionAdjustment->confidenceAdjustment->getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that severity adjustments are properly processed
|
||||
*/
|
||||
public function testSeverityAdjustments(): void
|
||||
{
|
||||
// Submit severity adjustment feedback
|
||||
$this->feedbackService->submitSeverityAdjustment(
|
||||
'detection1',
|
||||
'test-user',
|
||||
'This should be higher severity',
|
||||
DetectionCategory::PATH_TRAVERSAL,
|
||||
DetectionSeverity::MEDIUM,
|
||||
DetectionSeverity::HIGH,
|
||||
['test' => true]
|
||||
);
|
||||
|
||||
$this->feedbackService->submitSeverityAdjustment(
|
||||
'detection2',
|
||||
'test-user',
|
||||
'This should be higher severity',
|
||||
DetectionCategory::PATH_TRAVERSAL,
|
||||
DetectionSeverity::MEDIUM,
|
||||
DetectionSeverity::HIGH,
|
||||
['test' => true]
|
||||
);
|
||||
|
||||
$this->feedbackService->submitSeverityAdjustment(
|
||||
'detection3',
|
||||
'test-user',
|
||||
'This should be higher severity',
|
||||
DetectionCategory::PATH_TRAVERSAL,
|
||||
DetectionSeverity::MEDIUM,
|
||||
DetectionSeverity::HIGH,
|
||||
['test' => true]
|
||||
);
|
||||
|
||||
// Learn from feedback
|
||||
$learningResult = $this->learningService->learnFromFeedback();
|
||||
|
||||
// Verify learning result
|
||||
$this->assertTrue($learningResult['success']);
|
||||
|
||||
// Verify model adjustments were applied
|
||||
$receivedAdjustments = $this->mlEngine->getReceivedAdjustments();
|
||||
|
||||
// Verify PATH_TRAVERSAL adjustments
|
||||
$pathTraversalAdjustment = $this->findAdjustmentForCategory($receivedAdjustments, DetectionCategory::PATH_TRAVERSAL);
|
||||
$this->assertNotNull($pathTraversalAdjustment);
|
||||
|
||||
// For severity increase, confidence should be increased (positive adjustment)
|
||||
$this->assertGreaterThan(0, $pathTraversalAdjustment->confidenceAdjustment->getValue());
|
||||
|
||||
// For severity adjustments, threshold should not be adjusted
|
||||
$this->assertEquals(0, $pathTraversalAdjustment->thresholdAdjustment->getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that false negative feedback is properly processed
|
||||
*/
|
||||
public function testFalseNegativeFeedback(): void
|
||||
{
|
||||
// Submit false negative feedback
|
||||
$this->feedbackService->submitFalseNegative(
|
||||
'detection1',
|
||||
'test-user',
|
||||
'This should have been detected',
|
||||
DetectionCategory::COMMAND_INJECTION,
|
||||
DetectionSeverity::HIGH,
|
||||
['test' => true]
|
||||
);
|
||||
|
||||
$this->feedbackService->submitFalseNegative(
|
||||
'detection2',
|
||||
'test-user',
|
||||
'This should have been detected',
|
||||
DetectionCategory::COMMAND_INJECTION,
|
||||
DetectionSeverity::HIGH,
|
||||
['test' => true]
|
||||
);
|
||||
|
||||
// Learn from feedback
|
||||
$learningResult = $this->learningService->learnFromFeedback();
|
||||
|
||||
// Verify learning result
|
||||
$this->assertTrue($learningResult['success']);
|
||||
|
||||
// Verify model adjustments were applied
|
||||
$receivedAdjustments = $this->mlEngine->getReceivedAdjustments();
|
||||
|
||||
// Verify COMMAND_INJECTION adjustments
|
||||
$commandInjectionAdjustment = $this->findAdjustmentForCategory($receivedAdjustments, DetectionCategory::COMMAND_INJECTION);
|
||||
$this->assertNotNull($commandInjectionAdjustment);
|
||||
|
||||
// For false negatives, threshold should be decreased (negative adjustment)
|
||||
$this->assertLessThan(0, $commandInjectionAdjustment->thresholdAdjustment->getValue());
|
||||
|
||||
// For false negatives, confidence should be increased (positive adjustment)
|
||||
$this->assertGreaterThan(0, $commandInjectionAdjustment->confidenceAdjustment->getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit test feedback for various scenarios
|
||||
*/
|
||||
private function submitTestFeedback(): void
|
||||
{
|
||||
// Submit false positive feedback for SQL_INJECTION
|
||||
$this->feedbackService->submitFalsePositive(
|
||||
'detection1',
|
||||
'test-user',
|
||||
'This is a legitimate query',
|
||||
DetectionCategory::SQL_INJECTION,
|
||||
DetectionSeverity::HIGH,
|
||||
['query' => 'SELECT * FROM users WHERE id = 1']
|
||||
);
|
||||
|
||||
$this->feedbackService->submitFalsePositive(
|
||||
'detection2',
|
||||
'test-user',
|
||||
'Another legitimate query',
|
||||
DetectionCategory::SQL_INJECTION,
|
||||
DetectionSeverity::HIGH,
|
||||
['query' => 'SELECT * FROM products WHERE category_id = 2']
|
||||
);
|
||||
|
||||
$this->feedbackService->submitFalsePositive(
|
||||
'detection3',
|
||||
'test-user',
|
||||
'Yet another legitimate query',
|
||||
DetectionCategory::SQL_INJECTION,
|
||||
DetectionSeverity::HIGH,
|
||||
['query' => 'SELECT * FROM orders WHERE customer_id = 3']
|
||||
);
|
||||
|
||||
// Submit correct detection feedback
|
||||
$this->feedbackService->submitCorrectDetection(
|
||||
'detection4',
|
||||
'test-user',
|
||||
'This is indeed an XSS attempt',
|
||||
DetectionCategory::XSS,
|
||||
DetectionSeverity::CRITICAL,
|
||||
['payload' => '<script>alert("XSS")</script>']
|
||||
);
|
||||
|
||||
// Submit false negative feedback
|
||||
$this->feedbackService->submitFalseNegative(
|
||||
'detection5',
|
||||
'test-user',
|
||||
'This should have been detected as XSS',
|
||||
DetectionCategory::XSS,
|
||||
DetectionSeverity::HIGH,
|
||||
['payload' => '<img src="x" onerror="alert(\'XSS\')">']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an adjustment for a specific category in an array of adjustments
|
||||
*
|
||||
* @param array<string, ModelAdjustment> $adjustments Array of adjustments
|
||||
* @param DetectionCategory $category Category to find
|
||||
* @return ModelAdjustment|null The found adjustment or null
|
||||
*/
|
||||
private function findAdjustmentForCategory(array $adjustments, DetectionCategory $category): ?ModelAdjustment
|
||||
{
|
||||
foreach ($adjustments as $adjustment) {
|
||||
if ($adjustment->category === $category) {
|
||||
return $adjustment;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user