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,281 @@
<?php
declare(strict_types=1);
namespace Tests\Framework\Security;
use App\Framework\Http\HttpResponse;
use App\Framework\Http\MiddlewareContext;
use App\Framework\Http\Middlewares\SecurityHeaderConfig;
use App\Framework\Http\Middlewares\SecurityHeaderMiddleware;
use App\Framework\Http\Next;
use App\Framework\Http\Request;
use App\Framework\Http\RequestStateManager;
use App\Framework\Http\Status;
use PHPUnit\Framework\TestCase;
/**
* Critical Security Tests for HTTP Security Headers
* Tests gegen OWASP A05:2021 Security Misconfiguration
*/
final class SecurityHeadersTest extends TestCase
{
private SecurityHeaderConfig $config;
private SecurityHeaderMiddleware $middleware;
private RequestStateManager $stateManager;
protected function setUp(): void
{
$this->config = new SecurityHeaderConfig(
hsts: true,
hstsMaxAge: 31536000,
hstsIncludeSubdomains: true,
hstsPreload: true,
csp: "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'",
xFrameOptions: 'DENY',
xContentTypeOptions: true,
referrerPolicy: 'strict-origin-when-cross-origin',
permissionsPolicy: "camera=(), microphone=(), geolocation=()"
);
$this->middleware = new SecurityHeaderMiddleware($this->config);
$this->stateManager = $this->createMock(RequestStateManager::class);
}
/**
* Test: HSTS Header wird korrekt gesetzt
* Verhindert SSL-Stripping Attacks
*/
public function test_hsts_header_set_correctly(): void
{
// Arrange
$request = $this->createHttpsRequest();
$context = new MiddlewareContext($request);
$response = new HttpResponse(Status::OK, [], 'content');
$next = $this->createMock(Next::class);
$next->method('__invoke')->willReturn(new MiddlewareContext($request, $response));
// Act
$result = $this->middleware->__invoke($context, $next, $this->stateManager);
// Assert
$headers = $result->response->headers;
$this->assertEquals(
'max-age=31536000; includeSubDomains; preload',
$headers->getFirst('Strict-Transport-Security')
);
}
/**
* Test: CSP Header verhindert XSS
* OWASP A03:2021 Injection
*/
public function test_csp_header_prevents_xss(): void
{
// Arrange
$request = $this->createMock(Request::class);
$context = new MiddlewareContext($request);
$response = new HttpResponse(Status::OK, [], 'content');
$next = $this->createMock(Next::class);
$next->method('__invoke')->willReturn(new MiddlewareContext($request, $response));
// Act
$result = $this->middleware->__invoke($context, $next, $this->stateManager);
// Assert
$headers = $result->response->headers;
$cspHeader = $headers->getFirst('Content-Security-Policy');
$this->assertStringContains("default-src 'self'", $cspHeader);
$this->assertStringContains("script-src 'self'", $cspHeader);
$this->assertStringNotContains("'unsafe-eval'", $cspHeader);
}
/**
* Test: X-Frame-Options verhindert Clickjacking
* OWASP A05:2021 Security Misconfiguration
*/
public function test_x_frame_options_prevents_clickjacking(): void
{
// Arrange
$request = $this->createMock(Request::class);
$context = new MiddlewareContext($request);
$response = new HttpResponse(Status::OK, [], 'content');
$next = $this->createMock(Next::class);
$next->method('__invoke')->willReturn(new MiddlewareContext($request, $response));
// Act
$result = $this->middleware->__invoke($context, $next, $this->stateManager);
// Assert
$headers = $result->response->headers;
$this->assertEquals('DENY', $headers->getFirst('X-Frame-Options'));
}
/**
* Test: X-Content-Type-Options verhindert MIME-Type Confusion
*/
public function test_x_content_type_options_prevents_mime_sniffing(): void
{
// Arrange
$request = $this->createMock(Request::class);
$context = new MiddlewareContext($request);
$response = new HttpResponse(Status::OK, [], 'content');
$next = $this->createMock(Next::class);
$next->method('__invoke')->willReturn(new MiddlewareContext($request, $response));
// Act
$result = $this->middleware->__invoke($context, $next, $this->stateManager);
// Assert
$headers = $result->response->headers;
$this->assertEquals('nosniff', $headers->getFirst('X-Content-Type-Options'));
}
/**
* Test: Referrer Policy schützt vor Information Leakage
*/
public function test_referrer_policy_prevents_info_leakage(): void
{
// Arrange
$request = $this->createMock(Request::class);
$context = new MiddlewareContext($request);
$response = new HttpResponse(Status::OK, [], 'content');
$next = $this->createMock(Next::class);
$next->method('__invoke')->willReturn(new MiddlewareContext($request, $response));
// Act
$result = $this->middleware->__invoke($context, $next, $this->stateManager);
// Assert
$headers = $result->response->headers;
$this->assertEquals(
'strict-origin-when-cross-origin',
$headers->getFirst('Referrer-Policy')
);
}
/**
* Test: Permissions Policy beschränkt Browser-Features
*/
public function test_permissions_policy_restricts_features(): void
{
// Arrange
$request = $this->createMock(Request::class);
$context = new MiddlewareContext($request);
$response = new HttpResponse(Status::OK, [], 'content');
$next = $this->createMock(Next::class);
$next->method('__invoke')->willReturn(new MiddlewareContext($request, $response));
// Act
$result = $this->middleware->__invoke($context, $next, $this->stateManager);
// Assert
$headers = $result->response->headers;
$permissionsPolicy = $headers->getFirst('Permissions-Policy');
$this->assertStringContains('camera=()', $permissionsPolicy);
$this->assertStringContains('microphone=()', $permissionsPolicy);
$this->assertStringContains('geolocation=()', $permissionsPolicy);
}
/**
* Test: HSTS wird nur über HTTPS gesetzt
* Verhindert Mixed Content Probleme
*/
public function test_hsts_only_over_https(): void
{
// Arrange: HTTP Request
$request = $this->createHttpRequest();
$context = new MiddlewareContext($request);
$response = new HttpResponse(Status::OK, [], 'content');
$next = $this->createMock(Next::class);
$next->method('__invoke')->willReturn(new MiddlewareContext($request, $response));
// Act
$result = $this->middleware->__invoke($context, $next, $this->stateManager);
// Assert: Kein HSTS Header über HTTP
$headers = $result->response->headers;
$this->assertNull($headers->getFirst('Strict-Transport-Security'));
}
/**
* Test: Security Headers werden nicht überschrieben
* Respektiert explizit gesetzte Headers
*/
public function test_existing_security_headers_not_overridden(): void
{
// Arrange: Response mit bereits gesetztem CSP Header
$request = $this->createMock(Request::class);
$context = new MiddlewareContext($request);
$existingHeaders = ['Content-Security-Policy' => ["default-src 'none'"]];
$response = new HttpResponse(Status::OK, $existingHeaders, 'content');
$next = $this->createMock(Next::class);
$next->method('__invoke')->willReturn(new MiddlewareContext($request, $response));
// Act
$result = $this->middleware->__invoke($context, $next, $this->stateManager);
// Assert: Ursprünglicher CSP Header bleibt erhalten
$headers = $result->response->headers;
$this->assertEquals("default-src 'none'", $headers->getFirst('Content-Security-Policy'));
}
/**
* Test: Development-spezifische CSP Policy
* Erlaubt eval() und inline styles in Development
*/
public function test_development_csp_policy(): void
{
// Arrange: Development Config
$devConfig = new SecurityHeaderConfig(
csp: "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'"
);
$middleware = new SecurityHeaderMiddleware($devConfig);
$request = $this->createMock(Request::class);
$context = new MiddlewareContext($request);
$response = new HttpResponse(Status::OK, [], 'content');
$next = $this->createMock(Next::class);
$next->method('__invoke')->willReturn(new MiddlewareContext($request, $response));
// Act
$result = $middleware->__invoke($context, $next, $this->stateManager);
// Assert: Development CSP erlaubt unsafe-eval
$headers = $result->response->headers;
$cspHeader = $headers->getFirst('Content-Security-Policy');
$this->assertStringContains("'unsafe-eval'", $cspHeader);
$this->assertStringContains("'unsafe-inline'", $cspHeader);
}
// Helper Methods
private function createHttpsRequest(): Request
{
$request = $this->createMock(Request::class);
$request->method('isSecure')->willReturn(true);
return $request;
}
private function createHttpRequest(): Request
{
$request = $this->createMock(Request::class);
$request->method('isSecure')->willReturn(false);
return $request;
}
}