diff --git c/src/wp-includes/functions.php w/src/wp-includes/functions.php index 8ae58299d6..0dec6df4f5 100644 --- c/src/wp-includes/functions.php +++ w/src/wp-includes/functions.php @@ -5553,8 +5553,11 @@ function _device_can_upload() { * @return bool True if the path is a stream URL. */ function wp_is_stream( $path ) { - $wrappers = stream_get_wrappers(); - $wrappers = array_map( 'preg_quote', $wrappers ); + $wrappers = stream_get_wrappers(); + foreach ( $wrappers as &$wrapper ) { + $wrapper = preg_quote( $wrapper, '!' ); + } + $wrappers_re = '(' . join( '|', $wrappers ) . ')'; return preg_match( "!^$wrappers_re://!", $path ) === 1; diff --git c/tests/phpunit/tests/functions.php w/tests/phpunit/tests/functions.php index d9e3d5fbb8..82d689984b 100644 --- c/tests/phpunit/tests/functions.php +++ w/tests/phpunit/tests/functions.php @@ -1451,4 +1451,38 @@ class Tests_Functions extends WP_UnitTestCase { ); } + + /** + * Test wp_is_stream validation. + * + * @ticket 44533 + * @dataProvider data_test_wp_is_stream + * + * @param $path + * @param $expected + */ + public function test_wp_is_stream( $path, $expected ) { + $this->assertSame( $expected, wp_is_stream( $path ) ); + } + + /** + * Data provider for stream validation. + * + * @return array + */ + public function data_test_wp_is_stream() { + return array( + // Legitimate stream examples. + array( 'https://example.com', true ), + array( 'ftp://example.com', true ), + array( 'file:///path/to/some/file', true ), + array( 'php://some/php/file.php', true ), + + // Non-stream examples. + array( 'fakestream://foo/bar/baz', false ), + array( '../../some/relative/path', false ), + array( 'some/other/relative/path', false ), + array( '/leading/relative/path', false ), + ); + } }