From 037d2d23715356adf835133a35051b543d3d40d0 Mon Sep 17 00:00:00 2001 From: Riddhesh Sanghvi Date: Mon, 12 Oct 2026 17:01:48 +0530 Subject: [PATCH] fix(runner): refuse remote targets instead of running the command locally An alias with an `ssh:` or `http:` key, `--ssh=`, `--http=` and an `ssh:`/`http:` key in the config were accepted, but nothing ever dispatched the command to the remote target, so a command such as `ee @remote site delete foo --yes` ran on the local server. EasyEngine only manages the server it runs on (it needs root and local Docker, and runs its migrations before dispatching a command), so refuse these targets with a clear error before anything else runs. An alias group or `@all` is refused when any member has a remote target; aliases without one keep working as before. `config` and `help` stay usable with an `ssh:`/`http:` config key, so that `ee config unset ssh` can remove it. --- php/EE/Runner.php | 73 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/php/EE/Runner.php b/php/EE/Runner.php index f31ca9a89..00cf11a57 100644 --- a/php/EE/Runner.php +++ b/php/EE/Runner.php @@ -765,12 +765,85 @@ private function set_alias( $alias ) { } } + /** + * EE only manages the host it runs on: the WP-CLI ssh/http targets were never wired up, so a command given one + * would silently run here. + * + * @param array $args Command line arguments without the script name. + */ + private function refuse_remote_targets( $args ) { + $hint = 'EasyEngine only manages the server it runs on; run ee on the target server instead.'; + + if ( $this->alias ) { + $keys = $this->get_remote_alias_keys( $this->alias ); + if ( $keys ) { + EE::error( sprintf( "The alias '%s' has a remote target (%s), which is not supported. %s", $this->alias, implode( ', ', $keys ), $hint ) ); + } + } + + // Raw argv: EE_STRICT_ARGS_MODE drops unknown global flags before they reach assoc_args. + list( , $assoc_args ) = Configurator::extract_assoc( $args ); + foreach ( $assoc_args as $assoc_arg ) { + if ( in_array( $assoc_arg[0], array( 'ssh', 'http' ), true ) ) { + EE::error( "The --{$assoc_arg[0]} parameter is not supported. {$hint}" ); + } + } + + // Keep `ee config unset ` and help usable. + if ( empty( $this->arguments ) || in_array( $this->arguments[0], array( 'config', 'help' ), true ) ) { + return; + } + + foreach ( array( 'ssh', 'http' ) as $key ) { + if ( ! empty( $this->extra_config[ $key ] ) ) { + $files = implode( ', ', array_filter( array( $this->global_config_path, $this->project_config_path ) ) ); + EE::error( "The '{$key}' config key is not supported. {$hint} Remove it from the config ({$files}, or a file they inherit)." ); + } + } + } + + /** + * Remote target keys (ssh, http) of an alias, or of any member of a group or of @all. + * + * @param string $alias Alias name. + * @param array $seen Aliases already visited, so groups listing each other don't recurse forever. + * + * @return string[] + */ + private function get_remote_alias_keys( $alias, $seen = array() ) { + if ( isset( $seen[ $alias ] ) || ! isset( $this->aliases[ $alias ] ) ) { + return array(); + } + $seen[ $alias ] = true; + $config = $this->aliases[ $alias ]; + + if ( '@all' === $alias && is_string( $config ) ) { + $config = array_values( array_diff( array_keys( $this->aliases ), array( '@all' ) ) ); + } + if ( ! is_array( $config ) ) { + return array(); + } + if ( isset( $config[0] ) ) { + $keys = array(); + foreach ( $config as $member ) { + $keys = array_merge( $keys, $this->get_remote_alias_keys( $member, $seen ) ); + } + + return array_values( array_unique( $keys ) ); + } + + return array_values( array_intersect( array( 'ssh', 'http' ), array_keys( $config ) ) ); + } + public function start() { EE::debug( $this->_global_config_path_debug, 'bootstrap' ); EE::debug( $this->_project_config_path_debug, 'bootstrap' ); EE::debug( 'argv: ' . implode( ' ', $GLOBALS['argv'] ), 'bootstrap' ); + // Before init_ee(): migrations must not run for a command we refuse. + $this->refuse_remote_targets( array_slice( $GLOBALS['argv'], 1 ) ); + $this->init_ee(); // Enable PHP error reporting to stderr if testing.