Commit 887863a283 for wordpress.org

commit 887863a28304e5e395e128bf6a72a94164f73854
Author: adamsilverstein <adamsilverstein@git.wordpress.org>
Date:   Thu Sep 17 17:25:46 2026 +0000

    Plugins: Require network plugin authority to Ajax-activate a network-only plugin.

    Props whyisjake, johnbillion, jeremyfelt, dmsnell, buffer1024.


    Built from https://develop.svn.wordpress.org/trunk@63657


    git-svn-id: http://core.svn.wordpress.org/trunk@62831 1a063a9b-81f0-0310-95a4-ce76da25c4cd

diff --git a/wp-admin/includes/ajax-actions.php b/wp-admin/includes/ajax-actions.php
index 747bcc75e5..126b6b75d4 100644
--- a/wp-admin/includes/ajax-actions.php
+++ b/wp-admin/includes/ajax-actions.php
@@ -4615,6 +4615,12 @@ function wp_ajax_activate_plugin() {
 		wp_send_json_error( $status );
 	}

+	// A network-only plugin is activated for the entire network.
+	if ( is_multisite() && is_network_only_plugin( $status['plugin'] ) && ! current_user_can( 'manage_network_plugins' ) ) {
+		$status['errorMessage'] = __( 'Sorry, you are not allowed to activate this plugin.' );
+		wp_send_json_error( $status );
+	}
+
 	if ( is_plugin_active( $status['plugin'] ) ) {
 		$status['errorMessage'] = sprintf(
 			/* translators: %s: Plugin name. */
diff --git a/wp-admin/includes/plugin.php b/wp-admin/includes/plugin.php
index 9969316ab8..53d933d07e 100644
--- a/wp-admin/includes/plugin.php
+++ b/wp-admin/includes/plugin.php
@@ -598,11 +598,17 @@ function is_plugin_active_for_network( $plugin ) {
  * Checks for "Site Wide Only: true" for backward compatibility.
  *
  * @since 3.0.0
+ * @since 7.1.1 The `$plugin` path is normalized with `plugin_basename()` and `trim()`,
+ *              matching how `activate_plugin()` resolves it.
  *
- * @param string $plugin Path to the plugin file relative to the plugins directory.
+ * @param string $plugin Path to the plugin file. Accepts a path relative to the plugins
+ *                       directory, or an absolute path, with or without surrounding whitespace.
  * @return bool True if plugin is network only, false otherwise.
  */
 function is_network_only_plugin( $plugin ) {
+	// Normalize the path the same way activate_plugin() does, so both agree on the file.
+	$plugin = plugin_basename( trim( $plugin ) );
+
 	$plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
 	if ( $plugin_data ) {
 		return $plugin_data['Network'];
diff --git a/wp-includes/version.php b/wp-includes/version.php
index 118762a146..30242fafe1 100644
--- a/wp-includes/version.php
+++ b/wp-includes/version.php
@@ -16,7 +16,7 @@
  *
  * @global string $wp_version
  */
-$wp_version = '7.2-alpha-63656';
+$wp_version = '7.2-alpha-63657';

 /**
  * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.