Answer only for our own plugin, not every plugin on the host
🔴 Live-shop bug, found by the subscriptions session with a screenshot. `update_plugins_{$hostname}` is keyed on the HOSTNAME, not the repository. Every plugin updating from gitea.nolimit.dk shares the single hook `update_plugins_gitea.nolimit.dk`, and WordPress runs every callback on it for every one of those plugins: $update = apply_filters( "update_plugins_{$hostname}", false, $plugin_data, $plugin_file, $locales ); This updater registered with `10, 2` — so it never received $plugin_file — and returned its own release unconditionally. Result on a live shop: a subscriptions plugin was shown this plugin's 1.4.4 and told it was an update for itself. Load order decides which callback wins, so the direction it breaks in is arbitrary: a rename, a different folder name, a plugins_loaded priority change, and it flips. If it flips this way round, a plugin with auto-updates enabled installs somebody else's zip over itself, unattended, on a shop taking money. Both halves are the fix. `10, 3` so the argument arrives, and a check that the file WordPress is asking about is ours. With the count left at 2 the guard would compare against an empty string and silently do nothing — the same shape of bug it exists to fix. tests/smoke.php now calls offer_update() with a foreign plugin file and requires false back. The site test installs exactly one plugin from this host, so the collision cannot arise there on its own: that assertion is the defence. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
416c74ac5b
commit
b5b4bc2e03
+22
-2
@@ -305,7 +305,7 @@ set_transient(
|
||||
60
|
||||
);
|
||||
|
||||
$offered = wc_edm()->updater->offer_update( false, get_plugin_data( WC_EDM_PLUGIN_FILE, false, false ) );
|
||||
$offered = wc_edm()->updater->offer_update( false, get_plugin_data( WC_EDM_PLUGIN_FILE, false, false ), plugin_basename( WC_EDM_PLUGIN_FILE ) );
|
||||
wc_edm_is( 'a newer release is offered as an update', '99.0.0', is_array( $offered ) ? $offered['version'] : null );
|
||||
wc_edm_is(
|
||||
'and it carries the folder name WordPress will unpack into',
|
||||
@@ -344,9 +344,29 @@ set_transient(
|
||||
),
|
||||
60
|
||||
);
|
||||
$offered = wc_edm()->updater->offer_update( false, get_plugin_data( WC_EDM_PLUGIN_FILE, false, false ) );
|
||||
$offered = wc_edm()->updater->offer_update( false, get_plugin_data( WC_EDM_PLUGIN_FILE, false, false ), plugin_basename( WC_EDM_PLUGIN_FILE ) );
|
||||
wc_edm_is( 'the current version is answered too, not passed over', WC_EDM_VERSION, is_array( $offered ) ? $offered['version'] : null );
|
||||
|
||||
// 🔴 The filter name is built from the HOSTNAME, not the repository. Every
|
||||
// plugin updating from gitea.nolimit.dk shares the hook
|
||||
// update_plugins_gitea.nolimit.dk, and WordPress runs every callback on it for
|
||||
// every one of those plugins. Answering unconditionally means offering the
|
||||
// neighbour your own release — which happened on a live shop: a subscriptions
|
||||
// plugin was shown this one's 1.4.4 and told it was an update for itself. Load
|
||||
// order decides who wins, so the direction it breaks in is arbitrary.
|
||||
//
|
||||
// The site test installs exactly one plugin from this host, so the collision
|
||||
// can never occur here. This assertion is the whole defence.
|
||||
wc_edm_ok(
|
||||
'another plugin on the same host is not offered our release',
|
||||
false === wc_edm()->updater->offer_update(
|
||||
false,
|
||||
get_plugin_data( WC_EDM_PLUGIN_FILE, false, false ),
|
||||
'kw-subscriptions/kw-subscriptions.php'
|
||||
),
|
||||
'we answered for a plugin that is not ours'
|
||||
);
|
||||
|
||||
// The "View version details" link next to the update notice. WordPress builds
|
||||
// it as plugin-install.php?tab=plugin-information&plugin=<slug>, which calls
|
||||
// plugins_api() — and plugins_api() asks wordpress.org, where this plugin does
|
||||
|
||||
Reference in New Issue
Block a user