diff --git a/src/wp-admin/includes/file.php b/src/wp-admin/includes/file.php index 052c235e15..8c4797d113 100644 --- a/src/wp-admin/includes/file.php +++ b/src/wp-admin/includes/file.php @@ -1126,7 +1126,11 @@ function download_url( $url, $timeout = 300, $signature_verification = false ) { return new WP_Error( 'http_no_url', __( 'Invalid URL Provided.' ) ); } - $url_filename = basename( parse_url( $url, PHP_URL_PATH ) ); + $url_path = parse_url( $url, PHP_URL_PATH ); + $url_filename = ''; + if ( is_string( $url_path ) && '' !== $url_path ) { + $url_filename = basename( $url_path ); + } $tmpfname = wp_tempnam( $url_filename ); if ( ! $tmpfname ) { @@ -1212,9 +1216,8 @@ function download_url( $url, $timeout = 300, $signature_verification = false ) { // WordPress.org stores signatures at $package_url.sig. $signature_url = false; - $url_path = parse_url( $url, PHP_URL_PATH ); - if ( '.zip' === substr( $url_path, -4 ) || '.tar.gz' === substr( $url_path, -7 ) ) { + if ( is_string( $url_path ) && ( '.zip' === substr( $url_path, -4 ) || '.tar.gz' === substr( $url_path, -7 ) ) ) { $signature_url = str_replace( $url_path, $url_path . '.sig', $url ); } @@ -1243,7 +1246,7 @@ function download_url( $url, $timeout = 300, $signature_verification = false ) { } // Perform the checks. - $signature_verification = verify_file_signature( $tmpfname, $signature, basename( parse_url( $url, PHP_URL_PATH ) ) ); + $signature_verification = verify_file_signature( $tmpfname, $signature, $url_filename ); } if ( is_wp_error( $signature_verification ) ) { diff --git a/tests/phpunit/tests/admin/includesFile.php b/tests/phpunit/tests/admin/includesFile.php index a3af5da2d1..5c58cbfcd4 100644 --- a/tests/phpunit/tests/admin/includesFile.php +++ b/tests/phpunit/tests/admin/includesFile.php @@ -77,4 +77,78 @@ class Tests_Admin_includesFile extends WP_UnitTestCase { public function __return_5() { return 5; } + + /** + * Verify that a WP_Error object is returned when invalid input is passed as the `$url` parameter. + * + * @covers ::download_url + * @dataProvider data_download_url_empty_url + * + * @param mixed $url Input URL. + */ + public function test_download_url_empty_url( $url ) { + $error = download_url( $url ); + $this->assertWPError( $error ); + $this->assertSame( 'http_no_url', $error->get_error_code() ); + $this->assertSame( 'Invalid URL Provided.', $error->get_error_message() ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_download_url_empty_url() { + return array( + 'null' => array( null ), + 'false' => array( false ), + 'integer 0' => array( 0 ), + 'empty string' => array( '' ), + 'string 0' => array( '0' ), + ); + } + + /** + * Test that PHP 8.1 "passing null to non-nullable" deprecation notice + * is not thrown when the `$url` does not have a path component. + * + * @ticket 53635 + * @covers ::download_url + */ + public function test_download_url_no_warning_for_url_without_path() { + $result = download_url( 'https://example.com' ); + + $this->assertIsString( $result ); + $this->assertNotEmpty( $result ); // File path will be generated, but will never be empty. + } + + /** + * Test that PHP 8.1 "passing null to non-nullable" deprecation notice + * is not thrown when the `$url` does not have a path component, + * and signature verification via a local file is requested. + * + * @ticket 53635 + * @covers ::download_url + */ + public function test_download_url_no_warning_for_url_without_path_with_signature_verification() { + add_filter( + 'wp_signature_hosts', + static function( $urls ) { + $urls[] = 'example.com'; + return $urls; + } + ); + $error = download_url( 'https://example.com', 300, true ); + + /* + * Note: This test is not testing the signature verification itself. + * There is no signature available for the domain used in the test, + * which is why an error is expected and that's fine. + * The point of the test is to verify that the call to `verify_file_signature()` + * is actually reached and that no PHP deprecation notice is thrown + * before this point. + */ + $this->assertWPError( $error ); + $this->assertSame( 'signature_verification_no_signature', $error->get_error_code() ); + } }