diff --git a/src/helper/Site_Backup_Restore.php b/src/helper/Site_Backup_Restore.php index f473f2be..a287b639 100644 --- a/src/helper/Site_Backup_Restore.php +++ b/src/helper/Site_Backup_Restore.php @@ -827,8 +827,22 @@ private function restore_wp( $backup_dir ) { $meta_data = json_decode( file_get_contents( $backup_dir . '/meta.json' ), true ); $wp_version = $meta_data['wordpressVersion']; + // $wp_version is read from the backup's meta.json and interpolated into the + // shell command below (which runs through `bash -c` in the container), so a + // crafted value could otherwise inject shell tokens. A WordPress version + // only ever contains [0-9A-Za-z.-]; strip anything else so no shell + // metacharacter can survive either shell layer. + $wp_version = preg_replace( '/[^0-9A-Za-z.\-]/', '', (string) $wp_version ); + + // wp core download extracts the WordPress archive in PHP, which needs more + // than a typical site's 128M memory_limit and OOMs on low-RAM hosts. Run it + // under a higher limit via `php -d memory_limit=256M $(which wp)`, matching + // the site-creation path in site-type-wp. The command runs through `bash -c` + // in the container, so the `$` in `$(which wp)` is escaped here to defer the + // substitution to the container's shell (EE's `wp` is the phar, invoked + // directly, so the WP_CLI_PHP_ARGS env var would not apply). $args = [ 'shell', $this->site_data['site_url'] ]; - $assoc_args = [ 'command' => sprintf( 'wp core download --force --version=%s', $wp_version ) ]; + $assoc_args = [ 'command' => sprintf( "php -d memory_limit=256M \\$(which wp) core download --force --version=%s", $wp_version ) ]; $options = [ 'skip-tty' => true ]; EE::run_command( $args, $assoc_args, $options );