diff --git a/includes/functions.php b/includes/functions.php index 02c97785edda8df180ed4e290b915aad8968c5a6..7a3fce4a8b2c1b989a486b8bfe3f59d451dad2e8 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -3,13 +3,13 @@ namespace Automattic\WP\WP_CLI_Cron_Control_Offload; /** - * Check if subcommand is allowed + * Check if command is allowed * - * @param string $subcommand + * @param string $command * @return bool */ -function is_subcommand_allowed( $subcommand ) { - return in_array( $subcommand, get_command_whitelist(), true ) && ! in_array( $subcommand, get_command_blacklist(), true ); +function is_command_allowed( $command ) { + return in_array( $command, get_command_whitelist(), true ) && ! in_array( $command, get_command_blacklist(), true ); } /** @@ -23,6 +23,8 @@ function get_command_whitelist() { 'cache', 'cap', 'comment', + 'cron-control', + 'cron-control-fixers', 'media', 'menu', 'network', @@ -80,7 +82,7 @@ function schedule_cli_command( $args ) { return $event_args; } - $scheduled = wp_schedule_single_event( strtotime( '+30 seconds' ), ACTION, $event_args ); + $scheduled = wp_schedule_single_event( strtotime( '+30 seconds' ), ACTION, array( 'command' => $event_args ) ); return false !== $scheduled; } @@ -88,21 +90,33 @@ function schedule_cli_command( $args ) { /** * Validate WP-CLI command to be scheduled * - * @param array $args + * @param string $args * @return array|\WP_Error */ function validate_args( $args ) { - $validated_args = array(); + // Strip `wp` if included + if ( 0 === stripos( $args, 'wp' ) ) { + $args = trim( substr( $args, 2 ) ); + } + + // Block disallowed commands + $command = explode( ' ', $args ); + $command = array_shift( $command ); + if ( ! is_command_allowed( $command ) ) { + return new \WP_Error( "$command not allowed" ); + } - // TODO: validate - // TODO: strip leading "wp" - // TODO: check first positional argument against `is_command_allowed()` + // Don't worry about the user WP-CLI runs as + if ( false === stripos( $args, '--allow-root' ) ) { + $args .= ' --allow-root'; + } - $validated_args['command'] = $args; + // TODO: validate further - if ( empty( $validated_args ) ) { - return new \WP_Error( 'Arguments could not be parsed for validation.' ); + // Nothing to run + if ( empty( $args ) ) { + return new \WP_Error( 'Invalid command provided' ); } - return $validated_args; + return $args; } diff --git a/includes/run.php b/includes/run.php index 95d6ca4ad3d1c19b67fffa2d2574edb1c4226e52..a4af0be7e424d4da9c8b04e19c669b6b8451a884 100644 --- a/includes/run.php +++ b/includes/run.php @@ -5,17 +5,40 @@ namespace Automattic\WP\WP_CLI_Cron_Control_Offload; /** * Intended for non-interactive use, so all output ends up in the error log * - * @param array $args + * @param string $command * @return null */ -function run_event( $args ) { +function run_event( $command ) { if ( ! defined( 'WP_CLI' ) || ! \WP_CLI ) { // TODO: reschedule at least once or twice - trigger_error( 'Attempted to run event without WP-CLI loaded. ' . var_export( $args, true ), E_USER_WARNING ); - return false; + trigger_error( 'Attempted to run event without WP-CLI loaded. (' . var_export( $command, true ) . ')', E_USER_WARNING ); + return; } - // TODO: run event, sending output to error log - trigger_error( var_export( $args, true ), E_USER_NOTICE ); + if ( ! validate_args( $command ) ) { + trigger_error( 'Attempted to run blocked WP-CLI command. (' . var_export( $command, true ) . ')', E_USER_WARNING ); + return; + } + + // TODO: time event execution + + $output = \WP_CLI::runcommand( $command, array( + 'return' => 'all', + ) ); + + // Command failed + if ( ! is_object( $output ) || is_wp_error( $output ) ) { + trigger_error( 'WP-CLI command failed. (' . var_export( $command, true ) . ')', E_USER_WARNING ); + trigger_error( var_export( $output, true ), E_USER_WARNING ); + return; + } + + // On success, reformat response for logging + $output->command = $command; + + $output = var_export( $output, true ); + $output = ACTION . ":\n{$output}"; + + error_log( $output ); } add_action( ACTION, __NAMESPACE__ . '\run_event' );