PATH:
home
/
thebhoeo
/
.trash
/
backwpup
/
inc
/
Utils
<?php namespace BackWPup\Utils; use BackWPup; class BackWPupHelpers { /** * Cache for resolved component paths. * * @var array */ private static $component_path_cache = []; /** * Render or return a component's HTML. * * @param string $component The name of the component file to include. * @param array $args Variables to pass to the component. * @param bool $return Whether to return the HTML instead of echoing it. * * @return string|null HTML content of the component if `$return` is true; otherwise, null. */ public static function component( string $component, array $args = [], bool $return = false ) { // @phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.returnFound $sanitized_component = sanitize_text_field( $component ); if ( ! isset( self::$component_path_cache[ 'c:' . $sanitized_component ] ) ) { // Explicitly reject traversal-like inputs (e.g., "..", "../", "..\"). if ( self::contains_path_traversal( $sanitized_component ) ) { error_log( "Component name contains disallowed path traversal sequence: {$sanitized_component}" ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log return null; } $filename = $sanitized_component . '.php'; $path = realpath( untrailingslashit( BackWPup::get_plugin_data( 'plugindir' ) ) . '/components/' . $filename ); $allowed_dir = realpath( untrailingslashit( BackWPup::get_plugin_data( 'plugindir' ) ) . '/components/' ); if ( false === $path || false === $allowed_dir ) { error_log( "Component file not found or outside allowed directory: {$filename}" ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log return null; } // Ensure the resolved path is within the allowed components directory, using a directory-boundary-safe check. $allowed_dir = rtrim( $allowed_dir, DIRECTORY_SEPARATOR ); if ( 0 !== strpos( $path, $allowed_dir . DIRECTORY_SEPARATOR ) ) { error_log( "Component file not found or outside allowed directory: {$filename}" ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log return null; } if ( ! file_exists( $path ) ) { error_log( "Component file not found: {$path}" ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log return null; } self::$component_path_cache[ 'c:' . $sanitized_component ] = $path; } else { $path = self::$component_path_cache[ 'c:' . $sanitized_component ]; } // Extract the arguments for the component context. // Avoid overwriting built-in variables. extract( $args, EXTR_SKIP ); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract // Start output buffering. ob_start(); include $path; // Execute the PHP file in the local context of extracted arguments. $output = ob_get_clean(); if ( $return ) { return $output; // Return the output content for further processing. } // Directly echo the content if $return is false (default behavior). echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped return; //phpcs:ignore Squiz.PHP.NonExecutableCode.ReturnNotRequired } /** * Render or return a component's children HTML. * * @param string $component The name of the component file to include. * @param bool $return Whether to return the HTML instead of echoing it. * @param array $args Variables to pass to the children. * * @return string|null HTML content of the component if `$return` is true; otherwise, null. */ public static function children( string $component, bool $return = false, array $args = [] ) { // @phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.returnFound $sanitized_component = sanitize_text_field( $component ); if ( ! isset( self::$component_path_cache[ 'h:' . $sanitized_component ] ) ) { // Explicitly reject traversal-like inputs (e.g., "..", "../", "..\"). if ( self::contains_path_traversal( $sanitized_component ) ) { error_log( "Component name contains disallowed path traversal sequence: {$sanitized_component}" ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log return null; } $filename = $sanitized_component . '.php'; $path = realpath( untrailingslashit( BackWPup::get_plugin_data( 'plugindir' ) ) . '/parts/' . $filename ); $allowed_dir = realpath( untrailingslashit( BackWPup::get_plugin_data( 'plugindir' ) ) . '/parts/' ); // If the file doesn't exist and it's a pro version of the plugin, check the pro directory for the component. if ( ! file_exists( $path ) && BackWPup::is_pro() ) { $path = realpath( untrailingslashit( BackWPup::get_plugin_data( 'plugindir' ) ) . '/pro/parts/' . $filename ); $allowed_dir = realpath( untrailingslashit( BackWPup::get_plugin_data( 'plugindir' ) ) . '/pro/parts/' ); } if ( false === $path || false === $allowed_dir ) { error_log( "Component file not found or outside allowed directory: {$filename}" ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log return null; } // Ensure the resolved path is within the allowed components directory, using a directory-boundary-safe check. $allowed_dir = rtrim( $allowed_dir, DIRECTORY_SEPARATOR ); if ( 0 !== strpos( $path, $allowed_dir . DIRECTORY_SEPARATOR ) ) { error_log( "Component file not found or outside allowed directory: {$filename}" ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log return null; } if ( ! file_exists( $path ) ) { return; } self::$component_path_cache[ 'h:' . $sanitized_component ] = $path; } else { $path = self::$component_path_cache[ 'h:' . $sanitized_component ]; } // Extract the arguments for the component context. // Avoid overwriting built-in variables. extract( $args, EXTR_SKIP ); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract // Start output buffering. ob_start(); include $path; // Execute the PHP file in the local context of extracted arguments. $output = ob_get_clean(); if ( $return ) { return $output; // Return the output content for further processing. } // Directly echo the content if $return is false (default behavior). echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped return; //phpcs:ignore Squiz.PHP.NonExecutableCode.ReturnNotRequired } /** * Combines class names into a single string. * * @param mixed ...$classes Variable list of class names which can be strings or arrays. * @return string A space-separated string of class names. */ public static function clsx( ...$classes ) { return implode( ' ', array_filter( $classes ) ); } /** * Process backup items. * * @param array $items The list of backup items. * @param array $job_data The job data to merge with each item. * @param string $dest The destination of the backup. * @param int $page The current page for pagination. * * @return array The processed items. */ public static function process_backup_items( array $items, array $job_data, string $dest, int $page ): array { array_walk( $items, function ( &$item ) use ( $job_data, $dest, $page ) { $item = array_merge( $item, $job_data, [ 'stored_on' => $dest ] ); // Parse the filename to get the type of backup. $filename = pathinfo( $item['filename'] )['filename']; if ( stripos( $item['filename'], '.tar.gz' ) === strlen( $item['filename'] ) - 7 ) { $filename = substr( $item['filename'], 0, -7 ); } elseif ( stripos( $item['filename'], '.tar.bz2' ) === strlen( $item['filename'] ) - 8 ) { $filename = substr( $item['filename'], 0, -8 ); } $filename_parts = explode( '_', $filename ); if ( count( $filename_parts ) > 1 ) { $item['data'] = (array) explode( '-', end( $filename_parts ) ); } $local_file = untrailingslashit( BackWPup::get_plugin_data( 'TEMP' ) ) . "/{$item['filename']}"; $downloadurl = wp_nonce_url( $item['downloadurl'], 'backwpup_action_nonce' ); $downloadhref = '#TB_inline?height=300&inlineId=tb_download_file&width=640&height=412'; $downloadtrigger = 'download-backup'; // Add the download URL and dataset. $item['dataset-download'] = [ 'data-jobid' => $job_data['id'], 'data-destination' => $dest, 'data-file' => $item['file'], 'data-local-file' => $local_file, 'data-nonce' => wp_create_nonce( 'backwpup_action_nonce' ), 'data-url' => $downloadurl, 'data-href' => $downloadhref, ]; $item['download-trigger'] = $downloadtrigger; // If the user can restore, add the restore URL. if ( current_user_can( 'backwpup_restore' ) && ! empty( $item['restoreurl'] ) ) { $item['dataset-restore'] = [ 'label' => __( 'Restore Backup', 'backwpup' ), 'data-url' => wp_nonce_url( add_query_arg( [ 'step' => 1, 'trigger_download' => 1, ], $item['restoreurl'] ), 'restore-backup_' . $job_data['id'] ), ]; } elseif ( current_user_can( 'backwpup_restore' ) ) { $item['dataset-restore'] = [ 'label' => __( 'Restore Backup', 'backwpup' ), 'data-url' => network_admin_url( 'admin.php?page=backwpuprestore' ), ]; } // If the user can delete, add the delete URL. if ( current_user_can( 'backwpup_backups_delete' ) ) { $item['dataset-delete'] = [ 'data-url' => wp_nonce_url( add_query_arg( [ 'page' => 'backwpupbackups', 'action' => 'delete', 'jobdest-top' => $job_data['id'] . '_' . $dest, 'backupfiles[]' => esc_attr( $item['file'] ), 'paged' => $page, ], network_admin_url( 'admin.php' ) ), 'bulk-backups' ), ]; } } ); return $items; } /*** * Checks if the input string contains path traversal sequences like "..", "../", or "..\". * * @param string $input The input string to check. * @return bool True if the input contains path traversal sequences, false otherwise. */ private static function contains_path_traversal( string $input ): bool { return preg_match( '#(^|[\\\\/])\.\.([\\\\/]|$)#', $input ) === 1; } }
[-] BackWPupHelpers.php
[edit]
[+]
..