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:
2025-08-11 20:13:26 +02:00
parent 59fd3dd3b1
commit 55a330b223
3683 changed files with 2956207 additions and 16948 deletions

View File

@@ -0,0 +1,230 @@
<?php
declare(strict_types=1);
namespace Tests\Framework\Security;
use App\Framework\Http\Method;
use App\Framework\Http\MiddlewareContext;
use App\Framework\Http\Middlewares\CsrfMiddleware;
use App\Framework\Http\Next;
use App\Framework\Http\Request;
use App\Framework\Http\RequestStateManager;
use App\Framework\Http\Session\Session;
use App\Framework\Http\Session\SessionInterface;
use App\Framework\Security\CsrfToken;
use PHPUnit\Framework\TestCase;
/**
* Critical Security Tests for CSRF Protection
* Tests gegen OWASP A01:2021 Broken Access Control
*/
final class CsrfSecurityTest extends TestCase
{
private SessionInterface $session;
private CsrfMiddleware $middleware;
private RequestStateManager $stateManager;
protected function setUp(): void
{
$this->session = $this->createMock(SessionInterface::class);
$this->middleware = new CsrfMiddleware($this->session);
$this->stateManager = $this->createMock(RequestStateManager::class);
}
/**
* Test: CSRF-Schutz für state-changing Operations
* OWASP A01:2021 Broken Access Control
*/
public function test_csrf_protection_blocks_post_without_token(): void
{
// Arrange: Session ist gestartet aber kein CSRF Token
$this->session->method('isStarted')->willReturn(true);
$request = $this->createPostRequest([], []);
$context = new MiddlewareContext($request);
$next = $this->createMock(Next::class);
// Act & Assert: Exception erwartet
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('CSRF protection requires both form ID and token');
$this->middleware->__invoke($context, $next, $this->stateManager);
}
/**
* Test: CSRF-Token Validation gegen Token-Forgery
*/
public function test_csrf_validation_rejects_invalid_token(): void
{
// Arrange: Ungültiges Token
$this->session->method('isStarted')->willReturn(true);
$csrf = $this->createMock(\App\Framework\Http\Session\CsrfProtection::class);
$csrf->method('validateToken')->willReturn(false);
$this->session->csrf = $csrf;
$request = $this->createPostRequest([
'_form_id' => 'login_form',
'_token' => 'invalid_token_value',
], []);
$context = new MiddlewareContext($request);
$next = $this->createMock(Next::class);
// Act & Assert
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('CSRF token validation failed');
$this->middleware->__invoke($context, $next, $this->stateManager);
}
/**
* Test: Valide CSRF-Token werden akzeptiert
*/
public function test_csrf_validation_accepts_valid_token(): void
{
// Arrange: Valides Token
$this->session->method('isStarted')->willReturn(true);
$csrf = $this->createMock(\App\Framework\Http\Session\CsrfProtection::class);
$csrf->method('validateToken')
->with('login_form', $this->isInstanceOf(CsrfToken::class))
->willReturn(true);
$this->session->csrf = $csrf;
$request = $this->createPostRequest([
'_form_id' => 'login_form',
'_token' => 'valid_token_abc123',
], []);
$context = new MiddlewareContext($request);
$next = $this->createMock(Next::class);
$next->expects($this->once())->method('__invoke')->willReturn($context);
// Act: Sollte ohne Exception durchlaufen
$result = $this->middleware->__invoke($context, $next, $this->stateManager);
// Assert
$this->assertSame($context, $result);
}
/**
* Test: GET-Requests benötigen keinen CSRF-Schutz
*/
public function test_get_requests_bypass_csrf_validation(): void
{
// Arrange: GET Request ohne Token
$this->session->method('isStarted')->willReturn(true);
$request = $this->createGetRequest();
$context = new MiddlewareContext($request);
$next = $this->createMock(Next::class);
$next->expects($this->once())->method('__invoke')->willReturn($context);
// Act: Sollte ohne Validation durchlaufen
$result = $this->middleware->__invoke($context, $next, $this->stateManager);
// Assert
$this->assertSame($context, $result);
}
/**
* Test: Session muss vor CSRF-Validation gestartet sein
* Verhindert Race Conditions
*/
public function test_requires_started_session(): void
{
// Arrange: Session nicht gestartet
$this->session->method('isStarted')->willReturn(false);
$request = $this->createPostRequest(['_token' => 'abc'], []);
$context = new MiddlewareContext($request);
$next = $this->createMock(Next::class);
// Act & Assert
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('Session must be started before CSRF validation');
$this->middleware->__invoke($context, $next, $this->stateManager);
}
/**
* Test: CSRF-Token über HTTP Headers (AJAX)
*/
public function test_csrf_token_via_headers(): void
{
// Arrange: Token über Headers statt Form Data
$this->session->method('isStarted')->willReturn(true);
$csrf = $this->createMock(\App\Framework\Http\Session\CsrfProtection::class);
$csrf->method('validateToken')->willReturn(true);
$this->session->csrf = $csrf;
$request = $this->createPostRequest([], [
'X-CSRF-Form-ID' => 'ajax_form',
'X-CSRF-Token' => 'header_token_xyz',
]);
$context = new MiddlewareContext($request);
$next = $this->createMock(Next::class);
$next->expects($this->once())->method('__invoke')->willReturn($context);
// Act
$result = $this->middleware->__invoke($context, $next, $this->stateManager);
// Assert
$this->assertSame($context, $result);
}
/**
* Test: Malformed CSRF Token wird abgelehnt
*/
public function test_malformed_csrf_token_rejected(): void
{
// Arrange: Malformed Token
$this->session->method('isStarted')->willReturn(true);
$request = $this->createPostRequest([
'_form_id' => 'form1',
'_token' => 'malformed token with spaces and @#$%',
], []);
$context = new MiddlewareContext($request);
$next = $this->createMock(Next::class);
// Act & Assert: Exception bei malformed Token
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Invalid CSRF token format');
$this->middleware->__invoke($context, $next, $this->stateManager);
}
// Helper Methods
private function createPostRequest(array $body, array $headers): Request
{
$request = $this->createMock(Request::class);
$request->method = Method::POST;
$parsedBody = $this->createMock(\App\Framework\Http\RequestBody::class);
$parsedBody->method('get')->willReturnCallback(fn ($key) => $body[$key] ?? null);
$request->parsedBody = $parsedBody;
$headersBag = $this->createMock(\App\Framework\Http\Headers::class);
$headersBag->method('getFirst')->willReturnCallback(fn ($key) => $headers[$key] ?? null);
$request->headers = $headersBag;
return $request;
}
private function createGetRequest(): Request
{
$request = $this->createMock(Request::class);
$request->method = Method::GET;
return $request;
}
}