diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index aa507bb004..81409e9382 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -8221,21 +8221,50 @@ function recurse_dirsize( $directory, $exclude = null, $max_execution_time = nul * Removes the current directory and all parent directories from the `dirsize_cache` transient. * * @since 5.6.0 + * @since 5.9.0 Added input validation with a notice for invalid input. * * @param string $path Full path of a directory or file. */ function clean_dirsize_cache( $path ) { + if ( ! is_string( $path ) || empty( $path ) ) { + trigger_error( + sprintf( + /* translators: 1: Function name, 2: A variable type, like "boolean" or "integer". */ + __( '%1$s only accepts a non-empty path string, received %2$s.' ), + 'clean_dirsize_cache()', + '' . gettype( $path ) . '' + ) + ); + return; + } + $directory_cache = get_transient( 'dirsize_cache' ); if ( empty( $directory_cache ) ) { return; } - $path = untrailingslashit( $path ); + if ( + strpos( $path, '/' ) === false && + strpos( $path, '\\' ) === false + ) { + unset( $directory_cache[ $path ] ); + set_transient( 'dirsize_cache', $directory_cache ); + return; + } + + $last_path = null; + $path = untrailingslashit( $path ); unset( $directory_cache[ $path ] ); - while ( DIRECTORY_SEPARATOR !== $path && '.' !== $path && '..' !== $path ) { - $path = dirname( $path ); + while ( + $last_path !== $path && + DIRECTORY_SEPARATOR !== $path && + '.' !== $path && + '..' !== $path + ) { + $last_path = $path; + $path = dirname( $path ); unset( $directory_cache[ $path ] ); } diff --git a/tests/phpunit/tests/functions/cleanDirsizeCache.php b/tests/phpunit/tests/functions/cleanDirsizeCache.php new file mode 100644 index 0000000000..6ae7d6d2dc --- /dev/null +++ b/tests/phpunit/tests/functions/cleanDirsizeCache.php @@ -0,0 +1 @@ +expectNotice(); $this->expectNoticeMessage( $expected_message ); clean_dirsize_cache( $path ); } /** * Data provider. * * @return array */ public function data_clean_dirsize_cache_with_invalid_inputs() { return array( 'null' => array( 'path' => null, 'expected_message' => 'clean_dirsize_cache() only accepts a non-empty path string, received NULL.', ), 'bool false' => array( 'path' => false, 'expected_message' => 'clean_dirsize_cache() only accepts a non-empty path string, received boolean.', ), 'empty string' => array( 'path' => '', 'expected_message' => 'clean_dirsize_cache() only accepts a non-empty path string, received string.', ), 'array' => array( 'path' => array( '.', './second/path/' ), 'expected_message' => 'clean_dirsize_cache() only accepts a non-empty path string, received array.', ), ); } /** * Test the handling of a non-path text string passed as the $path parameter. * * @ticket 52241 * * @dataProvider data_clean_dirsize_cache_with_non_path_string * * @param string $path Path input to use in the test. * @param int $expected_count Expected number of paths in the cache after cleaning. */ public function test_clean_dirsize_cache_with_non_path_string( $path, $expected_count ) { // Set the dirsize cache to our mock. set_transient( 'dirsize_cache', $this->mock_dirsize_cache_with_non_path_string() ); clean_dirsize_cache( $path ); $cache = get_transient( 'dirsize_cache' ); $this->assertIsArray( $cache ); $this->assertCount( $expected_count, $cache ); } /** * Data provider. * * @return array */ public function data_clean_dirsize_cache_with_non_path_string() { return array( 'single dot' => array( 'path' => '.', 'expected_count' => 1, ), 'non-path' => array( 'path' => 'string', 'expected_count' => 1, ), 'non-existant string, but non-path' => array( 'path' => 'doesnotexist', 'expected_count' => 2, ), ); } private function mock_dirsize_cache_with_non_path_string() { return array( '.' => array( 'size' => 50 ), 'string' => array( 'size' => 42 ), ); } } \ No newline at end of file