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,251 @@
<?php
declare(strict_types=1);
namespace Tests\Framework\Http\Parser;
use App\Framework\Cache\Compression\NullCompression;
use App\Framework\Cache\CompressionCacheDecorator;
use App\Framework\Cache\Driver\InMemoryCache;
use App\Framework\Cache\GeneralCache;
use App\Framework\Cache\Serializer\PhpSerializer;
use App\Framework\Http\Parser\Exception\ParserSecurityException;
use App\Framework\Http\Parser\HttpRequestParser;
use App\Framework\Http\Parser\ParserCache;
use App\Framework\Http\Parser\ParserConfig;
use PHPUnit\Framework\TestCase;
/**
* Security tests for HttpRequestParser
* Tests body size limits, URI length limits, and integration security
*/
final class HttpRequestParserSecurityTest extends TestCase
{
private HttpRequestParser $parser;
private ParserConfig $strictConfig;
private ParserConfig $webConfig;
protected function setUp(): void
{
// Strict security config for testing
$this->strictConfig = new ParserConfig(
maxTotalUploadSize: \App\Framework\Core\ValueObjects\Byte::fromBytes(1024), // 1KB limit
maxFileSize: \App\Framework\Core\ValueObjects\Byte::fromBytes(500),
maxFormDataSize: \App\Framework\Core\ValueObjects\Byte::fromBytes(500),
maxQueryStringLength: 100,
validateFileExtensions: true,
scanForMaliciousContent: true,
throwOnLimitExceeded: true,
logSecurityViolations: false // Don't log during tests
);
// Web-friendly config
$this->webConfig = new ParserConfig(
maxTotalUploadSize: \App\Framework\Core\ValueObjects\Byte::fromMegabytes(10),
maxFileSize: \App\Framework\Core\ValueObjects\Byte::fromMegabytes(5),
maxFormDataSize: \App\Framework\Core\ValueObjects\Byte::fromMegabytes(5),
maxQueryStringLength: 8192,
validateFileExtensions: false,
scanForMaliciousContent: false,
throwOnLimitExceeded: true,
logSecurityViolations: false
);
// Create parser cache with proper serialization
$baseCache = new GeneralCache(new InMemoryCache(), new \App\Framework\Serializer\Php\PhpSerializer());
$compressionCache = new CompressionCacheDecorator(
$baseCache,
new NullCompression(),
new PhpSerializer()
);
$cache = new ParserCache($compressionCache);
$this->parser = new HttpRequestParser($cache, $this->strictConfig);
}
public function testRequestBodySizeExceeded(): void
{
// Create a body larger than the 1KB limit
$largeBody = str_repeat('a', 2048);
$this->expectException(ParserSecurityException::class);
$this->expectExceptionMessage('Request body size exceeded: 2048 bytes > 1024 bytes maximum');
$this->parser->parseRequest('POST', '/test', [], $largeBody);
}
public function testRequestBodySizeWithinLimits(): void
{
// Create a body smaller than the 1KB limit
$smallBody = str_repeat('a', 500);
$request = $this->parser->parseRequest('POST', '/test', ['HTTP_CONTENT_TYPE' => 'text/plain'], $smallBody);
$this->assertEquals('/test', $request->path);
$this->assertEquals($smallBody, $request->body);
}
public function testUriTooLong(): void
{
// Create a URI longer than 4KB limit
$longUri = '/test?' . str_repeat('param=value&', 500); // ~5KB
$this->expectException(ParserSecurityException::class);
$this->expectExceptionMessage('URI too long:');
$this->parser->parseRequest('GET', $longUri, [], '');
}
public function testUriWithinLimits(): void
{
// Normal length URI
$normalUri = '/test?param1=value1&param2=value2';
$request = $this->parser->parseRequest('GET', $normalUri, [], '');
$this->assertEquals('/test', $request->path);
$this->assertEquals(['param1' => 'value1', 'param2' => 'value2'], $request->queryParams);
}
public function testMultipartFormDataWithSizeLimit(): void
{
$boundary = 'test-boundary-123';
$contentType = "multipart/form-data; boundary={$boundary}";
// Create multipart data that exceeds 1KB limit
$multipartData = "--{$boundary}\r\n" .
"Content-Disposition: form-data; name=\"field1\"\r\n\r\n" .
str_repeat('large_value_', 200) . "\r\n" . // ~2.4KB of data
"--{$boundary}--\r\n";
$this->expectException(ParserSecurityException::class);
$this->expectExceptionMessage('Request body size exceeded');
$this->parser->parseRequest('POST', '/upload', [
'HTTP_CONTENT_TYPE' => $contentType,
], $multipartData);
}
public function testFormDataWithSizeLimit(): void
{
$contentType = 'application/x-www-form-urlencoded';
// Create form data that exceeds 1KB limit
$formData = 'field1=' . str_repeat('value_', 300); // ~1.8KB
$this->expectException(ParserSecurityException::class);
$this->expectExceptionMessage('Request body size exceeded');
$this->parser->parseRequest('POST', '/form', [
'HTTP_CONTENT_TYPE' => $contentType,
], $formData);
}
public function testWebConfigAllowsLargerRequests(): void
{
// Create parser cache for web test
$baseCache = new GeneralCache(new InMemoryCache(), new \App\Framework\Serializer\Php\PhpSerializer());
$compressionCache = new CompressionCacheDecorator(
$baseCache,
new NullCompression(),
new PhpSerializer()
);
$cache = new ParserCache($compressionCache);
$webParser = new HttpRequestParser($cache, $this->webConfig);
// Create a 2KB body (within web config's 10MB limit)
$body = str_repeat('a', 2048);
$request = $webParser->parseRequest('POST', '/test', [
'HTTP_CONTENT_TYPE' => 'text/plain',
], $body);
$this->assertEquals('/test', $request->path);
$this->assertEquals($body, $request->body);
}
public function testEmptyBodyIsAllowed(): void
{
$request = $this->parser->parseRequest('GET', '/test', [], '');
$this->assertEquals('/test', $request->path);
$this->assertEquals('', $request->body);
}
public function testSecurityIntegrationWithAllParsers(): void
{
// Test that security limits work across all sub-parsers
$boundary = 'security-test-boundary';
$contentType = "multipart/form-data; boundary={$boundary}";
// Create valid multipart data within limits
$validData = "--{$boundary}\r\n" .
"Content-Disposition: form-data; name=\"message\"\r\n\r\n" .
"Hello World\r\n" .
"--{$boundary}\r\n" .
"Content-Disposition: form-data; name=\"file\"; filename=\"test.txt\"\r\n" .
"Content-Type: text/plain\r\n\r\n" .
"File content\r\n" .
"--{$boundary}--\r\n";
$request = $this->parser->parseRequest('POST', '/upload?param=value', [
'HTTP_CONTENT_TYPE' => $contentType,
'HTTP_COOKIE' => 'session=abc123',
], $validData);
$this->assertEquals('/upload', $request->path);
$this->assertEquals(['param' => 'value'], $request->queryParams);
$this->assertCount(1, $request->files->all());
$this->assertEquals('abc123', $request->cookies->get('session')->value);
}
public function testParseFromGlobalsWithSecurityLimits(): void
{
// Test that parseFromGlobals also applies security limits
$largeBody = str_repeat('x', 2048);
$server = [
'REQUEST_METHOD' => 'POST',
'REQUEST_URI' => '/test',
'HTTP_CONTENT_TYPE' => 'text/plain',
];
$this->expectException(ParserSecurityException::class);
$this->expectExceptionMessage('Request body size exceeded');
$this->parser->parseFromGlobals($server, $largeBody);
}
public function testRawHttpRequestWithSecurityLimits(): void
{
// Test that parseRawHttpRequest also applies security limits
$largeBody = str_repeat('y', 2048);
$rawRequest = "POST /test HTTP/1.1\r\n" .
"Content-Type: text/plain\r\n" .
"Content-Length: " . strlen($largeBody) . "\r\n" .
"\r\n" .
$largeBody;
$this->expectException(ParserSecurityException::class);
$this->expectExceptionMessage('Request body size exceeded');
$this->parser->parseRawHttpRequest($rawRequest);
}
public function testMethodOverrideWithSecurityLimits(): void
{
// Test that method override doesn't bypass security
$largeData = '_method=PUT&data=' . str_repeat('value_', 300); // ~1.8KB
$this->expectException(ParserSecurityException::class);
$this->expectExceptionMessage('Request body size exceeded');
$this->parser->parseRequest('POST', '/test', [
'HTTP_CONTENT_TYPE' => 'application/x-www-form-urlencoded',
], $largeData);
}
}