From a6d20397b5b1d485c6d64eed05d4346e45f29b21 Mon Sep 17 00:00:00 2001 From: Riddhesh Sanghvi Date: Fri, 12 Jun 2026 16:06:30 +0530 Subject: [PATCH 1/3] fix(restore): raise PHP memory_limit for wp core download to prevent OOM During restore, 'wp core download' downloads and extracts the WordPress archive in PHP. It ran with the site container's default 128M memory_limit, so on low-RAM hosts the extraction exhausts memory and the restore is OOM-killed. Set WP_CLI_PHP_ARGS='-d memory_limit=512M' for that command so WP-CLI applies the higher limit to the PHP process it spawns. The restore runs the command via 'ee shell --command', which executes through bash in the php container (Shell_Command.php), so the env-var prefix is honoured. This mirrors the site-creation path in site-type-wp, which already runs core download with 'php -d memory_limit=256M'. Other restore wp-cli calls (wp config set, wp cache flush) are trivial and left unchanged. --- src/helper/Site_Backup_Restore.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/helper/Site_Backup_Restore.php b/src/helper/Site_Backup_Restore.php index f473f2be..7e4f33b0 100644 --- a/src/helper/Site_Backup_Restore.php +++ b/src/helper/Site_Backup_Restore.php @@ -827,8 +827,14 @@ private function restore_wp( $backup_dir ) { $meta_data = json_decode( file_get_contents( $backup_dir . '/meta.json' ), true ); $wp_version = $meta_data['wordpressVersion']; + // 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. Raise + // the limit for this command (site creation does the same with `php -d + // memory_limit` in site-type-wp) via WP_CLI_PHP_ARGS, which WP-CLI applies + // to the PHP process it spawns. The shell command runs through bash, so the + // env-var prefix is honoured. $args = [ 'shell', $this->site_data['site_url'] ]; - $assoc_args = [ 'command' => sprintf( 'wp core download --force --version=%s', $wp_version ) ]; + $assoc_args = [ 'command' => sprintf( "WP_CLI_PHP_ARGS='-d memory_limit=512M' wp core download --force --version=%s", $wp_version ) ]; $options = [ 'skip-tty' => true ]; EE::run_command( $args, $assoc_args, $options ); From b806b83e1f1568a7237017799e9d28316c8221e5 Mon Sep 17 00:00:00 2001 From: Riddhesh Sanghvi Date: Fri, 12 Jun 2026 16:12:52 +0530 Subject: [PATCH 2/3] fix(restore): use php -d memory_limit (not WP_CLI_PHP_ARGS) for wp core download The previous commit raised the limit via WP_CLI_PHP_ARGS, but that env var is only read by WP-CLI's bash launcher. EE's 'wp' is the phar invoked directly (which is why site creation runs 'php -d memory_limit=256M $(which wp) core download'), so WP_CLI_PHP_ARGS would be ignored and the limit never applied. EE has no WP_CLI_PHP_ARGS usage anywhere. Match the proven site-type-wp pattern instead: 'php -d memory_limit=256M $(which wp) core download ...'. The restore runs this via ee shell --command, which (like site creation) goes through 'bash -c ""' in the php container, so the '$' is escaped to defer the which-wp substitution to the container shell. Value is 256M to match the site-creation path (proven sufficient for the same operation on the same hardware). --- src/helper/Site_Backup_Restore.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/helper/Site_Backup_Restore.php b/src/helper/Site_Backup_Restore.php index 7e4f33b0..2cefce4b 100644 --- a/src/helper/Site_Backup_Restore.php +++ b/src/helper/Site_Backup_Restore.php @@ -828,13 +828,14 @@ private function restore_wp( $backup_dir ) { $wp_version = $meta_data['wordpressVersion']; // 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. Raise - // the limit for this command (site creation does the same with `php -d - // memory_limit` in site-type-wp) via WP_CLI_PHP_ARGS, which WP-CLI applies - // to the PHP process it spawns. The shell command runs through bash, so the - // env-var prefix is honoured. + // 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_CLI_PHP_ARGS='-d memory_limit=512M' 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 ); From 11de8c4b29f21a38857ec94f3d6a1ee9201c323d Mon Sep 17 00:00:00 2001 From: Riddhesh Sanghvi Date: Fri, 12 Jun 2026 16:27:50 +0530 Subject: [PATCH 3/3] fix(restore): sanitize wp_version from backup metadata before shell use Addresses a Copilot review finding: wordpressVersion is read from the backup's meta.json and interpolated into the wp core download shell command, which runs through 'bash -c' in the container. A backup carrying a crafted wordpressVersion (e.g. one pulled from remote storage) could therefore inject shell tokens during 'ee site restore'. Whitelist-sanitize the value to [0-9A-Za-z.-] -- the full character set of a legitimate WordPress version -- so no shell metacharacter survives. A whitelist is used rather than escapeshellarg() because the command crosses two shell layers (host sh, then container bash via 'bash -c "..."'), where single-layer escaping is unreliable. A malformed version reduces to an invalid version string, which fails wp core download safely. --- src/helper/Site_Backup_Restore.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/helper/Site_Backup_Restore.php b/src/helper/Site_Backup_Restore.php index 2cefce4b..a287b639 100644 --- a/src/helper/Site_Backup_Restore.php +++ b/src/helper/Site_Backup_Restore.php @@ -827,6 +827,13 @@ 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