Skip to content

Commit 3c7ce8a

Browse files
committed
Sanitize 'update_plugins' and 'update_themes' transients.
WordPress stores plugin and theme update information in the 'update_plugins' and 'update_themes' transients respectively. If the `wp_options` table uses a legacy character set (e.g., `utf8mb3`), then it is possible that plugin or theme update information may contain characters which cannot be represented in the database. If that happens, then the transients cannot be saved to the database, which will prevent updates from occurring. To avoid this, we need to call `$wpdb->strip_invalid_text_for_column()` on the transients before they are serialized and stored in the database.
1 parent 3f5fdc5 commit 3c7ce8a

2 files changed

Lines changed: 84 additions & 0 deletions

File tree

‎src/wp-includes/formatting.php‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5180,6 +5180,22 @@ function sanitize_option( $option, $value ) {
51805180
$value = implode( "\n", $value );
51815181
}
51825182
break;
5183+
5184+
case '_site_transient_update_plugins':
5185+
case '_site_transient_update_themes':
5186+
$value = map_deep(
5187+
$value,
5188+
static function ( $scalar_value ) use ( &$error ) {
5189+
global $wpdb;
5190+
5191+
$scalar_value = $wpdb->strip_invalid_text_for_column( $wpdb->options, 'option_value', $scalar_value );
5192+
if ( is_wp_error( $scalar_value ) ) {
5193+
$error = $scalar_value->get_error_message();
5194+
}
5195+
return $scalar_value;
5196+
}
5197+
);
5198+
break;
51835199
}
51845200

51855201
if ( null !== $error ) {

‎tests/phpunit/tests/option/sanitizeOption.php‎

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,74 @@ public function test_emoji_in_blogname_and_description() {
134134
$this->assertSame( $expected, sanitize_option( 'blogdescription', $value ) );
135135
}
136136

137+
/**
138+
* @ticket 64550
139+
*
140+
* @covers ::sanitize_option
141+
*/
142+
public function test_emoji_in_plugin_updates() {
143+
global $wpdb;
144+
145+
$updates = new stdClass();
146+
$updates->last_checked = 1234567890;
147+
$plugin_1 = new stdClass();
148+
$plugin_1->id = 'w.org/plugins/classic-editor';
149+
$plugin_1->slug = 'classic-editor';
150+
$plugin_1->plugin = 'classic-editor/classic-editor.php';
151+
$plugin_1->new_version = '1.7.0';
152+
$plugin_1->url = 'https://wordpress.org/plugins/classic-editor/';
153+
$plugin_1->package = 'https://downloads.wordpress.org/plugin/classic-editor.1.7.0.zip';
154+
$plugin_1->icons = array(
155+
'2x' => 'https://ps.w.org/classic-editor/assets/icon-256x256.png?rev=1998671',
156+
'1x' => 'https://ps.w.org/classic-editor/assets/icon-128x128.png?rev=1998671',
157+
);
158+
$plugin_1->banners = array(
159+
'2x' => 'https://ps.w.org/classic-editor/assets/banner-1544x500.png?rev=1998671',
160+
'1x' => 'https://ps.w.org/classic-editor/assets/banner-772x250.png?rev=1998676',
161+
);
162+
$plugin_1->banners_rtl = array();
163+
$plugin_1->requires = '4.9';
164+
$plugin_1->tested = '7.0.4';
165+
$plugin_1->requires_php = '5.2.4';
166+
$plugin_1->requires_plugins = array();
167+
$plugin_2 = new stdClass();
168+
$plugin_2->id = 'w.org/plugins/classic-widgets';
169+
$plugin_2->slug = 'classic-widgets';
170+
$plugin_2->plugin = 'classic-widgets/classic-widgets.php';
171+
$plugin_2->new_version = '0.3';
172+
$plugin_2->url = 'https://wordpress.org/plugins/classic-widgets/';
173+
$plugin_2->package = 'https://downloads.wordpress.org/plugin/classic-widgets.0.3.zip';
174+
$plugin_2->icons = array(
175+
'default' => 'https://s.w.org/plugins/geopattern-icon/classic-widgets.svg',
176+
);
177+
$plugin_2->banners = array();
178+
$plugin_2->banners_rtl = array();
179+
$plugin_2->requires = '4.9';
180+
$plugin_2->tested = '6.9.7';
181+
$plugin_2->requires_php = '5.6';
182+
$plugin_2->requires_plugins = array();
183+
$plugin_2->upgrade_notice = "\xf0\x9f\x98\x88 This plugin does not really have an upgrade notice, but if it did, it would be here.";
184+
$updates->response = array(
185+
'classic-editor/classic-editor.php' => $plugin_1,
186+
'classic-widgets/classic-widgets.php' => $plugin_2,
187+
);
188+
$updates->translations = array();
189+
$updates->no_update = array();
190+
$updates->checked = array(
191+
'classic-editor/classic-editor.php' => '1.6.7',
192+
'classic-widgets/classic-widgets.php' => '0.2',
193+
);
194+
195+
// Create a deep copy.
196+
$expected = unserialize( serialize( $updates ) );
197+
198+
if ( 'utf8mb4' !== $wpdb->get_col_charset( $wpdb->options, 'option_value' ) ) {
199+
$expected->response['classic-widgets/classic-widgets.php']->upgrade_notice = ' This plugin does not really have an upgrade notice, but if it did, it would be here.';
200+
}
201+
202+
$this->assertEquals( $expected, sanitize_option( '_site_transient_update_plugins', $updates ) );
203+
}
204+
137205
/**
138206
* @dataProvider data_sanitize_option_permalink_structure
139207
*

0 commit comments

Comments
 (0)