Skip to content

Commit 3bec7e4

Browse files
committed
ext/uri: Validate credentials against normalized builder hosts
Reject credentials and ports when a host becomes empty after normalization instead of silently discarding them. Perform validation once, after setting the host.
1 parent a7a2303 commit 3bec7e4

5 files changed

Lines changed: 137 additions & 21 deletions
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--TEST--
2+
Test Uri\WhatWg\UrlBuilder::build() - error - password with a host that normalizes to empty
3+
--FILE--
4+
<?php
5+
6+
$builder = new Uri\WhatWg\UrlBuilder();
7+
$builder->setScheme("foo");
8+
$builder->setHost("\t\n");
9+
$builder->setPassword("pass");
10+
$softErrors = ["unchanged"];
11+
12+
try {
13+
$builder->build(softErrors: $softErrors);
14+
} catch (Throwable $e) {
15+
echo $e::class, ': ', $e->getMessage(), "\n";
16+
var_dump($e->errors);
17+
}
18+
19+
var_dump($softErrors);
20+
21+
?>
22+
--EXPECTF--
23+
Uri\WhatWg\InvalidUrlException: The specified URL cannot have password
24+
array(1) {
25+
[0]=>
26+
object(Uri\WhatWg\UrlValidationError)#%d (%d) {
27+
["context"]=>
28+
string(2) "
29+
"
30+
["type"]=>
31+
enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit)
32+
["failure"]=>
33+
bool(false)
34+
}
35+
}
36+
array(1) {
37+
[0]=>
38+
string(9) "unchanged"
39+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--TEST--
2+
Test Uri\WhatWg\UrlBuilder::build() - error - port with a host that normalizes to empty
3+
--FILE--
4+
<?php
5+
6+
$builder = new Uri\WhatWg\UrlBuilder();
7+
$builder->setScheme("foo");
8+
$builder->setHost("\t\n");
9+
$builder->setPort(123);
10+
$softErrors = ["unchanged"];
11+
12+
try {
13+
$builder->build(softErrors: $softErrors);
14+
} catch (Throwable $e) {
15+
echo $e::class, ': ', $e->getMessage(), "\n";
16+
var_dump($e->errors);
17+
}
18+
19+
var_dump($softErrors);
20+
21+
?>
22+
--EXPECTF--
23+
Uri\WhatWg\InvalidUrlException: The specified URL cannot have port
24+
array(1) {
25+
[0]=>
26+
object(Uri\WhatWg\UrlValidationError)#%d (%d) {
27+
["context"]=>
28+
string(2) "
29+
"
30+
["type"]=>
31+
enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit)
32+
["failure"]=>
33+
bool(false)
34+
}
35+
}
36+
array(1) {
37+
[0]=>
38+
string(9) "unchanged"
39+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--TEST--
2+
Test Uri\WhatWg\UrlBuilder::build() - error - username with a host that normalizes to empty
3+
--FILE--
4+
<?php
5+
6+
$builder = new Uri\WhatWg\UrlBuilder();
7+
$builder->setScheme("foo");
8+
$builder->setHost("\t\n");
9+
$builder->setUsername("user");
10+
$softErrors = ["unchanged"];
11+
12+
try {
13+
$builder->build(softErrors: $softErrors);
14+
} catch (Throwable $e) {
15+
echo $e::class, ': ', $e->getMessage(), "\n";
16+
var_dump($e->errors);
17+
}
18+
19+
var_dump($softErrors);
20+
21+
?>
22+
--EXPECTF--
23+
Uri\WhatWg\InvalidUrlException: The specified URL cannot have username
24+
array(1) {
25+
[0]=>
26+
object(Uri\WhatWg\UrlValidationError)#%d (%d) {
27+
["context"]=>
28+
string(2) "
29+
"
30+
["type"]=>
31+
enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit)
32+
["failure"]=>
33+
bool(false)
34+
}
35+
}
36+
array(1) {
37+
[0]=>
38+
string(9) "unchanged"
39+
}

ext/uri/tests/whatwg/builder/password_error_missing_host.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ try {
1515

1616
?>
1717
--EXPECT--
18-
Uri\WhatWg\InvalidUrlException: The specified URL cannot have password
18+
Uri\WhatWg\InvalidUrlException: The specified host is malformed (HostMissing)

ext/uri/uri_parser_whatwg.c

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -989,26 +989,6 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(2, 3, 4, 5, 6, 7, 8, 9) lxb_url_t *php_uri_parser_wh
989989
const zval *host, const zval *port, const zval *path, const zval *query, const zval *fragment,
990990
zval *soft_errors_zv
991991
) {
992-
if (Z_TYPE_P(host) == IS_NULL ||
993-
Z_STRLEN_P(host) == 0 ||
994-
php_uri_parser_whatwg_get_special_scheme(Z_STR_P(scheme)) == LXB_URL_SCHEMEL_TYPE_FILE
995-
) {
996-
if (Z_TYPE_P(username) != IS_NULL) {
997-
php_uri_parser_whatwg_throw_exception("The specified URL cannot have username");
998-
return NULL;
999-
}
1000-
1001-
if (Z_TYPE_P(password) != IS_NULL) {
1002-
php_uri_parser_whatwg_throw_exception("The specified URL cannot have password");
1003-
return NULL;
1004-
}
1005-
1006-
if (Z_TYPE_P(port) != IS_NULL) {
1007-
php_uri_parser_whatwg_throw_exception("The specified URL cannot have port");
1008-
return NULL;
1009-
}
1010-
}
1011-
1012992
lxb_url_parser_clean(&lexbor_parser);
1013993

1014994
lxb_url_t *lexbor_url = lexbor_mraw_calloc(lexbor_parser.mraw, sizeof(*lexbor_url));
@@ -1042,6 +1022,25 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(2, 3, 4, 5, 6, 7, 8, 9) lxb_url_t *php_uri_parser_wh
10421022
goto failure;
10431023
}
10441024

1025+
if (lexbor_url->host.type == LXB_URL_HOST_TYPE__UNDEF
1026+
|| lexbor_url->host.type == LXB_URL_HOST_TYPE_EMPTY
1027+
|| lexbor_url->scheme.type == LXB_URL_SCHEMEL_TYPE_FILE) {
1028+
if (Z_TYPE_P(username) != IS_NULL) {
1029+
php_uri_parser_whatwg_throw_exception("The specified URL cannot have username");
1030+
goto failure;
1031+
}
1032+
1033+
if (Z_TYPE_P(password) != IS_NULL) {
1034+
php_uri_parser_whatwg_throw_exception("The specified URL cannot have password");
1035+
goto failure;
1036+
}
1037+
1038+
if (Z_TYPE_P(port) != IS_NULL) {
1039+
php_uri_parser_whatwg_throw_exception("The specified URL cannot have port");
1040+
goto failure;
1041+
}
1042+
}
1043+
10451044
/* Intentionally writing username after host to avoid error when the username is set but the host is missing */
10461045
result = php_uri_parser_whatwg_username_write(lexbor_url, username, NULL);
10471046
php_uri_parser_whatwg_build_errors(&errors);

0 commit comments

Comments
 (0)