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,339 @@
<?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\CookieParser;
use App\Framework\Http\Parser\Exception\ParserSecurityException;
use App\Framework\Http\Parser\ParserCache;
use App\Framework\Http\Parser\ParserConfig;
use PHPUnit\Framework\TestCase;
final class CookieParserTest extends TestCase
{
private CookieParser $parser;
protected function setUp(): void
{
$this->parser = $this->createCookieParser();
}
private function createCookieParser(?ParserConfig $config = null): CookieParser
{
// 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);
return new CookieParser($config ?? new ParserConfig(), $cache);
}
public function testParseEmptyCookieHeader(): void
{
$result = $this->parser->parseCookieHeader('');
$this->assertSame([], $result);
}
public function testParseSingleCookie(): void
{
$result = $this->parser->parseCookieHeader('sessionId=abc123');
$this->assertSame(['sessionId' => 'abc123'], $result);
}
public function testParseMultipleCookies(): void
{
$result = $this->parser->parseCookieHeader('sessionId=abc123; userId=456; theme=dark');
$this->assertSame([
'sessionId' => 'abc123',
'userId' => '456',
'theme' => 'dark',
], $result);
}
public function testParseUrlEncodedValues(): void
{
$result = $this->parser->parseCookieHeader('name=John%20Doe; email=test%40example.com');
$this->assertSame([
'name' => 'John Doe',
'email' => 'test@example.com',
], $result);
}
public function testParseEmptyValues(): void
{
$result = $this->parser->parseCookieHeader('empty=; valid=value');
$this->assertSame([
'empty' => '',
'valid' => 'value',
], $result);
}
public function testIgnoreInvalidPairs(): void
{
$result = $this->parser->parseCookieHeader('valid=value; invalid_no_equals; another=test');
$this->assertSame([
'valid' => 'value',
'another' => 'test',
], $result);
}
public function testHandleExtraSpaces(): void
{
$result = $this->parser->parseCookieHeader(' key1 = value1 ; key2 = value2 ');
$this->assertSame([
'key1' => 'value1',
'key2' => 'value2',
], $result);
}
public function testParseSetCookieHeader(): void
{
$setCookie = 'sessionId=abc123; Expires=Wed, 09 Jun 2021 10:18:14 GMT; Path=/; Domain=.example.com; Secure; HttpOnly; SameSite=Lax';
$result = $this->parser->parseSetCookieHeader($setCookie);
$this->assertSame('sessionId', $result['name']);
$this->assertSame('abc123', $result['value']);
$this->assertSame('Wed, 09 Jun 2021 10:18:14 GMT', $result['expires']);
$this->assertSame('/', $result['path']);
$this->assertSame('.example.com', $result['domain']);
$this->assertTrue($result['secure']);
$this->assertTrue($result['httponly']);
$this->assertSame('Lax', $result['samesite']);
}
public function testParseSetCookieWithMaxAge(): void
{
$setCookie = 'token=xyz789; Max-Age=3600; Path=/api';
$result = $this->parser->parseSetCookieHeader($setCookie);
$this->assertSame('token', $result['name']);
$this->assertSame('xyz789', $result['value']);
$this->assertSame(3600, $result['max-age']);
$this->assertSame('/api', $result['path']);
}
public function testParseSetCookieWithUrlEncodedValue(): void
{
$setCookie = 'data=hello%20world%21; Path=/';
$result = $this->parser->parseSetCookieHeader($setCookie);
$this->assertSame('data', $result['name']);
$this->assertSame('hello world!', $result['value']);
}
public function testParseInvalidSetCookieThrowsException(): void
{
$this->expectException(\InvalidArgumentException::class);
$this->parser->parseSetCookieHeader('invalid_cookie_format');
}
public function testParseToCookiesObject(): void
{
$cookies = $this->parser->parseToCookies('foo=bar; baz=qux');
$this->assertSame('bar', $cookies->get('foo')?->value);
$this->assertSame('qux', $cookies->get('baz')?->value);
$this->assertNull($cookies->get('nonexistent'));
}
// Security Tests
public function testCookieCountLimitExceeded(): void
{
$config = new ParserConfig(maxCookieCount: 2);
$parser = $this->createCookieParser($config);
$this->expectException(ParserSecurityException::class);
$this->expectExceptionMessage('Cookie count exceeded: 3 cookies > 2 maximum');
$parser->parseCookieHeader('cookie1=value1; cookie2=value2; cookie3=value3');
}
public function testCookieNameTooLong(): void
{
$config = new ParserConfig(maxCookieNameLength: 10);
$parser = $this->createCookieParser($config);
$this->expectException(ParserSecurityException::class);
$this->expectExceptionMessage('Cookie name too long');
$parser->parseCookieHeader('verylongcookiename=value');
}
public function testCookieValueTooLong(): void
{
$config = new ParserConfig(maxCookieValueLength: 10);
$parser = $this->createCookieParser($config);
$this->expectException(ParserSecurityException::class);
$this->expectExceptionMessage('Cookie value too long');
$parser->parseCookieHeader('cookie=verylongcookievaluethatexceedslimit');
}
public function testMaliciousScriptInjectionDetected(): void
{
$config = new ParserConfig(scanForMaliciousContent: true);
$parser = $this->createCookieParser($config);
$this->expectException(ParserSecurityException::class);
$this->expectExceptionMessage('Malicious content detected');
$parser->parseCookieHeader('evil=<script>alert("xss")</script>');
}
public function testMaliciousJavaScriptUrlDetected(): void
{
$config = new ParserConfig(scanForMaliciousContent: true);
$parser = $this->createCookieParser($config);
$this->expectException(ParserSecurityException::class);
$this->expectExceptionMessage('Malicious content detected');
$parser->parseCookieHeader('redirect=javascript:alert("xss")');
}
public function testMaliciousEventHandlerDetected(): void
{
$config = new ParserConfig(scanForMaliciousContent: true);
$parser = $this->createCookieParser($config);
$this->expectException(ParserSecurityException::class);
$this->expectExceptionMessage('Malicious content detected');
$parser->parseCookieHeader('data=onclick=alert("xss")');
}
public function testExcessiveUrlEncodingDetected(): void
{
$config = new ParserConfig(scanForMaliciousContent: true);
$parser = $this->createCookieParser($config);
$this->expectException(ParserSecurityException::class);
$this->expectExceptionMessage('Excessive URL encoding detected');
$excessive = str_repeat('%20', 15); // More than 10 % characters
$parser->parseCookieHeader("data={$excessive}");
}
public function testControlCharactersDetected(): void
{
$config = new ParserConfig(scanForMaliciousContent: true);
$parser = $this->createCookieParser($config);
$this->expectException(ParserSecurityException::class);
$this->expectExceptionMessage('Control characters detected');
$parser->parseCookieHeader("data=value\x00nullbyte");
}
public function testCrlfInjectionDetected(): void
{
$config = new ParserConfig(scanForMaliciousContent: true);
$parser = $this->createCookieParser($config);
$this->expectException(ParserSecurityException::class);
$this->expectExceptionMessage('Malicious content detected');
$parser->parseCookieHeader("data=value\r\nSet-Cookie: evil=injected");
}
// Set-Cookie Security Tests
public function testSetCookieNameTooLong(): void
{
$config = new ParserConfig(maxCookieNameLength: 5);
$parser = $this->createCookieParser($config);
$this->expectException(ParserSecurityException::class);
$this->expectExceptionMessage('Cookie name too long');
$parser->parseSetCookieHeader('verylongname=value; Path=/');
}
public function testSetCookieValueTooLong(): void
{
$config = new ParserConfig(maxCookieValueLength: 5);
$parser = $this->createCookieParser($config);
$this->expectException(ParserSecurityException::class);
$this->expectExceptionMessage('Cookie value too long');
$parser->parseSetCookieHeader('name=verylongvalue; Path=/');
}
public function testSetCookieMaliciousContent(): void
{
$config = new ParserConfig(scanForMaliciousContent: true);
$parser = $this->createCookieParser($config);
$this->expectException(ParserSecurityException::class);
$this->expectExceptionMessage('Malicious content detected');
$parser->parseSetCookieHeader('evil=<script>alert("xss")</script>; Path=/');
}
public function testMultipleSetCookieCountLimit(): void
{
$config = new ParserConfig(maxCookieCount: 2);
$parser = $this->createCookieParser($config);
$this->expectException(ParserSecurityException::class);
$this->expectExceptionMessage('Cookie count exceeded');
$parser->parseSetCookieHeaders([
'cookie1=value1; Path=/',
'cookie2=value2; Path=/',
'cookie3=value3; Path=/',
]);
}
// Security Configuration Tests
public function testSecurityDisabled(): void
{
$config = new ParserConfig(
scanForMaliciousContent: false,
maxCookieCount: 1000,
maxCookieNameLength: 1000,
maxCookieValueLength: 1000
);
$parser = $this->createCookieParser($config);
// Should not throw exception when security is disabled
$result = $parser->parseCookieHeader('evil=<script>alert("xss")</script>');
$this->assertSame(['evil' => '<script>alert("xss")</script>'], $result);
}
public function testWithinSecurityLimits(): void
{
$config = new ParserConfig(
maxCookieCount: 5,
maxCookieNameLength: 20,
maxCookieValueLength: 50,
scanForMaliciousContent: true
);
$parser = $this->createCookieParser($config);
// Should work fine within limits
$result = $parser->parseCookieHeader('session=abc123; theme=dark; lang=en');
$this->assertSame([
'session' => 'abc123',
'theme' => 'dark',
'lang' => 'en',
], $result);
}
}