diff --git a/packages/playground/php-cors-proxy/cors-proxy.php b/packages/playground/php-cors-proxy/cors-proxy.php index e7c519088c..3f1a5c8e47 100644 --- a/packages/playground/php-cors-proxy/cors-proxy.php +++ b/packages/playground/php-cors-proxy/cors-proxy.php @@ -15,6 +15,18 @@ $server_host = $_SERVER['HTTP_HOST'] ?? ''; $origin = $_SERVER['HTTP_ORIGIN'] ?? ''; +// Define allowlist of Content-Types +$allowed_content_types = [ + 'application/json', + 'application/xml', + 'application/rss+xml', + 'application/atom+xml', + 'text/html', + 'text/plain', + 'text/xml', + // Add more as needed +]; + if (should_respond_with_cors_headers($server_host, $origin)) { header('Access-Control-Allow-Origin: ' . $origin); header('Access-Control-Allow-Credentials: true'); @@ -149,7 +161,7 @@ function should_send_as_chunked_response() { filter_headers_by_name( getallheaders(), $strictly_disallowed_headers, - $headers_requiring_opt_in, + $headers_requiring_opt_in ) ); curl_setopt( @@ -181,7 +193,7 @@ function( ) use ( $targetUrl, $relay_http_code_and_initial_headers_if_not_already_sent, - &$is_chunked_response + &$is_chunked_response, $allowed_content_types ) { @$relay_http_code_and_initial_headers_if_not_already_sent(); @@ -205,6 +217,24 @@ function( return $len; } + // Check if Content-Type is allowed + if ($name === 'content-type') { + $mimeType = strtolower(trim(explode(';', $value)[0])); // Normalize and strip charset + + $isGitContent = str_starts_with($mimeType, 'application/x-git-'); + + // If not in allowlist and not a special case, block it + if ( + !in_array($mimeType, $allowed_content_types, true) && + $mimeType !== 'application/octet-stream' && + !$isGitContent + ) { + http_response_code(415); // Unsupported Media Type + send_response_chunk("Unsupported Content-Type: $mimeType"); + exit; + } + } + if ($name === 'transfer-encoding' && stripos($value, 'chunked') !== false) { $is_chunked_response = true; header($header, false);