Files
michaelschiemer/tests/debug/test-csrf-multipart.php
Michael Schiemer 55a330b223 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
2025-08-11 20:13:26 +02:00

160 lines
5.1 KiB
PHP

<?php
declare(strict_types=1);
/**
* Test script to verify CSRF token handling in multipart/form-data requests
* This simulates what happens when a form is submitted via JavaScript with FormData
*/
require_once __DIR__ . '/../../vendor/autoload.php';
use App\Framework\Cache\Driver\InMemoryCache;
use App\Framework\Cache\SmartCache;
use App\Framework\Http\Parser\HttpRequestParser;
use App\Framework\Http\Parser\ParserCache;
// Create a mock multipart/form-data request similar to what JavaScript FormData sends
function createMultipartRequest(): array
{
$boundary = '----WebKitFormBoundary7MA4YWxkTrZu0gW';
$contentType = "multipart/form-data; boundary=$boundary";
// Simulate multipart body with CSRF tokens
$body = "--$boundary\r\n";
$body .= "Content-Disposition: form-data; name=\"_form_id\"\r\n\r\n";
$body .= "contact_form\r\n";
$body .= "--$boundary\r\n";
$body .= "Content-Disposition: form-data; name=\"_token\"\r\n\r\n";
$body .= "abc123token456\r\n";
$body .= "--$boundary\r\n";
$body .= "Content-Disposition: form-data; name=\"name\"\r\n\r\n";
$body .= "John Doe\r\n";
$body .= "--$boundary\r\n";
$body .= "Content-Disposition: form-data; name=\"email\"\r\n\r\n";
$body .= "john@example.com\r\n";
$body .= "--$boundary--\r\n";
return [
'server' => [
'REQUEST_METHOD' => 'POST',
'REQUEST_URI' => '/contact',
'CONTENT_TYPE' => $contentType,
'HTTP_HOST' => 'localhost',
],
'body' => $body,
'expected_post' => [
'_form_id' => 'contact_form',
'_token' => 'abc123token456',
'name' => 'John Doe',
'email' => 'john@example.com',
],
];
}
// Test 1: Normal multipart parsing (should work with custom parsers)
function testMultipartParsing()
{
echo "=== Test 1: Normal Multipart Parsing ===\n";
$data = createMultipartRequest();
$cache = new ParserCache(new SmartCache(new InMemoryCache()));
$parser = new HttpRequestParser($cache);
try {
$request = $parser->parseRequest(
method: $data['server']['REQUEST_METHOD'],
uri: $data['server']['REQUEST_URI'],
server: $data['server'],
rawBody: $data['body']
);
echo "✓ Request parsed successfully\n";
echo "Method: " . $request->method->value . "\n";
echo "Parsed body data: " . json_encode($request->parsedBody->data) . "\n";
// Check if CSRF tokens are present
$formId = $request->parsedBody->get('_form_id');
$token = $request->parsedBody->get('_token');
if ($formId && $token) {
echo "✓ CSRF tokens found: form_id='$formId', token='$token'\n";
} else {
echo "✗ CSRF tokens missing!\n";
}
} catch (Exception $e) {
echo "✗ Error: " . $e->getMessage() . "\n";
}
echo "\n";
}
// Test 2: Simulate empty php://input scenario (this tests our fallback)
function testEmptyInputFallback()
{
echo "=== Test 2: Empty php://input Fallback ===\n";
// Simulate the scenario where php://input is empty but $_POST has data
// This is what actually happens with multipart/form-data
global $_POST;
$originalPost = $_POST;
$_POST = [
'_form_id' => 'contact_form',
'_token' => 'fallback_token_123',
'name' => 'Jane Doe',
'email' => 'jane@example.com',
];
$cache = new ParserCache(new SmartCache(new InMemoryCache()));
$parser = new HttpRequestParser($cache);
try {
$request = $parser->parseRequest(
method: 'POST',
uri: '/contact',
server: [
'REQUEST_METHOD' => 'POST',
'REQUEST_URI' => '/contact',
'CONTENT_TYPE' => 'multipart/form-data; boundary=test',
'HTTP_HOST' => 'localhost',
],
rawBody: '' // Empty body simulates php://input being empty
);
echo "✓ Request parsed with empty body\n";
echo "Parsed body data: " . json_encode($request->parsedBody->data) . "\n";
// Check if fallback worked
$formId = $request->parsedBody->get('_form_id');
$token = $request->parsedBody->get('_token');
if ($formId === 'contact_form' && $token === 'fallback_token_123') {
echo "✓ Fallback to \$_POST worked correctly!\n";
} else {
echo "✗ Fallback failed: form_id='$formId', token='$token'\n";
}
} catch (Exception $e) {
echo "✗ Error: " . $e->getMessage() . "\n";
} finally {
$_POST = $originalPost; // Restore original $_POST
}
echo "\n";
}
// Run tests
echo "Testing CSRF Token Handling in Multipart Requests\n";
echo "================================================\n\n";
testMultipartParsing();
testEmptyInputFallback();
echo "Test completed!\n";
echo "\n";
echo "If both tests pass, the CSRF token issue should be resolved.\n";
echo "The key improvement is that HttpRequestParser now uses \$_POST as fallback\n";
echo "when php://input is empty for multipart/form-data requests.\n";