Fix GHSA-w8qr-v226-r27w

We should not early-out with success status if we found an ipv6
hostname, we should keep checking the rest of the conditions.
Because integrating the if-check of the ipv6 hostname in the
"Validate domain" if-check made the code hard to read, I extracted the
condition out to a separate function. This also required to make
a few pointers const in order to have some clean code.
This commit is contained in:
Niels Dossche 2024-05-22 22:25:02 +02:00 committed by Pierrick Charron
parent 88315f263d
commit ec803292c2
No known key found for this signature in database
GPG key ID: 286AF1F9897469DC
2 changed files with 61 additions and 15 deletions

View file

@ -89,7 +89,7 @@
#define FORMAT_IPV4 4
#define FORMAT_IPV6 6
static int _php_filter_validate_ipv6(char *str, size_t str_len, int ip[8]);
static int _php_filter_validate_ipv6(const char *str, size_t str_len, int ip[8]);
static int php_filter_parse_int(const char *str, size_t str_len, zend_long *ret) { /* {{{ */
zend_long ctx_value;
@ -580,6 +580,14 @@ static int is_userinfo_valid(zend_string *str)
return 1;
}
static bool php_filter_is_valid_ipv6_hostname(const char *s, size_t l)
{
const char *e = s + l;
const char *t = e - 1;
return *s == '[' && *t == ']' && _php_filter_validate_ipv6(s + 1, l - 2, NULL);
}
void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
{
php_url *url;
@ -600,7 +608,7 @@ void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
if (url->scheme != NULL &&
(zend_string_equals_literal_ci(url->scheme, "http") || zend_string_equals_literal_ci(url->scheme, "https"))) {
char *e, *s, *t;
const char *s;
size_t l;
if (url->host == NULL) {
@ -609,17 +617,14 @@ void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
s = ZSTR_VAL(url->host);
l = ZSTR_LEN(url->host);
e = s + l;
t = e - 1;
/* An IPv6 enclosed by square brackets is a valid hostname */
if (*s == '[' && *t == ']' && _php_filter_validate_ipv6((s + 1), l - 2, NULL)) {
php_url_free(url);
return;
}
// Validate domain
if (!_php_filter_validate_domain(ZSTR_VAL(url->host), l, FILTER_FLAG_HOSTNAME)) {
if (
/* An IPv6 enclosed by square brackets is a valid hostname.*/
!php_filter_is_valid_ipv6_hostname(s, l) &&
/* Validate domain.
* This includes a loose check for an IPv4 address. */
!_php_filter_validate_domain(ZSTR_VAL(url->host), l, FILTER_FLAG_HOSTNAME)
) {
php_url_free(url);
RETURN_VALIDATION_FAILED
}
@ -753,15 +758,15 @@ static int _php_filter_validate_ipv4(char *str, size_t str_len, int *ip) /* {{{
}
/* }}} */
static int _php_filter_validate_ipv6(char *str, size_t str_len, int ip[8]) /* {{{ */
static int _php_filter_validate_ipv6(const char *str, size_t str_len, int ip[8]) /* {{{ */
{
int compressed_pos = -1;
int blocks = 0;
int num, n, i;
char *ipv4;
char *end;
const char *end;
int ip4elm[4];
char *s = str;
const char *s = str;
if (!memchr(str, ':', str_len)) {
return 0;

View file

@ -0,0 +1,41 @@
--TEST--
GHSA-w8qr-v226-r27w
--EXTENSIONS--
filter
--FILE--
<?php
function test(string $input) {
var_dump(filter_var($input, FILTER_VALIDATE_URL));
}
echo "--- These ones should fail ---\n";
test("http://t[est@127.0.0.1");
test("http://t[est@[::1]");
test("http://t[est@[::1");
test("http://t[est@::1]");
test("http://php.net\\@aliyun.com/aaa.do");
test("http://test[@2001:db8:3333:4444:5555:6666:1.2.3.4]");
test("http://te[st@2001:db8:3333:4444:5555:6666:1.2.3.4]");
test("http://te[st@2001:db8:3333:4444:5555:6666:1.2.3.4");
echo "--- These ones should work ---\n";
test("http://test@127.0.0.1");
test("http://test@[2001:db8:3333:4444:5555:6666:1.2.3.4]");
test("http://test@[::1]");
?>
--EXPECT--
--- These ones should fail ---
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
--- These ones should work ---
string(21) "http://test@127.0.0.1"
string(50) "http://test@[2001:db8:3333:4444:5555:6666:1.2.3.4]"
string(17) "http://test@[::1]"