mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
Merge branch 'PHP-8.2' into PHP-8.3
This commit is contained in:
commit
acf2f4988a
23 changed files with 1266 additions and 210 deletions
13
NEWS
13
NEWS
|
@ -50,6 +50,11 @@ PHP NEWS
|
|||
. Fixed bug GH-17704 (ldap_search fails when $attributes contains a
|
||||
non-packed array with numerical keys). (nielsdos, 7u83)
|
||||
|
||||
- LibXML:
|
||||
. Fixed GHSA-wg4p-4hqh-c3g9 (Reocurrence of #72714). (nielsdos)
|
||||
. Fixed GHSA-p3x9-6h7p-cgfc (libxml streams use wrong `content-type` header
|
||||
when requesting a redirected resource). (CVE-2025-1219) (timwolla)
|
||||
|
||||
- MBString:
|
||||
. Fixed bug GH-17503 (Undefined float conversion in mb_convert_variables).
|
||||
(cmb)
|
||||
|
@ -89,6 +94,14 @@ PHP NEWS
|
|||
- Streams:
|
||||
. Fixed bug GH-17650 (realloc with size 0 in user_filters.c). (nielsdos)
|
||||
. Fix memory leak on overflow in _php_stream_scandir(). (nielsdos)
|
||||
. Fixed GHSA-hgf54-96fm-v528 (Stream HTTP wrapper header check might omit
|
||||
basic auth header). (CVE-2025-1736) (Jakub Zelenka)
|
||||
. Fixed GHSA-52jp-hrpf-2jff (Stream HTTP wrapper truncate redirect location
|
||||
to 1024 bytes). (CVE-2025-1861) (Jakub Zelenka)
|
||||
. Fixed GHSA-pcmh-g36c-qc44 (Streams HTTP wrapper does not fail for headers
|
||||
without colon). (CVE-2025-1734) (Jakub Zelenka)
|
||||
. Fixed GHSA-v8xr-gpvj-cx9g (Header parser of `http` stream wrapper does not
|
||||
handle folded headers). (CVE-2025-1217) (Jakub Zelenka)
|
||||
|
||||
- Windows:
|
||||
. Fixed phpize for Windows 11 (24H2). (bwoebi)
|
||||
|
|
60
ext/dom/tests/ghsa-p3x9-6h7p-cgfc_001.phpt
Normal file
60
ext/dom/tests/ghsa-p3x9-6h7p-cgfc_001.phpt
Normal file
|
@ -0,0 +1,60 @@
|
|||
--TEST--
|
||||
GHSA-p3x9-6h7p-cgfc: libxml streams use wrong `content-type` header when requesting a redirected resource (Basic)
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--SKIPIF--
|
||||
<?php
|
||||
if (@!include "./ext/standard/tests/http/server.inc") die('skip server.inc not available');
|
||||
http_server_skipif();
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
require "./ext/standard/tests/http/server.inc";
|
||||
|
||||
function genResponses($server) {
|
||||
$uri = 'http://' . stream_socket_get_name($server, false);
|
||||
yield "data://text/plain,HTTP/1.1 302 Moved Temporarily\r\nLocation: $uri/document.xml\r\nContent-Type: text/html;charset=utf-16\r\n\r\n";
|
||||
$xml = <<<'EOT'
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>GHSA-p3x9-6h7p-cgfc</title>
|
||||
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>GHSA-p3x9-6h7p-cgfc</h1>
|
||||
</body>
|
||||
</html>
|
||||
EOT;
|
||||
// Intentionally using non-standard casing for content-type to verify it is matched not case sensitively.
|
||||
yield "data://text/plain,HTTP/1.1 200 OK\r\nconteNt-tyPe: text/html; charset=utf-8\r\n\r\n{$xml}";
|
||||
}
|
||||
|
||||
['pid' => $pid, 'uri' => $uri] = http_server('genResponses', $output);
|
||||
$document = new \DOMDocument();
|
||||
$document->loadHTMLFile($uri);
|
||||
|
||||
$h1 = $document->getElementsByTagName('h1');
|
||||
var_dump($h1->length);
|
||||
var_dump($document->saveHTML());
|
||||
http_server_kill($pid);
|
||||
?>
|
||||
--EXPECT--
|
||||
int(1)
|
||||
string(266) "<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>GHSA-p3x9-6h7p-cgfc</title>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>GHSA-p3x9-6h7p-cgfc</h1>
|
||||
</body>
|
||||
</html>
|
||||
"
|
60
ext/dom/tests/ghsa-p3x9-6h7p-cgfc_002.phpt
Normal file
60
ext/dom/tests/ghsa-p3x9-6h7p-cgfc_002.phpt
Normal file
|
@ -0,0 +1,60 @@
|
|||
--TEST--
|
||||
GHSA-p3x9-6h7p-cgfc: libxml streams use wrong `content-type` header when requesting a redirected resource (Missing content-type)
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--SKIPIF--
|
||||
<?php
|
||||
if (@!include "./ext/standard/tests/http/server.inc") die('skip server.inc not available');
|
||||
http_server_skipif();
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
require "./ext/standard/tests/http/server.inc";
|
||||
|
||||
function genResponses($server) {
|
||||
$uri = 'http://' . stream_socket_get_name($server, false);
|
||||
yield "data://text/plain,HTTP/1.1 302 Moved Temporarily\r\nLocation: $uri/document.xml\r\nContent-Type: text/html;charset=utf-16\r\n\r\n";
|
||||
$xml = <<<'EOT'
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>GHSA-p3x9-6h7p-cgfc</title>
|
||||
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>GHSA-p3x9-6h7p-cgfc</h1>
|
||||
</body>
|
||||
</html>
|
||||
EOT;
|
||||
// Missing content-type in actual response.
|
||||
yield "data://text/plain,HTTP/1.1 200 OK\r\n\r\n{$xml}";
|
||||
}
|
||||
|
||||
['pid' => $pid, 'uri' => $uri] = http_server('genResponses', $output);
|
||||
$document = new \DOMDocument();
|
||||
$document->loadHTMLFile($uri);
|
||||
|
||||
$h1 = $document->getElementsByTagName('h1');
|
||||
var_dump($h1->length);
|
||||
var_dump($document->saveHTML());
|
||||
http_server_kill($pid);
|
||||
?>
|
||||
--EXPECT--
|
||||
int(1)
|
||||
string(266) "<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>GHSA-p3x9-6h7p-cgfc</title>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>GHSA-p3x9-6h7p-cgfc</h1>
|
||||
</body>
|
||||
</html>
|
||||
"
|
60
ext/dom/tests/ghsa-p3x9-6h7p-cgfc_003.phpt
Normal file
60
ext/dom/tests/ghsa-p3x9-6h7p-cgfc_003.phpt
Normal file
|
@ -0,0 +1,60 @@
|
|||
--TEST--
|
||||
GHSA-p3x9-6h7p-cgfc: libxml streams use wrong `content-type` header when requesting a redirected resource (Reason with colon)
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--SKIPIF--
|
||||
<?php
|
||||
if (@!include "./ext/standard/tests/http/server.inc") die('skip server.inc not available');
|
||||
http_server_skipif();
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
require "./ext/standard/tests/http/server.inc";
|
||||
|
||||
function genResponses($server) {
|
||||
$uri = 'http://' . stream_socket_get_name($server, false);
|
||||
yield "data://text/plain,HTTP/1.1 302 Moved Temporarily\r\nLocation: $uri/document.xml\r\nContent-Type: text/html;charset=utf-16\r\n\r\n";
|
||||
$xml = <<<'EOT'
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>GHSA-p3x9-6h7p-cgfc</title>
|
||||
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>GHSA-p3x9-6h7p-cgfc</h1>
|
||||
</body>
|
||||
</html>
|
||||
EOT;
|
||||
// Missing content-type in actual response.
|
||||
yield "data://text/plain,HTTP/1.1 200 OK: This is fine\r\n\r\n{$xml}";
|
||||
}
|
||||
|
||||
['pid' => $pid, 'uri' => $uri] = http_server('genResponses', $output);
|
||||
$document = new \DOMDocument();
|
||||
$document->loadHTMLFile($uri);
|
||||
|
||||
$h1 = $document->getElementsByTagName('h1');
|
||||
var_dump($h1->length);
|
||||
var_dump($document->saveHTML());
|
||||
http_server_kill($pid);
|
||||
?>
|
||||
--EXPECT--
|
||||
int(1)
|
||||
string(266) "<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>GHSA-p3x9-6h7p-cgfc</title>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>GHSA-p3x9-6h7p-cgfc</h1>
|
||||
</body>
|
||||
</html>
|
||||
"
|
|
@ -525,41 +525,52 @@ php_libxml_input_buffer_create_filename(const char *URI, xmlCharEncoding enc)
|
|||
if (Z_TYPE(s->wrapperdata) == IS_ARRAY) {
|
||||
zval *header;
|
||||
|
||||
ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL(s->wrapperdata), header) {
|
||||
/* Scan backwards: The header array might contain the headers for multiple responses, if
|
||||
* a redirect was followed.
|
||||
*/
|
||||
ZEND_HASH_REVERSE_FOREACH_VAL_IND(Z_ARRVAL(s->wrapperdata), header) {
|
||||
const char buf[] = "Content-Type:";
|
||||
if (Z_TYPE_P(header) == IS_STRING &&
|
||||
!zend_binary_strncasecmp(Z_STRVAL_P(header), Z_STRLEN_P(header), buf, sizeof(buf)-1, sizeof(buf)-1)) {
|
||||
char needle[] = "charset=";
|
||||
char *haystack = estrndup(Z_STRVAL_P(header), Z_STRLEN_P(header));
|
||||
char *encoding = php_stristr(haystack, needle, Z_STRLEN_P(header), strlen(needle));
|
||||
|
||||
if (encoding) {
|
||||
char *end;
|
||||
|
||||
encoding += sizeof("charset=")-1;
|
||||
if (*encoding == '"') {
|
||||
encoding++;
|
||||
}
|
||||
end = strchr(encoding, ';');
|
||||
if (end == NULL) {
|
||||
end = encoding + strlen(encoding);
|
||||
}
|
||||
end--; /* end == encoding-1 isn't a buffer underrun */
|
||||
while (*end == ' ' || *end == '\t') {
|
||||
end--;
|
||||
}
|
||||
if (*end == '"') {
|
||||
end--;
|
||||
}
|
||||
if (encoding >= end) continue;
|
||||
*(end+1) = '\0';
|
||||
enc = xmlParseCharEncoding(encoding);
|
||||
if (enc <= XML_CHAR_ENCODING_NONE) {
|
||||
enc = XML_CHAR_ENCODING_NONE;
|
||||
}
|
||||
if (Z_TYPE_P(header) == IS_STRING) {
|
||||
/* If no colon is found in the header, we assume it's the HTTP status line and bail out. */
|
||||
char *colon = memchr(Z_STRVAL_P(header), ':', Z_STRLEN_P(header));
|
||||
char *space = memchr(Z_STRVAL_P(header), ' ', Z_STRLEN_P(header));
|
||||
if (colon == NULL || space < colon) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!zend_binary_strncasecmp(Z_STRVAL_P(header), Z_STRLEN_P(header), buf, sizeof(buf)-1, sizeof(buf)-1)) {
|
||||
char needle[] = "charset=";
|
||||
char *haystack = estrndup(Z_STRVAL_P(header), Z_STRLEN_P(header));
|
||||
char *encoding = php_stristr(haystack, needle, Z_STRLEN_P(header), sizeof("charset=")-1);
|
||||
|
||||
if (encoding) {
|
||||
char *end;
|
||||
|
||||
encoding += sizeof("charset=")-1;
|
||||
if (*encoding == '"') {
|
||||
encoding++;
|
||||
}
|
||||
end = strchr(encoding, ';');
|
||||
if (end == NULL) {
|
||||
end = encoding + strlen(encoding);
|
||||
}
|
||||
end--; /* end == encoding-1 isn't a buffer underrun */
|
||||
while (*end == ' ' || *end == '\t') {
|
||||
end--;
|
||||
}
|
||||
if (*end == '"') {
|
||||
end--;
|
||||
}
|
||||
if (encoding >= end) continue;
|
||||
*(end+1) = '\0';
|
||||
enc = xmlParseCharEncoding(encoding);
|
||||
if (enc <= XML_CHAR_ENCODING_NONE) {
|
||||
enc = XML_CHAR_ENCODING_NONE;
|
||||
}
|
||||
}
|
||||
efree(haystack);
|
||||
break; /* found content-type */
|
||||
}
|
||||
efree(haystack);
|
||||
break; /* found content-type */
|
||||
}
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
}
|
||||
|
|
|
@ -67,15 +67,16 @@
|
|||
|
||||
#include "php_fopen_wrappers.h"
|
||||
|
||||
#define HTTP_HEADER_BLOCK_SIZE 1024
|
||||
#define PHP_URL_REDIRECT_MAX 20
|
||||
#define HTTP_HEADER_USER_AGENT 1
|
||||
#define HTTP_HEADER_HOST 2
|
||||
#define HTTP_HEADER_AUTH 4
|
||||
#define HTTP_HEADER_FROM 8
|
||||
#define HTTP_HEADER_CONTENT_LENGTH 16
|
||||
#define HTTP_HEADER_TYPE 32
|
||||
#define HTTP_HEADER_CONNECTION 64
|
||||
#define HTTP_HEADER_BLOCK_SIZE 1024
|
||||
#define HTTP_HEADER_MAX_LOCATION_SIZE 8182 /* 8192 - 10 (size of "Location: ") */
|
||||
#define PHP_URL_REDIRECT_MAX 20
|
||||
#define HTTP_HEADER_USER_AGENT 1
|
||||
#define HTTP_HEADER_HOST 2
|
||||
#define HTTP_HEADER_AUTH 4
|
||||
#define HTTP_HEADER_FROM 8
|
||||
#define HTTP_HEADER_CONTENT_LENGTH 16
|
||||
#define HTTP_HEADER_TYPE 32
|
||||
#define HTTP_HEADER_CONNECTION 64
|
||||
|
||||
#define HTTP_WRAPPER_HEADER_INIT 1
|
||||
#define HTTP_WRAPPER_REDIRECTED 2
|
||||
|
@ -107,7 +108,7 @@ static inline void strip_header(char *header_bag, char *lc_header_bag,
|
|||
static bool check_has_header(const char *headers, const char *header) {
|
||||
const char *s = headers;
|
||||
while ((s = strstr(s, header))) {
|
||||
if (s == headers || *(s-1) == '\n') {
|
||||
if (s == headers || (*(s-1) == '\n' && *(s-2) == '\r')) {
|
||||
return 1;
|
||||
}
|
||||
s++;
|
||||
|
@ -115,6 +116,214 @@ static bool check_has_header(const char *headers, const char *header) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
typedef struct _php_stream_http_response_header_info {
|
||||
php_stream_filter *transfer_encoding;
|
||||
size_t file_size;
|
||||
bool error;
|
||||
bool follow_location;
|
||||
char *location;
|
||||
size_t location_len;
|
||||
} php_stream_http_response_header_info;
|
||||
|
||||
static void php_stream_http_response_header_info_init(
|
||||
php_stream_http_response_header_info *header_info)
|
||||
{
|
||||
memset(header_info, 0, sizeof(php_stream_http_response_header_info));
|
||||
header_info->follow_location = 1;
|
||||
}
|
||||
|
||||
/* Trim white spaces from response header line and update its length */
|
||||
static bool php_stream_http_response_header_trim(char *http_header_line,
|
||||
size_t *http_header_line_length)
|
||||
{
|
||||
char *http_header_line_end = http_header_line + *http_header_line_length - 1;
|
||||
while (http_header_line_end >= http_header_line &&
|
||||
(*http_header_line_end == '\n' || *http_header_line_end == '\r')) {
|
||||
http_header_line_end--;
|
||||
}
|
||||
|
||||
/* The primary definition of an HTTP header in RFC 7230 states:
|
||||
* > Each header field consists of a case-insensitive field name followed
|
||||
* > by a colon (":"), optional leading whitespace, the field value, and
|
||||
* > optional trailing whitespace. */
|
||||
|
||||
/* Strip trailing whitespace */
|
||||
bool space_trim = (*http_header_line_end == ' ' || *http_header_line_end == '\t');
|
||||
if (space_trim) {
|
||||
do {
|
||||
http_header_line_end--;
|
||||
} while (http_header_line_end >= http_header_line &&
|
||||
(*http_header_line_end == ' ' || *http_header_line_end == '\t'));
|
||||
}
|
||||
http_header_line_end++;
|
||||
*http_header_line_end = '\0';
|
||||
*http_header_line_length = http_header_line_end - http_header_line;
|
||||
|
||||
return space_trim;
|
||||
}
|
||||
|
||||
/* Process folding headers of the current line and if there are none, parse last full response
|
||||
* header line. It returns NULL if the last header is finished, otherwise it returns updated
|
||||
* last header line. */
|
||||
static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *wrapper,
|
||||
php_stream *stream, php_stream_context *context, int options,
|
||||
zend_string *last_header_line_str, char *header_line, size_t *header_line_length,
|
||||
int response_code, zval *response_header,
|
||||
php_stream_http_response_header_info *header_info)
|
||||
{
|
||||
char *last_header_line = ZSTR_VAL(last_header_line_str);
|
||||
size_t last_header_line_length = ZSTR_LEN(last_header_line_str);
|
||||
char *last_header_line_end = ZSTR_VAL(last_header_line_str) + ZSTR_LEN(last_header_line_str) - 1;
|
||||
|
||||
/* Process non empty header line. */
|
||||
if (header_line && (*header_line != '\n' && *header_line != '\r')) {
|
||||
/* Removing trailing white spaces. */
|
||||
if (php_stream_http_response_header_trim(header_line, header_line_length) &&
|
||||
*header_line_length == 0) {
|
||||
/* Only spaces so treat as an empty folding header. */
|
||||
return last_header_line_str;
|
||||
}
|
||||
|
||||
/* Process folding headers if starting with a space or a tab. */
|
||||
if (header_line && (*header_line == ' ' || *header_line == '\t')) {
|
||||
char *http_folded_header_line = header_line;
|
||||
size_t http_folded_header_line_length = *header_line_length;
|
||||
/* Remove the leading white spaces. */
|
||||
while (*http_folded_header_line == ' ' || *http_folded_header_line == '\t') {
|
||||
http_folded_header_line++;
|
||||
http_folded_header_line_length--;
|
||||
}
|
||||
/* It has to have some characters because it would get returned after the call
|
||||
* php_stream_http_response_header_trim above. */
|
||||
ZEND_ASSERT(http_folded_header_line_length > 0);
|
||||
/* Concatenate last header line, space and current header line. */
|
||||
zend_string *extended_header_str = zend_string_concat3(
|
||||
last_header_line, last_header_line_length,
|
||||
" ", 1,
|
||||
http_folded_header_line, http_folded_header_line_length);
|
||||
zend_string_efree(last_header_line_str);
|
||||
last_header_line_str = extended_header_str;
|
||||
/* Return new header line. */
|
||||
return last_header_line_str;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find header separator position. */
|
||||
char *last_header_value = memchr(last_header_line, ':', last_header_line_length);
|
||||
if (last_header_value) {
|
||||
/* Verify there is no space in header name */
|
||||
char *last_header_name = last_header_line + 1;
|
||||
while (last_header_name < last_header_value) {
|
||||
if (*last_header_name == ' ' || *last_header_name == '\t') {
|
||||
header_info->error = true;
|
||||
php_stream_wrapper_log_error(wrapper, options,
|
||||
"HTTP invalid response format (space in header name)!");
|
||||
zend_string_efree(last_header_line_str);
|
||||
return NULL;
|
||||
}
|
||||
++last_header_name;
|
||||
}
|
||||
|
||||
last_header_value++; /* Skip ':'. */
|
||||
|
||||
/* Strip leading whitespace. */
|
||||
while (last_header_value < last_header_line_end
|
||||
&& (*last_header_value == ' ' || *last_header_value == '\t')) {
|
||||
last_header_value++;
|
||||
}
|
||||
} else {
|
||||
/* There is no colon which means invalid response so error. */
|
||||
header_info->error = true;
|
||||
php_stream_wrapper_log_error(wrapper, options,
|
||||
"HTTP invalid response format (no colon in header line)!");
|
||||
zend_string_efree(last_header_line_str);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool store_header = true;
|
||||
zval *tmpzval = NULL;
|
||||
|
||||
if (!strncasecmp(last_header_line, "Location:", sizeof("Location:")-1)) {
|
||||
/* Check if the location should be followed. */
|
||||
if (context && (tmpzval = php_stream_context_get_option(context, "http", "follow_location")) != NULL) {
|
||||
header_info->follow_location = zval_is_true(tmpzval);
|
||||
} else if (!((response_code >= 300 && response_code < 304)
|
||||
|| 307 == response_code || 308 == response_code)) {
|
||||
/* The redirection should not be automatic if follow_location is not set and
|
||||
* response_code not in (300, 301, 302, 303 and 307)
|
||||
* see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.1
|
||||
* RFC 7238 defines 308: http://tools.ietf.org/html/rfc7238 */
|
||||
header_info->follow_location = 0;
|
||||
}
|
||||
size_t last_header_value_len = strlen(last_header_value);
|
||||
if (last_header_value_len > HTTP_HEADER_MAX_LOCATION_SIZE) {
|
||||
header_info->error = true;
|
||||
php_stream_wrapper_log_error(wrapper, options,
|
||||
"HTTP Location header size is over the limit of %d bytes",
|
||||
HTTP_HEADER_MAX_LOCATION_SIZE);
|
||||
zend_string_efree(last_header_line_str);
|
||||
return NULL;
|
||||
}
|
||||
if (header_info->location_len == 0) {
|
||||
header_info->location = emalloc(last_header_value_len + 1);
|
||||
} else if (header_info->location_len <= last_header_value_len) {
|
||||
header_info->location = erealloc(header_info->location, last_header_value_len + 1);
|
||||
}
|
||||
header_info->location_len = last_header_value_len;
|
||||
memcpy(header_info->location, last_header_value, last_header_value_len + 1);
|
||||
} else if (!strncasecmp(last_header_line, "Content-Type:", sizeof("Content-Type:")-1)) {
|
||||
php_stream_notify_info(context, PHP_STREAM_NOTIFY_MIME_TYPE_IS, last_header_value, 0);
|
||||
} else if (!strncasecmp(last_header_line, "Content-Length:", sizeof("Content-Length:")-1)) {
|
||||
/* https://www.rfc-editor.org/rfc/rfc9110.html#name-content-length */
|
||||
const char *ptr = last_header_value;
|
||||
/* must contain only digits, no + or - symbols */
|
||||
if (*ptr >= '0' && *ptr <= '9') {
|
||||
char *endptr = NULL;
|
||||
size_t parsed = ZEND_STRTOUL(ptr, &endptr, 10);
|
||||
/* check whether there was no garbage in the header value and the conversion was successful */
|
||||
if (endptr && !*endptr) {
|
||||
/* truncate for 32-bit such that no negative file sizes occur */
|
||||
header_info->file_size = MIN(parsed, ZEND_LONG_MAX);
|
||||
php_stream_notify_file_size(context, header_info->file_size, last_header_line, 0);
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
!strncasecmp(last_header_line, "Transfer-Encoding:", sizeof("Transfer-Encoding:")-1)
|
||||
&& !strncasecmp(last_header_value, "Chunked", sizeof("Chunked")-1)
|
||||
) {
|
||||
/* Create filter to decode response body. */
|
||||
if (!(options & STREAM_ONLY_GET_HEADERS)) {
|
||||
zend_long decode = 1;
|
||||
|
||||
if (context && (tmpzval = php_stream_context_get_option(context, "http", "auto_decode")) != NULL) {
|
||||
decode = zend_is_true(tmpzval);
|
||||
}
|
||||
if (decode) {
|
||||
if (header_info->transfer_encoding != NULL) {
|
||||
/* Prevent a memory leak in case there are more transfer-encoding headers. */
|
||||
php_stream_filter_free(header_info->transfer_encoding);
|
||||
}
|
||||
header_info->transfer_encoding = php_stream_filter_create(
|
||||
"dechunk", NULL, php_stream_is_persistent(stream));
|
||||
if (header_info->transfer_encoding != NULL) {
|
||||
/* Do not store transfer-encoding header. */
|
||||
store_header = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (store_header) {
|
||||
zval http_header;
|
||||
ZVAL_NEW_STR(&http_header, last_header_line_str);
|
||||
zend_hash_next_index_insert(Z_ARRVAL_P(response_header), &http_header);
|
||||
} else {
|
||||
zend_string_efree(last_header_line_str);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
|
||||
const char *path, const char *mode, int options, zend_string **opened_path,
|
||||
php_stream_context *context, int redirect_max, int flags,
|
||||
|
@ -127,11 +336,12 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
|
|||
zend_string *tmp = NULL;
|
||||
char *ua_str = NULL;
|
||||
zval *ua_zval = NULL, *tmpzval = NULL, ssl_proxy_peer_name;
|
||||
char location[HTTP_HEADER_BLOCK_SIZE];
|
||||
int reqok = 0;
|
||||
char *http_header_line = NULL;
|
||||
zend_string *last_header_line_str = NULL;
|
||||
php_stream_http_response_header_info header_info;
|
||||
char tmp_line[128];
|
||||
size_t chunk_size = 0, file_size = 0;
|
||||
size_t chunk_size = 0;
|
||||
int eol_detect = 0;
|
||||
zend_string *transport_string;
|
||||
zend_string *errstr = NULL;
|
||||
|
@ -142,8 +352,6 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
|
|||
int header_init = ((flags & HTTP_WRAPPER_HEADER_INIT) != 0);
|
||||
int redirected = ((flags & HTTP_WRAPPER_REDIRECTED) != 0);
|
||||
int redirect_keep_method = ((flags & HTTP_WRAPPER_KEEP_METHOD) != 0);
|
||||
bool follow_location = 1;
|
||||
php_stream_filter *transfer_encoding = NULL;
|
||||
int response_code;
|
||||
smart_str req_buf = {0};
|
||||
bool custom_request_method;
|
||||
|
@ -366,6 +574,8 @@ finish:
|
|||
}
|
||||
}
|
||||
|
||||
php_stream_http_response_header_info_init(&header_info);
|
||||
|
||||
if (stream == NULL)
|
||||
goto out;
|
||||
|
||||
|
@ -666,8 +876,6 @@ finish:
|
|||
/* send it */
|
||||
php_stream_write(stream, ZSTR_VAL(req_buf.s), ZSTR_LEN(req_buf.s));
|
||||
|
||||
location[0] = '\0';
|
||||
|
||||
if (Z_ISUNDEF_P(response_header)) {
|
||||
array_init(response_header);
|
||||
}
|
||||
|
@ -750,140 +958,103 @@ finish:
|
|||
}
|
||||
|
||||
/* read past HTTP headers */
|
||||
|
||||
while (!php_stream_eof(stream)) {
|
||||
size_t http_header_line_length;
|
||||
|
||||
if (http_header_line != NULL) {
|
||||
efree(http_header_line);
|
||||
}
|
||||
if ((http_header_line = php_stream_get_line(stream, NULL, 0, &http_header_line_length)) && *http_header_line != '\n' && *http_header_line != '\r') {
|
||||
char *e = http_header_line + http_header_line_length - 1;
|
||||
char *http_header_value;
|
||||
|
||||
while (e >= http_header_line && (*e == '\n' || *e == '\r')) {
|
||||
e--;
|
||||
}
|
||||
|
||||
/* The primary definition of an HTTP header in RFC 7230 states:
|
||||
* > Each header field consists of a case-insensitive field name followed
|
||||
* > by a colon (":"), optional leading whitespace, the field value, and
|
||||
* > optional trailing whitespace. */
|
||||
|
||||
/* Strip trailing whitespace */
|
||||
while (e >= http_header_line && (*e == ' ' || *e == '\t')) {
|
||||
e--;
|
||||
}
|
||||
|
||||
/* Terminate header line */
|
||||
e++;
|
||||
*e = '\0';
|
||||
http_header_line_length = e - http_header_line;
|
||||
|
||||
http_header_value = memchr(http_header_line, ':', http_header_line_length);
|
||||
if (http_header_value) {
|
||||
http_header_value++; /* Skip ':' */
|
||||
|
||||
/* Strip leading whitespace */
|
||||
while (http_header_value < e
|
||||
&& (*http_header_value == ' ' || *http_header_value == '\t')) {
|
||||
http_header_value++;
|
||||
if ((http_header_line = php_stream_get_line(stream, NULL, 0, &http_header_line_length))) {
|
||||
bool last_line;
|
||||
if (*http_header_line == '\r') {
|
||||
if (http_header_line[1] != '\n') {
|
||||
php_stream_close(stream);
|
||||
stream = NULL;
|
||||
php_stream_wrapper_log_error(wrapper, options,
|
||||
"HTTP invalid header name (cannot start with CR character)!");
|
||||
goto out;
|
||||
}
|
||||
last_line = true;
|
||||
} else if (*http_header_line == '\n') {
|
||||
last_line = true;
|
||||
} else {
|
||||
/* There is no colon. Set the value to the end of the header line, which is
|
||||
* effectively an empty string. */
|
||||
http_header_value = e;
|
||||
last_line = false;
|
||||
}
|
||||
|
||||
if (!strncasecmp(http_header_line, "Location:", sizeof("Location:")-1)) {
|
||||
if (context && (tmpzval = php_stream_context_get_option(context, "http", "follow_location")) != NULL) {
|
||||
follow_location = zval_is_true(tmpzval);
|
||||
} else if (!((response_code >= 300 && response_code < 304)
|
||||
|| 307 == response_code || 308 == response_code)) {
|
||||
/* we shouldn't redirect automatically
|
||||
if follow_location isn't set and response_code not in (300, 301, 302, 303 and 307)
|
||||
see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.1
|
||||
RFC 7238 defines 308: http://tools.ietf.org/html/rfc7238 */
|
||||
follow_location = 0;
|
||||
}
|
||||
strlcpy(location, http_header_value, sizeof(location));
|
||||
} else if (!strncasecmp(http_header_line, "Content-Type:", sizeof("Content-Type:")-1)) {
|
||||
php_stream_notify_info(context, PHP_STREAM_NOTIFY_MIME_TYPE_IS, http_header_value, 0);
|
||||
} else if (!strncasecmp(http_header_line, "Content-Length:", sizeof("Content-Length:")-1)) {
|
||||
/* https://www.rfc-editor.org/rfc/rfc9110.html#name-content-length */
|
||||
const char *ptr = http_header_value;
|
||||
/* must contain only digits, no + or - symbols */
|
||||
if (*ptr >= '0' && *ptr <= '9') {
|
||||
char *endptr = NULL;
|
||||
size_t parsed = ZEND_STRTOUL(ptr, &endptr, 10);
|
||||
/* check whether there was no garbage in the header value and the conversion was successful */
|
||||
if (endptr && !*endptr) {
|
||||
/* truncate for 32-bit such that no negative file sizes occur */
|
||||
file_size = MIN(parsed, ZEND_LONG_MAX);
|
||||
php_stream_notify_file_size(context, file_size, http_header_line, 0);
|
||||
|
||||
if (last_header_line_str != NULL) {
|
||||
/* Parse last header line. */
|
||||
last_header_line_str = php_stream_http_response_headers_parse(wrapper, stream,
|
||||
context, options, last_header_line_str, http_header_line,
|
||||
&http_header_line_length, response_code, response_header, &header_info);
|
||||
if (EXPECTED(last_header_line_str == NULL)) {
|
||||
if (UNEXPECTED(header_info.error)) {
|
||||
php_stream_close(stream);
|
||||
stream = NULL;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
/* Folding header present so continue. */
|
||||
continue;
|
||||
}
|
||||
} else if (
|
||||
!strncasecmp(http_header_line, "Transfer-Encoding:", sizeof("Transfer-Encoding:")-1)
|
||||
&& !strncasecmp(http_header_value, "Chunked", sizeof("Chunked")-1)
|
||||
) {
|
||||
|
||||
/* create filter to decode response body */
|
||||
if (!(options & STREAM_ONLY_GET_HEADERS)) {
|
||||
zend_long decode = 1;
|
||||
|
||||
if (context && (tmpzval = php_stream_context_get_option(context, "http", "auto_decode")) != NULL) {
|
||||
decode = zend_is_true(tmpzval);
|
||||
}
|
||||
if (decode) {
|
||||
transfer_encoding = php_stream_filter_create("dechunk", NULL, php_stream_is_persistent(stream));
|
||||
if (transfer_encoding) {
|
||||
/* don't store transfer-encodeing header */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if (!last_line) {
|
||||
/* The first line cannot start with spaces. */
|
||||
if (*http_header_line == ' ' || *http_header_line == '\t') {
|
||||
php_stream_close(stream);
|
||||
stream = NULL;
|
||||
php_stream_wrapper_log_error(wrapper, options,
|
||||
"HTTP invalid response format (folding header at the start)!");
|
||||
goto out;
|
||||
}
|
||||
/* Trim the first line if it is not the last line. */
|
||||
php_stream_http_response_header_trim(http_header_line, &http_header_line_length);
|
||||
}
|
||||
|
||||
{
|
||||
zval http_header;
|
||||
ZVAL_STRINGL(&http_header, http_header_line, http_header_line_length);
|
||||
zend_hash_next_index_insert(Z_ARRVAL_P(response_header), &http_header);
|
||||
if (last_line) {
|
||||
/* For the last line the last header line must be NULL. */
|
||||
ZEND_ASSERT(last_header_line_str == NULL);
|
||||
break;
|
||||
}
|
||||
/* Save current line as the last line so it gets parsed in the next round. */
|
||||
last_header_line_str = zend_string_init(http_header_line, http_header_line_length, 0);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!reqok || (location[0] != '\0' && follow_location)) {
|
||||
if (!follow_location || (((options & STREAM_ONLY_GET_HEADERS) || ignore_errors) && redirect_max <= 1)) {
|
||||
/* If the stream was closed early, we still want to process the last line to keep BC. */
|
||||
if (last_header_line_str != NULL) {
|
||||
php_stream_http_response_headers_parse(wrapper, stream, context, options,
|
||||
last_header_line_str, NULL, NULL, response_code, response_header, &header_info);
|
||||
}
|
||||
|
||||
if (!reqok || (header_info.location != NULL && header_info.follow_location)) {
|
||||
if (!header_info.follow_location || (((options & STREAM_ONLY_GET_HEADERS) || ignore_errors) && redirect_max <= 1)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (location[0] != '\0')
|
||||
php_stream_notify_info(context, PHP_STREAM_NOTIFY_REDIRECTED, location, 0);
|
||||
if (header_info.location != NULL)
|
||||
php_stream_notify_info(context, PHP_STREAM_NOTIFY_REDIRECTED, header_info.location, 0);
|
||||
|
||||
php_stream_close(stream);
|
||||
stream = NULL;
|
||||
|
||||
if (transfer_encoding) {
|
||||
php_stream_filter_free(transfer_encoding);
|
||||
transfer_encoding = NULL;
|
||||
if (header_info.transfer_encoding) {
|
||||
php_stream_filter_free(header_info.transfer_encoding);
|
||||
header_info.transfer_encoding = NULL;
|
||||
}
|
||||
|
||||
if (location[0] != '\0') {
|
||||
if (header_info.location != NULL) {
|
||||
|
||||
char new_path[HTTP_HEADER_BLOCK_SIZE];
|
||||
char loc_path[HTTP_HEADER_BLOCK_SIZE];
|
||||
char *new_path = NULL;
|
||||
|
||||
*new_path='\0';
|
||||
if (strlen(location)<8 || (strncasecmp(location, "http://", sizeof("http://")-1) &&
|
||||
strncasecmp(location, "https://", sizeof("https://")-1) &&
|
||||
strncasecmp(location, "ftp://", sizeof("ftp://")-1) &&
|
||||
strncasecmp(location, "ftps://", sizeof("ftps://")-1)))
|
||||
if (strlen(header_info.location) < 8 ||
|
||||
(strncasecmp(header_info.location, "http://", sizeof("http://")-1) &&
|
||||
strncasecmp(header_info.location, "https://", sizeof("https://")-1) &&
|
||||
strncasecmp(header_info.location, "ftp://", sizeof("ftp://")-1) &&
|
||||
strncasecmp(header_info.location, "ftps://", sizeof("ftps://")-1)))
|
||||
{
|
||||
if (*location != '/') {
|
||||
if (*(location+1) != '\0' && resource->path) {
|
||||
char *loc_path = NULL;
|
||||
if (*header_info.location != '/') {
|
||||
if (*(header_info.location+1) != '\0' && resource->path) {
|
||||
char *s = strrchr(ZSTR_VAL(resource->path), '/');
|
||||
if (!s) {
|
||||
s = ZSTR_VAL(resource->path);
|
||||
|
@ -899,29 +1070,35 @@ finish:
|
|||
if (resource->path &&
|
||||
ZSTR_VAL(resource->path)[0] == '/' &&
|
||||
ZSTR_VAL(resource->path)[1] == '\0') {
|
||||
snprintf(loc_path, sizeof(loc_path) - 1, "%s%s", ZSTR_VAL(resource->path), location);
|
||||
spprintf(&loc_path, 0, "%s%s", ZSTR_VAL(resource->path), header_info.location);
|
||||
} else {
|
||||
snprintf(loc_path, sizeof(loc_path) - 1, "%s/%s", ZSTR_VAL(resource->path), location);
|
||||
spprintf(&loc_path, 0, "%s/%s", ZSTR_VAL(resource->path), header_info.location);
|
||||
}
|
||||
} else {
|
||||
snprintf(loc_path, sizeof(loc_path) - 1, "/%s", location);
|
||||
spprintf(&loc_path, 0, "/%s", header_info.location);
|
||||
}
|
||||
} else {
|
||||
strlcpy(loc_path, location, sizeof(loc_path));
|
||||
loc_path = header_info.location;
|
||||
header_info.location = NULL;
|
||||
}
|
||||
if ((use_ssl && resource->port != 443) || (!use_ssl && resource->port != 80)) {
|
||||
snprintf(new_path, sizeof(new_path) - 1, "%s://%s:%d%s", ZSTR_VAL(resource->scheme), ZSTR_VAL(resource->host), resource->port, loc_path);
|
||||
spprintf(&new_path, 0, "%s://%s:%d%s", ZSTR_VAL(resource->scheme),
|
||||
ZSTR_VAL(resource->host), resource->port, loc_path);
|
||||
} else {
|
||||
snprintf(new_path, sizeof(new_path) - 1, "%s://%s%s", ZSTR_VAL(resource->scheme), ZSTR_VAL(resource->host), loc_path);
|
||||
spprintf(&new_path, 0, "%s://%s%s", ZSTR_VAL(resource->scheme),
|
||||
ZSTR_VAL(resource->host), loc_path);
|
||||
}
|
||||
efree(loc_path);
|
||||
} else {
|
||||
strlcpy(new_path, location, sizeof(new_path));
|
||||
new_path = header_info.location;
|
||||
header_info.location = NULL;
|
||||
}
|
||||
|
||||
php_url_free(resource);
|
||||
/* check for invalid redirection URLs */
|
||||
if ((resource = php_url_parse(new_path)) == NULL) {
|
||||
php_stream_wrapper_log_error(wrapper, options, "Invalid redirect URL! %s", new_path);
|
||||
efree(new_path);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -933,6 +1110,7 @@ finish:
|
|||
while (s < e) { \
|
||||
if (iscntrl(*s)) { \
|
||||
php_stream_wrapper_log_error(wrapper, options, "Invalid redirect URL! %s", new_path); \
|
||||
efree(new_path); \
|
||||
goto out; \
|
||||
} \
|
||||
s++; \
|
||||
|
@ -955,6 +1133,7 @@ finish:
|
|||
stream = php_stream_url_wrap_http_ex(
|
||||
wrapper, new_path, mode, options, opened_path, context,
|
||||
--redirect_max, new_flags, response_header STREAMS_CC);
|
||||
efree(new_path);
|
||||
} else {
|
||||
php_stream_wrapper_log_error(wrapper, options, "HTTP request failed! %s", tmp_line);
|
||||
}
|
||||
|
@ -967,6 +1146,10 @@ out:
|
|||
efree(http_header_line);
|
||||
}
|
||||
|
||||
if (header_info.location != NULL) {
|
||||
efree(header_info.location);
|
||||
}
|
||||
|
||||
if (resource) {
|
||||
php_url_free(resource);
|
||||
}
|
||||
|
@ -975,7 +1158,7 @@ out:
|
|||
if (header_init) {
|
||||
ZVAL_COPY(&stream->wrapperdata, response_header);
|
||||
}
|
||||
php_stream_notify_progress_init(context, 0, file_size);
|
||||
php_stream_notify_progress_init(context, 0, header_info.file_size);
|
||||
|
||||
/* Restore original chunk size now that we're done with headers */
|
||||
if (options & STREAM_WILL_CAST)
|
||||
|
@ -991,8 +1174,8 @@ out:
|
|||
/* restore mode */
|
||||
strlcpy(stream->mode, mode, sizeof(stream->mode));
|
||||
|
||||
if (transfer_encoding) {
|
||||
php_stream_filter_append(&stream->readfilters, transfer_encoding);
|
||||
if (header_info.transfer_encoding) {
|
||||
php_stream_filter_append(&stream->readfilters, header_info.transfer_encoding);
|
||||
}
|
||||
|
||||
/* It's possible that the server already sent in more data than just the headers.
|
||||
|
|
|
@ -70,23 +70,27 @@ do_test(1, true);
|
|||
echo "\n";
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Type='text/plain'
|
||||
Hello
|
||||
Size=5
|
||||
World
|
||||
--EXPECTF--
|
||||
|
||||
Type='text/plain'
|
||||
Hello
|
||||
Size=5
|
||||
World
|
||||
|
||||
Warning: file_get_contents(http://%s:%d): Failed to open stream: HTTP invalid response format (no colon in header line)! in %s
|
||||
|
||||
|
||||
Type='text/plain'
|
||||
Hello
|
||||
Size=5
|
||||
World
|
||||
|
||||
Warning: file_get_contents(http://%s:%d): Failed to open stream: HTTP invalid response format (no colon in header line)! in %s
|
||||
|
||||
|
||||
Type='text/plain'
|
||||
Hello
|
||||
Size=5
|
||||
World
|
||||
|
||||
Warning: file_get_contents(http://%s:%d): Failed to open stream: HTTP invalid response format (no colon in header line)! in %s
|
||||
|
||||
|
||||
Type='text/plain'
|
||||
Hello
|
||||
|
||||
Warning: file_get_contents(http://%s:%d): Failed to open stream: HTTP invalid response format (no colon in header line)! in %s
|
||||
|
|
|
@ -21,9 +21,7 @@ http_server_kill($pid);
|
|||
|
||||
--EXPECT--
|
||||
string(0) ""
|
||||
array(2) {
|
||||
array(1) {
|
||||
[0]=>
|
||||
string(15) "HTTP/1.0 200 Ok"
|
||||
[1]=>
|
||||
string(14) "Content-Length"
|
||||
}
|
||||
|
|
58
ext/standard/tests/http/ghsa-52jp-hrpf-2jff-001.phpt
Normal file
58
ext/standard/tests/http/ghsa-52jp-hrpf-2jff-001.phpt
Normal file
|
@ -0,0 +1,58 @@
|
|||
--TEST--
|
||||
GHSA-52jp-hrpf-2jff: HTTP stream wrapper truncate redirect location to 1024 bytes (success)
|
||||
--FILE--
|
||||
<?php
|
||||
$serverCode = <<<'CODE'
|
||||
$ctxt = stream_context_create([
|
||||
"socket" => [
|
||||
"tcp_nodelay" => true
|
||||
]
|
||||
]);
|
||||
|
||||
$server = stream_socket_server(
|
||||
"tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
||||
phpt_notify_server_start($server);
|
||||
|
||||
$conn = stream_socket_accept($server);
|
||||
|
||||
phpt_notify(message:"server-accepted");
|
||||
|
||||
$loc = str_repeat("y", 8000);
|
||||
fwrite($conn, "HTTP/1.0 301 Ok\r\nContent-Type: text/html;\r\nLocation: $loc\r\n\r\nbody\r\n");
|
||||
CODE;
|
||||
|
||||
$clientCode = <<<'CODE'
|
||||
function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {
|
||||
switch($notification_code) {
|
||||
case STREAM_NOTIFY_MIME_TYPE_IS:
|
||||
echo "Found the mime-type: ", $message, PHP_EOL;
|
||||
break;
|
||||
case STREAM_NOTIFY_REDIRECTED:
|
||||
echo "Redirected: ";
|
||||
var_dump($message);
|
||||
}
|
||||
}
|
||||
|
||||
$ctx = stream_context_create();
|
||||
stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
|
||||
var_dump(trim(file_get_contents("http://{{ ADDR }}", false, $ctx)));
|
||||
var_dump($http_response_header);
|
||||
CODE;
|
||||
|
||||
include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
||||
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
||||
?>
|
||||
--EXPECTF--
|
||||
Found the mime-type: text/html;
|
||||
Redirected: string(8000) "%s"
|
||||
|
||||
Warning: file_get_contents(http://127.0.0.1:%d): Failed to open stream: %s
|
||||
string(0) ""
|
||||
array(3) {
|
||||
[0]=>
|
||||
string(15) "HTTP/1.0 301 Ok"
|
||||
[1]=>
|
||||
string(24) "Content-Type: text/html;"
|
||||
[2]=>
|
||||
string(8010) "Location: %s"
|
||||
}
|
55
ext/standard/tests/http/ghsa-52jp-hrpf-2jff-002.phpt
Normal file
55
ext/standard/tests/http/ghsa-52jp-hrpf-2jff-002.phpt
Normal file
|
@ -0,0 +1,55 @@
|
|||
--TEST--
|
||||
GHSA-52jp-hrpf-2jff: HTTP stream wrapper truncate redirect location to 1024 bytes (over limit)
|
||||
--FILE--
|
||||
<?php
|
||||
$serverCode = <<<'CODE'
|
||||
$ctxt = stream_context_create([
|
||||
"socket" => [
|
||||
"tcp_nodelay" => true
|
||||
]
|
||||
]);
|
||||
|
||||
$server = stream_socket_server(
|
||||
"tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
||||
phpt_notify_server_start($server);
|
||||
|
||||
$conn = stream_socket_accept($server);
|
||||
|
||||
phpt_notify(message:"server-accepted");
|
||||
|
||||
$loc = str_repeat("y", 9000);
|
||||
fwrite($conn, "HTTP/1.0 301 Ok\r\nContent-Type: text/html;\r\nLocation: $loc\r\n\r\nbody\r\n");
|
||||
CODE;
|
||||
|
||||
$clientCode = <<<'CODE'
|
||||
function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {
|
||||
switch($notification_code) {
|
||||
case STREAM_NOTIFY_MIME_TYPE_IS:
|
||||
echo "Found the mime-type: ", $message, PHP_EOL;
|
||||
break;
|
||||
case STREAM_NOTIFY_REDIRECTED:
|
||||
echo "Redirected: ";
|
||||
var_dump($message);
|
||||
}
|
||||
}
|
||||
|
||||
$ctx = stream_context_create();
|
||||
stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
|
||||
var_dump(trim(file_get_contents("http://{{ ADDR }}", false, $ctx)));
|
||||
var_dump($http_response_header);
|
||||
CODE;
|
||||
|
||||
include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
||||
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
||||
?>
|
||||
--EXPECTF--
|
||||
Found the mime-type: text/html;
|
||||
|
||||
Warning: file_get_contents(http://127.0.0.1:%d): Failed to open stream: HTTP Location header size is over the limit of 8182 bytes in %s
|
||||
string(0) ""
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(15) "HTTP/1.0 301 Ok"
|
||||
[1]=>
|
||||
string(24) "Content-Type: text/html;"
|
||||
}
|
65
ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt
Normal file
65
ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt
Normal file
|
@ -0,0 +1,65 @@
|
|||
--TEST--
|
||||
GHSA-hgf5-96fm-v528: Stream HTTP wrapper header check might omit basic auth header (incorrect inside pos)
|
||||
--FILE--
|
||||
<?php
|
||||
$serverCode = <<<'CODE'
|
||||
$ctxt = stream_context_create([
|
||||
"socket" => [
|
||||
"tcp_nodelay" => true
|
||||
]
|
||||
]);
|
||||
|
||||
$server = stream_socket_server(
|
||||
"tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
||||
phpt_notify_server_start($server);
|
||||
|
||||
$conn = stream_socket_accept($server);
|
||||
|
||||
phpt_notify(message:"server-accepted");
|
||||
|
||||
$result = fread($conn, 1024);
|
||||
$encoded_result = base64_encode($result);
|
||||
|
||||
fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html; charset=utf-8\r\n\r\n$encoded_result\r\n");
|
||||
|
||||
CODE;
|
||||
|
||||
$clientCode = <<<'CODE'
|
||||
$opts = [
|
||||
"http" => [
|
||||
"method" => "GET",
|
||||
"header" => "Cookie: foo=bar\nauthorization:x\r\n"
|
||||
]
|
||||
];
|
||||
$ctx = stream_context_create($opts);
|
||||
var_dump(explode("\r\n", base64_decode(file_get_contents("http://user:pwd@{{ ADDR }}", false, $ctx))));
|
||||
var_dump($http_response_header);
|
||||
CODE;
|
||||
|
||||
include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
||||
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
||||
?>
|
||||
--EXPECTF--
|
||||
array(7) {
|
||||
[0]=>
|
||||
string(14) "GET / HTTP/1.1"
|
||||
[1]=>
|
||||
string(33) "Authorization: Basic dXNlcjpwd2Q="
|
||||
[2]=>
|
||||
string(21) "Host: 127.0.0.1:%d"
|
||||
[3]=>
|
||||
string(17) "Connection: close"
|
||||
[4]=>
|
||||
string(31) "Cookie: foo=bar
|
||||
authorization:x"
|
||||
[5]=>
|
||||
string(0) ""
|
||||
[6]=>
|
||||
string(0) ""
|
||||
}
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(15) "HTTP/1.0 200 Ok"
|
||||
[1]=>
|
||||
string(38) "Content-Type: text/html; charset=utf-8"
|
||||
}
|
62
ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt
Normal file
62
ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt
Normal file
|
@ -0,0 +1,62 @@
|
|||
--TEST--
|
||||
GHSA-hgf5-96fm-v528: Header parser of http stream wrapper does not handle folded headers (correct start pos)
|
||||
--FILE--
|
||||
<?php
|
||||
$serverCode = <<<'CODE'
|
||||
$ctxt = stream_context_create([
|
||||
"socket" => [
|
||||
"tcp_nodelay" => true
|
||||
]
|
||||
]);
|
||||
|
||||
$server = stream_socket_server(
|
||||
"tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
||||
phpt_notify_server_start($server);
|
||||
|
||||
$conn = stream_socket_accept($server);
|
||||
|
||||
phpt_notify(message:"server-accepted");
|
||||
|
||||
$result = fread($conn, 1024);
|
||||
$encoded_result = base64_encode($result);
|
||||
|
||||
fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html; charset=utf-8\r\n\r\n$encoded_result\r\n");
|
||||
|
||||
CODE;
|
||||
|
||||
$clientCode = <<<'CODE'
|
||||
$opts = [
|
||||
"http" => [
|
||||
"method" => "GET",
|
||||
"header" => "Authorization: Bearer x\r\n"
|
||||
]
|
||||
];
|
||||
$ctx = stream_context_create($opts);
|
||||
var_dump(explode("\r\n", base64_decode(file_get_contents("http://user:pwd@{{ ADDR }}", false, $ctx))));
|
||||
var_dump($http_response_header);
|
||||
CODE;
|
||||
|
||||
include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
||||
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
||||
?>
|
||||
--EXPECTF--
|
||||
array(6) {
|
||||
[0]=>
|
||||
string(14) "GET / HTTP/1.1"
|
||||
[1]=>
|
||||
string(21) "Host: 127.0.0.1:%d"
|
||||
[2]=>
|
||||
string(17) "Connection: close"
|
||||
[3]=>
|
||||
string(23) "Authorization: Bearer x"
|
||||
[4]=>
|
||||
string(0) ""
|
||||
[5]=>
|
||||
string(0) ""
|
||||
}
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(15) "HTTP/1.0 200 Ok"
|
||||
[1]=>
|
||||
string(38) "Content-Type: text/html; charset=utf-8"
|
||||
}
|
64
ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt
Normal file
64
ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt
Normal file
|
@ -0,0 +1,64 @@
|
|||
--TEST--
|
||||
GHSA-hgf5-96fm-v528: Header parser of http stream wrapper does not handle folded headers (correct middle pos)
|
||||
--FILE--
|
||||
<?php
|
||||
$serverCode = <<<'CODE'
|
||||
$ctxt = stream_context_create([
|
||||
"socket" => [
|
||||
"tcp_nodelay" => true
|
||||
]
|
||||
]);
|
||||
|
||||
$server = stream_socket_server(
|
||||
"tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
||||
phpt_notify_server_start($server);
|
||||
|
||||
$conn = stream_socket_accept($server);
|
||||
|
||||
phpt_notify(message:"server-accepted");
|
||||
|
||||
$result = fread($conn, 1024);
|
||||
$encoded_result = base64_encode($result);
|
||||
|
||||
fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html; charset=utf-8\r\n\r\n$encoded_result\r\n");
|
||||
|
||||
CODE;
|
||||
|
||||
$clientCode = <<<'CODE'
|
||||
$opts = [
|
||||
"http" => [
|
||||
"method" => "GET",
|
||||
"header" => "Cookie: x=y\r\nAuthorization: Bearer x\r\n"
|
||||
]
|
||||
];
|
||||
$ctx = stream_context_create($opts);
|
||||
var_dump(explode("\r\n", base64_decode(file_get_contents("http://user:pwd@{{ ADDR }}", false, $ctx))));
|
||||
var_dump($http_response_header);
|
||||
CODE;
|
||||
|
||||
include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
||||
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
||||
?>
|
||||
--EXPECTF--
|
||||
array(7) {
|
||||
[0]=>
|
||||
string(14) "GET / HTTP/1.1"
|
||||
[1]=>
|
||||
string(21) "Host: 127.0.0.1:%d"
|
||||
[2]=>
|
||||
string(17) "Connection: close"
|
||||
[3]=>
|
||||
string(11) "Cookie: x=y"
|
||||
[4]=>
|
||||
string(23) "Authorization: Bearer x"
|
||||
[5]=>
|
||||
string(0) ""
|
||||
[6]=>
|
||||
string(0) ""
|
||||
}
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(15) "HTTP/1.0 200 Ok"
|
||||
[1]=>
|
||||
string(38) "Content-Type: text/html; charset=utf-8"
|
||||
}
|
51
ext/standard/tests/http/ghsa-pcmh-g36c-qc44-001.phpt
Normal file
51
ext/standard/tests/http/ghsa-pcmh-g36c-qc44-001.phpt
Normal file
|
@ -0,0 +1,51 @@
|
|||
--TEST--
|
||||
GHSA-pcmh-g36c-qc44: Header parser of http stream wrapper does not verify header name and colon (colon)
|
||||
--FILE--
|
||||
<?php
|
||||
$serverCode = <<<'CODE'
|
||||
$ctxt = stream_context_create([
|
||||
"socket" => [
|
||||
"tcp_nodelay" => true
|
||||
]
|
||||
]);
|
||||
|
||||
$server = stream_socket_server(
|
||||
"tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
||||
phpt_notify_server_start($server);
|
||||
|
||||
$conn = stream_socket_accept($server);
|
||||
|
||||
phpt_notify(message:"server-accepted");
|
||||
|
||||
fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html\r\nWrong-Header\r\nGood-Header: test\r\n\r\nbody\r\n");
|
||||
CODE;
|
||||
|
||||
$clientCode = <<<'CODE'
|
||||
function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {
|
||||
switch($notification_code) {
|
||||
case STREAM_NOTIFY_MIME_TYPE_IS:
|
||||
echo "Found the mime-type: ", $message, PHP_EOL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$ctx = stream_context_create();
|
||||
stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
|
||||
var_dump(file_get_contents("http://{{ ADDR }}", false, $ctx));
|
||||
var_dump($http_response_header);
|
||||
CODE;
|
||||
|
||||
include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
||||
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
||||
?>
|
||||
--EXPECTF--
|
||||
Found the mime-type: text/html
|
||||
|
||||
Warning: file_get_contents(http://127.0.0.1:%d): Failed to open stream: HTTP invalid response format (no colon in header line)! in %s
|
||||
bool(false)
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(15) "HTTP/1.0 200 Ok"
|
||||
[1]=>
|
||||
string(23) "Content-Type: text/html"
|
||||
}
|
51
ext/standard/tests/http/ghsa-pcmh-g36c-qc44-002.phpt
Normal file
51
ext/standard/tests/http/ghsa-pcmh-g36c-qc44-002.phpt
Normal file
|
@ -0,0 +1,51 @@
|
|||
--TEST--
|
||||
GHSA-pcmh-g36c-qc44: Header parser of http stream wrapper does not verify header name and colon (name)
|
||||
--FILE--
|
||||
<?php
|
||||
$serverCode = <<<'CODE'
|
||||
$ctxt = stream_context_create([
|
||||
"socket" => [
|
||||
"tcp_nodelay" => true
|
||||
]
|
||||
]);
|
||||
|
||||
$server = stream_socket_server(
|
||||
"tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
||||
phpt_notify_server_start($server);
|
||||
|
||||
$conn = stream_socket_accept($server);
|
||||
|
||||
phpt_notify(message:"server-accepted");
|
||||
|
||||
fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html\r\nWrong-Header : test\r\nGood-Header: test\r\n\r\nbody\r\n");
|
||||
CODE;
|
||||
|
||||
$clientCode = <<<'CODE'
|
||||
function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {
|
||||
switch($notification_code) {
|
||||
case STREAM_NOTIFY_MIME_TYPE_IS:
|
||||
echo "Found the mime-type: ", $message, PHP_EOL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$ctx = stream_context_create();
|
||||
stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
|
||||
var_dump(file_get_contents("http://{{ ADDR }}", false, $ctx));
|
||||
var_dump($http_response_header);
|
||||
CODE;
|
||||
|
||||
include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
||||
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
||||
?>
|
||||
--EXPECTF--
|
||||
Found the mime-type: text/html
|
||||
|
||||
Warning: file_get_contents(http://127.0.0.1:%d): Failed to open stream: HTTP invalid response format (space in header name)! in %s
|
||||
bool(false)
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(15) "HTTP/1.0 200 Ok"
|
||||
[1]=>
|
||||
string(23) "Content-Type: text/html"
|
||||
}
|
49
ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt
Normal file
49
ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt
Normal file
|
@ -0,0 +1,49 @@
|
|||
--TEST--
|
||||
GHSA-v8xr-gpvj-cx9g: Header parser of http stream wrapper does not handle folded headers (single)
|
||||
--FILE--
|
||||
<?php
|
||||
$serverCode = <<<'CODE'
|
||||
$ctxt = stream_context_create([
|
||||
"socket" => [
|
||||
"tcp_nodelay" => true
|
||||
]
|
||||
]);
|
||||
|
||||
$server = stream_socket_server(
|
||||
"tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
||||
phpt_notify_server_start($server);
|
||||
|
||||
$conn = stream_socket_accept($server);
|
||||
|
||||
phpt_notify(message:"server-accepted");
|
||||
|
||||
fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html;\r\n charset=utf-8\r\n\r\nbody\r\n");
|
||||
CODE;
|
||||
|
||||
$clientCode = <<<'CODE'
|
||||
function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {
|
||||
switch($notification_code) {
|
||||
case STREAM_NOTIFY_MIME_TYPE_IS:
|
||||
echo "Found the mime-type: ", $message, PHP_EOL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$ctx = stream_context_create();
|
||||
stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
|
||||
var_dump(trim(file_get_contents("http://{{ ADDR }}", false, $ctx)));
|
||||
var_dump($http_response_header);
|
||||
CODE;
|
||||
|
||||
include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
||||
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
||||
?>
|
||||
--EXPECTF--
|
||||
Found the mime-type: text/html; charset=utf-8
|
||||
string(4) "body"
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(15) "HTTP/1.0 200 Ok"
|
||||
[1]=>
|
||||
string(38) "Content-Type: text/html; charset=utf-8"
|
||||
}
|
51
ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt
Normal file
51
ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt
Normal file
|
@ -0,0 +1,51 @@
|
|||
--TEST--
|
||||
GHSA-v8xr-gpvj-cx9g: Header parser of http stream wrapper does not handle folded headers (multiple)
|
||||
--FILE--
|
||||
<?php
|
||||
$serverCode = <<<'CODE'
|
||||
$ctxt = stream_context_create([
|
||||
"socket" => [
|
||||
"tcp_nodelay" => true
|
||||
]
|
||||
]);
|
||||
|
||||
$server = stream_socket_server(
|
||||
"tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
||||
phpt_notify_server_start($server);
|
||||
|
||||
$conn = stream_socket_accept($server);
|
||||
|
||||
phpt_notify(message:"server-accepted");
|
||||
|
||||
fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html;\r\nCustom-Header: somevalue;\r\n param1=value1; \r\n param2=value2\r\n\r\nbody\r\n");
|
||||
CODE;
|
||||
|
||||
$clientCode = <<<'CODE'
|
||||
function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {
|
||||
switch($notification_code) {
|
||||
case STREAM_NOTIFY_MIME_TYPE_IS:
|
||||
echo "Found the mime-type: ", $message, PHP_EOL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$ctx = stream_context_create();
|
||||
stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
|
||||
var_dump(trim(file_get_contents("http://{{ ADDR }}", false, $ctx)));
|
||||
var_dump($http_response_header);
|
||||
CODE;
|
||||
|
||||
include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
||||
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
||||
?>
|
||||
--EXPECTF--
|
||||
Found the mime-type: text/html;
|
||||
string(4) "body"
|
||||
array(3) {
|
||||
[0]=>
|
||||
string(15) "HTTP/1.0 200 Ok"
|
||||
[1]=>
|
||||
string(24) "Content-Type: text/html;"
|
||||
[2]=>
|
||||
string(54) "Custom-Header: somevalue; param1=value1; param2=value2"
|
||||
}
|
49
ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt
Normal file
49
ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt
Normal file
|
@ -0,0 +1,49 @@
|
|||
--TEST--
|
||||
GHSA-v8xr-gpvj-cx9g: Header parser of http stream wrapper does not handle folded headers (empty)
|
||||
--FILE--
|
||||
<?php
|
||||
$serverCode = <<<'CODE'
|
||||
$ctxt = stream_context_create([
|
||||
"socket" => [
|
||||
"tcp_nodelay" => true
|
||||
]
|
||||
]);
|
||||
|
||||
$server = stream_socket_server(
|
||||
"tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
||||
phpt_notify_server_start($server);
|
||||
|
||||
$conn = stream_socket_accept($server);
|
||||
|
||||
phpt_notify(message:"server-accepted");
|
||||
|
||||
fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html;\r\n \r\n charset=utf-8\r\n\r\nbody\r\n");
|
||||
CODE;
|
||||
|
||||
$clientCode = <<<'CODE'
|
||||
function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {
|
||||
switch($notification_code) {
|
||||
case STREAM_NOTIFY_MIME_TYPE_IS:
|
||||
echo "Found the mime-type: ", $message, PHP_EOL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$ctx = stream_context_create();
|
||||
stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
|
||||
var_dump(trim(file_get_contents("http://{{ ADDR }}", false, $ctx)));
|
||||
var_dump($http_response_header);
|
||||
CODE;
|
||||
|
||||
include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
||||
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
||||
?>
|
||||
--EXPECTF--
|
||||
Found the mime-type: text/html; charset=utf-8
|
||||
string(4) "body"
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(15) "HTTP/1.0 200 Ok"
|
||||
[1]=>
|
||||
string(38) "Content-Type: text/html; charset=utf-8"
|
||||
}
|
48
ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt
Normal file
48
ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt
Normal file
|
@ -0,0 +1,48 @@
|
|||
--TEST--
|
||||
GHSA-v8xr-gpvj-cx9g: Header parser of http stream wrapper does not handle folded headers (first line)
|
||||
--FILE--
|
||||
<?php
|
||||
$serverCode = <<<'CODE'
|
||||
$ctxt = stream_context_create([
|
||||
"socket" => [
|
||||
"tcp_nodelay" => true
|
||||
]
|
||||
]);
|
||||
|
||||
$server = stream_socket_server(
|
||||
"tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
||||
phpt_notify_server_start($server);
|
||||
|
||||
$conn = stream_socket_accept($server);
|
||||
|
||||
phpt_notify(message:"server-accepted");
|
||||
|
||||
fwrite($conn, "HTTP/1.0 200 Ok\r\n Content-Type: text/html;\r\n \r\n charset=utf-8\r\n\r\nbody\r\n");
|
||||
CODE;
|
||||
|
||||
$clientCode = <<<'CODE'
|
||||
function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {
|
||||
switch($notification_code) {
|
||||
case STREAM_NOTIFY_MIME_TYPE_IS:
|
||||
echo "Found the mime-type: ", $message, PHP_EOL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$ctx = stream_context_create();
|
||||
stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
|
||||
var_dump(file_get_contents("http://{{ ADDR }}", false, $ctx));
|
||||
var_dump($http_response_header);
|
||||
CODE;
|
||||
|
||||
include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
||||
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
||||
?>
|
||||
--EXPECTF--
|
||||
|
||||
Warning: file_get_contents(http://127.0.0.1:%d): Failed to open stream: HTTP invalid response format (folding header at the start)! in %s
|
||||
bool(false)
|
||||
array(1) {
|
||||
[0]=>
|
||||
string(15) "HTTP/1.0 200 Ok"
|
||||
}
|
48
ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt
Normal file
48
ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt
Normal file
|
@ -0,0 +1,48 @@
|
|||
--TEST--
|
||||
GHSA-v8xr-gpvj-cx9g: Header parser of http stream wrapper does not handle folded headers (CR before header name)
|
||||
--FILE--
|
||||
<?php
|
||||
$serverCode = <<<'CODE'
|
||||
$ctxt = stream_context_create([
|
||||
"socket" => [
|
||||
"tcp_nodelay" => true
|
||||
]
|
||||
]);
|
||||
|
||||
$server = stream_socket_server(
|
||||
"tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
||||
phpt_notify_server_start($server);
|
||||
|
||||
$conn = stream_socket_accept($server);
|
||||
|
||||
phpt_notify(message:"server-accepted");
|
||||
|
||||
fwrite($conn, "HTTP/1.0 200 Ok\r\n\rIgnored: ignored\r\n\r\nbody\r\n");
|
||||
CODE;
|
||||
|
||||
$clientCode = <<<'CODE'
|
||||
function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {
|
||||
switch($notification_code) {
|
||||
case STREAM_NOTIFY_MIME_TYPE_IS:
|
||||
echo "Found the mime-type: ", $message, PHP_EOL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$ctx = stream_context_create();
|
||||
stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
|
||||
var_dump(file_get_contents("http://{{ ADDR }}", false, $ctx));
|
||||
var_dump($http_response_header);
|
||||
CODE;
|
||||
|
||||
include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
||||
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
||||
?>
|
||||
--EXPECTF--
|
||||
|
||||
Warning: file_get_contents(http://127.0.0.1:%d): Failed to open stream: HTTP invalid header name (cannot start with CR character)! in %s
|
||||
bool(false)
|
||||
array(1) {
|
||||
[0]=>
|
||||
string(15) "HTTP/1.0 200 Ok"
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
--TEST--
|
||||
$http_reponse_header (whitespace-only "header")
|
||||
--SKIPIF--
|
||||
<?php require 'server.inc'; http_server_skipif(); ?>
|
||||
--INI--
|
||||
allow_url_fopen=1
|
||||
--FILE--
|
||||
<?php
|
||||
require 'server.inc';
|
||||
|
||||
$responses = array(
|
||||
"data://text/plain,HTTP/1.0 200 Ok\r\n \r\n\r\nBody",
|
||||
);
|
||||
|
||||
['pid' => $pid, 'uri' => $uri] = http_server($responses, $output);
|
||||
|
||||
$f = file_get_contents($uri);
|
||||
var_dump($f);
|
||||
var_dump($http_response_header);
|
||||
|
||||
http_server_kill($pid);
|
||||
|
||||
--EXPECT--
|
||||
string(4) "Body"
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(15) "HTTP/1.0 200 Ok"
|
||||
[1]=>
|
||||
string(0) ""
|
||||
}
|
42
ext/xml/tests/toffset_bounds.phpt
Normal file
42
ext/xml/tests/toffset_bounds.phpt
Normal file
|
@ -0,0 +1,42 @@
|
|||
--TEST--
|
||||
XML_OPTION_SKIP_TAGSTART bounds
|
||||
--EXTENSIONS--
|
||||
xml
|
||||
--FILE--
|
||||
<?php
|
||||
$sample = "<?xml version=\"1.0\"?><test><child/></test>";
|
||||
$parser = xml_parser_create();
|
||||
xml_parser_set_option($parser, XML_OPTION_SKIP_TAGSTART, 100);
|
||||
$res = xml_parse_into_struct($parser,$sample,$vals,$index);
|
||||
var_dump($vals);
|
||||
?>
|
||||
--EXPECT--
|
||||
array(3) {
|
||||
[0]=>
|
||||
array(3) {
|
||||
["tag"]=>
|
||||
string(0) ""
|
||||
["type"]=>
|
||||
string(4) "open"
|
||||
["level"]=>
|
||||
int(1)
|
||||
}
|
||||
[1]=>
|
||||
array(3) {
|
||||
["tag"]=>
|
||||
string(0) ""
|
||||
["type"]=>
|
||||
string(8) "complete"
|
||||
["level"]=>
|
||||
int(2)
|
||||
}
|
||||
[2]=>
|
||||
array(3) {
|
||||
["tag"]=>
|
||||
string(0) ""
|
||||
["type"]=>
|
||||
string(5) "close"
|
||||
["level"]=>
|
||||
int(1)
|
||||
}
|
||||
}
|
|
@ -657,9 +657,11 @@ void _xml_startElementHandler(void *userData, const XML_Char *name, const XML_Ch
|
|||
array_init(&tag);
|
||||
array_init(&atr);
|
||||
|
||||
_xml_add_to_info(parser, ZSTR_VAL(tag_name) + parser->toffset);
|
||||
char *skipped_tag_name = SKIP_TAGSTART(ZSTR_VAL(tag_name));
|
||||
|
||||
add_assoc_string(&tag, "tag", SKIP_TAGSTART(ZSTR_VAL(tag_name))); /* cast to avoid gcc-warning */
|
||||
_xml_add_to_info(parser, skipped_tag_name);
|
||||
|
||||
add_assoc_string(&tag, "tag", skipped_tag_name);
|
||||
add_assoc_string(&tag, "type", "open");
|
||||
add_assoc_long(&tag, "level", parser->level);
|
||||
|
||||
|
@ -741,12 +743,14 @@ void _xml_endElementHandler(void *userData, const XML_Char *name)
|
|||
add_assoc_string(zv, "type", "complete");
|
||||
}
|
||||
} else {
|
||||
_xml_add_to_info(parser, ZSTR_VAL(tag_name) + parser->toffset);
|
||||
char *skipped_tag_name = SKIP_TAGSTART(ZSTR_VAL(tag_name));
|
||||
|
||||
_xml_add_to_info(parser, skipped_tag_name);
|
||||
|
||||
zval *data = xml_get_separated_data(parser);
|
||||
if (EXPECTED(data)) {
|
||||
array_init(&tag);
|
||||
add_assoc_string(&tag, "tag", SKIP_TAGSTART(ZSTR_VAL(tag_name))); /* cast to avoid gcc-warning */
|
||||
add_assoc_string(&tag, "tag", skipped_tag_name);
|
||||
add_assoc_string(&tag, "type", "close");
|
||||
add_assoc_long(&tag, "level", parser->level);
|
||||
zend_hash_next_index_insert(Z_ARRVAL_P(data), &tag);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue