From 123fb622c8b430ed9582c7a11a83a1f1014332ee Mon Sep 17 00:00:00 2001 From: vuckro Date: Wed, 10 Jun 2026 12:37:31 +0200 Subject: [PATCH] fix(security): reject unsafe manifest plugin paths during site import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit move_and_activate_plugins() builds rename()/activate_plugin() targets from the plugin keys of the imported site manifest (untrusted JSON inside the import ZIP). Those keys were used without validation, so a crafted manifest could use a path-traversal key (e.g. "../../..") to relocate files or activate a PHP file outside WP_PLUGIN_DIR — a second-order path traversal / arbitrary-inclusion risk when importing a site package from an untrusted source (e.g. a template marketplace). Validate each plugin key before use: reject non-strings, absolute paths, NUL bytes and any parent-directory ("..") segment, and skip with a warning. Co-Authored-By: Claude Fable 5 --- .../commands/class-mu-migration-import.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/inc/site-exporter/mu-migration/includes/commands/class-mu-migration-import.php b/inc/site-exporter/mu-migration/includes/commands/class-mu-migration-import.php index 43f5b3ab5..d436a3dd8 100644 --- a/inc/site-exporter/mu-migration/includes/commands/class-mu-migration-import.php +++ b/inc/site-exporter/mu-migration/includes/commands/class-mu-migration-import.php @@ -599,6 +599,23 @@ private function move_and_activate_plugins($plugins_dir, $plugins, $blog_plugins $installed_plugins = WP_PLUGIN_DIR; $check_plugins = false !== $blog_plugins && false !== $network_plugins; foreach ( $plugins as $plugin_name => $plugin ) { + /* + * $plugin_name comes from the imported site manifest (untrusted + * JSON). A plugin basename is "/.php" or + * ".php" — never absolute and never containing a + * parent-directory segment. Reject anything else so a crafted + * manifest cannot drive rename()/activate_plugin() to a path + * outside WP_PLUGIN_DIR (path traversal / arbitrary inclusion). + */ + if ( ! is_string($plugin_name) || '' === $plugin_name + || false !== strpos($plugin_name, "\0") + || 0 === strpos($plugin_name, '/') + || 0 === strpos($plugin_name, '\\') + || preg_match('#(^|[\\\\/])\.\.([\\\\/]|$)#', $plugin_name) ) { + WP_CLI::warning(sprintf(__('Skipping plugin with unsafe path: %s', 'mu-migration'), (string) $plugin_name)); + continue; + } + $plugin_folder = dirname($plugin_name); $fullPluginPath = $plugins_dir . '/' . $plugin_folder;