diff --git a/qit b/qit index dcdd3399..70ea2fbe 100755 Binary files a/qit and b/qit differ diff --git a/src/src/Environment/EnvironmentDanglingCleanup.php b/src/src/Environment/EnvironmentDanglingCleanup.php index 9502e8e5..36367a50 100644 --- a/src/src/Environment/EnvironmentDanglingCleanup.php +++ b/src/src/Environment/EnvironmentDanglingCleanup.php @@ -76,7 +76,7 @@ public function cleanup_dangling(): void { } if ( ! $this->header_printed ) { - $this->output->writeln( 'Cleaning up dangling temporary environments...' ); + $this->output->writeln( 'Removing dangling test environments...' ); $this->header_printed = true; } @@ -117,37 +117,64 @@ public function cleanup_dangling(): void { return; } - // Skip asking the user permission to delete in this directory if they answer with an "A". - $always_delete_from_this_directory = $this->cache->get( 'always_delete_from_this_directory' ); - $parent_dir = $this->get_parent_dir_to_delete(); + $expected_directories = [ + 'bin', + 'cache', + 'html', + 'mu-plugins', + ]; - if ( $always_delete_from_this_directory !== $parent_dir && ! is_ci() ) { - $this->output->writeln( "Found dangling temporary environments in directory: $parent_dir" ); + $allowed_extensions = [ + 'php', + 'js', + 'json', + ]; - $question = new Question( - "Do you want to clean up these environments? [Y/n/A]\nA: Yes, and always clean up in this directory in the future. (Recommended)\n", - 'n' // Default to 'n'. - ); + foreach ( $this->dangling_directories as $directory ) { + $unexpected_contents = null; + + /* + * We are being extra zealous here. + * We already have good security boundaries for deleting files, but since + * this is a recursive directory deletion, we validate the contents of the directory to be deleted. + */ + /** @var \DirectoryIterator $file_info */ + foreach ( new \DirectoryIterator( $directory ) as $file_info ) { + if ( $file_info->isDot() || $file_info->isLink() ) { + continue; + } - $answer = strtolower( ( new QuestionHelper() )->ask( $this->input, $this->output, $question ) ); + if ( $file_info->isDir() ) { + if ( ! in_array( $file_info->getFilename(), $expected_directories, true ) ) { + $this->debug_output( "Found non-expected directory: {$file_info->getPathname()}" ); + $unexpected_contents = $file_info; + break; + } + } elseif ( $file_info->isFile() ) { + $extension = pathinfo( $file_info->getFilename(), PATHINFO_EXTENSION ); + if ( ! in_array( $extension, $allowed_extensions, true ) ) { + $this->debug_output( "Found non-expected file: {$file_info->getPathname()}" ); + $unexpected_contents = $file_info; + break; + } + } + } - switch ( $answer ) { - case 'y': - // no-op. Proceed with the action. - break; - case 'a': - $this->cache->set( 'always_delete_from_this_directory', $parent_dir, YEAR_IN_SECONDS ); - break; - case 'n': - default: - $this->output->writeln( 'Please delete the dangling environments in that directory manually.' ); + if ( ! is_null( $unexpected_contents ) ) { + $this->output->writeln( 'Failed to cleanup dangling directory' ); + $this->output->writeln( sprintf( 'Unexpected file: %s', $unexpected_contents->getFilename() ) ); - return; + // Ask the user if we can delete it. + $question = new Question( 'Do you want to delete this directory? [y/N] ', 'n' ); + $answer = ( new QuestionHelper() )->ask( $this->input, $this->output, $question ); + if ( strtolower( $answer ) !== 'y' ) { + $this->output->writeln( 'Skipping directory deletion.' ); + continue; + } } - } - foreach ( $this->dangling_directories as $directory ) { - $this->output->writeln( "Removing dangling directory: {$directory}" ); + $this->debug_output( "Removing dangling directory: {$directory}" ); + SafeRemove::delete_dir( $directory, Environment::get_temp_envs_dir() ); } }