From a2ab1a3cfd6b68b90a2d81fb17cf89d96e0d3e86 Mon Sep 17 00:00:00 2001 From: Sam Najian Date: Thu, 31 Mar 2022 16:54:51 +0200 Subject: [PATCH] Escape queries before passing to $wpdb methods [MAILPOET-4219] --- .../WooCommerce/Events/FirstPurchase.php | 6 +-- mailpoet/lib/Config/MP2Migrator.php | 51 ++++++++++--------- mailpoet/lib/Config/Migrator.php | 51 ++++++++++--------- mailpoet/lib/Config/Populator.php | 41 +++++++-------- .../Cron/Workers/SendingQueue/Migration.php | 19 +++---- mailpoet/lib/WooCommerce/Helper.php | 6 +-- 6 files changed, 85 insertions(+), 89 deletions(-) diff --git a/mailpoet/lib/AutomaticEmails/WooCommerce/Events/FirstPurchase.php b/mailpoet/lib/AutomaticEmails/WooCommerce/Events/FirstPurchase.php index 4e7309cccf..0f534fc7c8 100644 --- a/mailpoet/lib/AutomaticEmails/WooCommerce/Events/FirstPurchase.php +++ b/mailpoet/lib/AutomaticEmails/WooCommerce/Events/FirstPurchase.php @@ -198,13 +198,13 @@ class FirstPurchase { private function getGuestCustomerOrderCountByEmail($customerEmail) { global $wpdb; - $count = $wpdb->get_var( "SELECT COUNT(*) + $count = $wpdb->get_var( $wpdb->prepare("SELECT COUNT(*) FROM $wpdb->posts as posts LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id WHERE meta.meta_key = '_billing_email' AND posts.post_type = 'shop_order' - AND meta_value = '" . WPFunctions::get()->escSql($customerEmail) . "' - " ); + AND meta_value = %s + ", $customerEmail)); return (int)$count; } } diff --git a/mailpoet/lib/Config/MP2Migrator.php b/mailpoet/lib/Config/MP2Migrator.php index 004a1f6093..86f0df12bc 100644 --- a/mailpoet/lib/Config/MP2Migrator.php +++ b/mailpoet/lib/Config/MP2Migrator.php @@ -15,6 +15,7 @@ use MailPoet\Util\Notices\AfterMigrationNotice; use MailPoet\Util\ProgressBar; use MailPoet\WP\Functions as WPFunctions; use MailPoetVendor\Idiorm\ORM; +use function WP_CLI\Utils\esc_like; class MP2Migrator { const IMPORT_TIMEOUT_IN_SECONDS = 7200; // Timeout = 2 hours @@ -134,7 +135,7 @@ class MP2Migrator { global $wpdb; try { - $sql = "SHOW TABLES LIKE '{$table}'"; + $sql = $wpdb->prepare("SHOW TABLES LIKE %s", $table); $result = $wpdb->query($sql); return !empty($result); } catch (\Exception $e) { @@ -369,14 +370,14 @@ class MP2Migrator { global $wpdb; $lastId = intval($this->settings->get('last_imported_list_id', 0)); - $table = $this->mp2ListTable; - $sql = " + $table = esc_sql($this->mp2ListTable); + $sql = $wpdb->prepare(" SELECT l.list_id, l.name, l.description, l.is_enabled, l.created_at FROM `$table` l - WHERE l.list_id > '$lastId' + WHERE l.list_id > %s ORDER BY l.list_id - LIMIT $limit - "; + LIMIT %d + ", $lastId, $limit); $lists = $wpdb->get_results($sql, ARRAY_A); return $lists; @@ -446,7 +447,7 @@ class MP2Migrator { global $wpdb; $customFields = []; - $table = $this->mp2CustomFieldTable; + $table = esc_sql($this->mp2CustomFieldTable); $sql = " SELECT cf.id, cf.name, cf.type, cf.required, cf.settings FROM `$table` cf @@ -606,14 +607,14 @@ class MP2Migrator { private function getUsers($limit) { global $wpdb; $lastId = intval($this->settings->get('last_imported_user_id', 0)); - $table = $this->mp2UserTable; - $sql = " + $table = esc_sql($this->mp2UserTable); + $sql = $wpdb->prepare(" SELECT u.* FROM `$table` u - WHERE u.user_id > '$lastId' + WHERE u.user_id > %d ORDER BY u.user_id - LIMIT $limit - "; + LIMIT %d + ", $lastId, $limit); $users = $wpdb->get_results($sql, ARRAY_A); return $users; @@ -703,12 +704,12 @@ class MP2Migrator { private function getUserLists($userId) { global $wpdb; - $table = $this->mp2UserListTable; - $sql = " + $table = esc_sql($this->mp2UserListTable); + $sql = $wpdb->prepare(" SELECT ul.list_id, ul.sub_date, ul.unsub_date FROM `$table` ul - WHERE ul.user_id = '$userId' - "; + WHERE ul.user_id = %s + ", $userId); $userLists = $wpdb->get_results($sql, ARRAY_A); return $userLists; @@ -853,14 +854,14 @@ class MP2Migrator { global $wpdb; $lastId = intval($this->settings->get('last_imported_form_id', 0)); - $table = $this->mp2FormTable; - $sql = " + $table = esc_sql($this->mp2FormTable); + $sql = $wpdb->prepare(" SELECT f.* FROM `$table` f - WHERE f.form_id > '$lastId' + WHERE f.form_id > %s ORDER BY f.form_id - LIMIT $limit - "; + LIMIT %d + ", $lastId, $limit); $forms = $wpdb->get_results($sql, ARRAY_A); return $forms; @@ -1117,12 +1118,12 @@ class MP2Migrator { global $wpdb; $email = []; - $table = $this->mp2EmailTable; - $sql = " + $table = esc_sql($this->mp2EmailTable); + $sql = $wpdb->prepare(" SELECT e.* FROM `$table` e - WHERE e.email_id = '$emailId' - "; + WHERE e.email_id = %s + ", $emailId); $email = $wpdb->get_row($sql, ARRAY_A); return $email; diff --git a/mailpoet/lib/Config/Migrator.php b/mailpoet/lib/Config/Migrator.php index fef3eb39a7..d34059f347 100644 --- a/mailpoet/lib/Config/Migrator.php +++ b/mailpoet/lib/Config/Migrator.php @@ -100,7 +100,7 @@ class Migrator { $_this = $this; $dropTable = function($model) use($wpdb, $_this) { - $table = $_this->prefix . $model; + $table = esc_sql($_this->prefix . $model); $wpdb->query("DROP TABLE {$table}"); }; @@ -642,8 +642,9 @@ class Migrator { if (version_compare((string)$this->settings->get('db_version', '3.47.6'), '3.47.6', '>')) { return false; } + $table = esc_sql("{$this->prefix}statistics_unsubscribes"); $query = " - ALTER TABLE `{$this->prefix}statistics_unsubscribes` + ALTER TABLE `{$table}` CHANGE `newsletter_id` `newsletter_id` int(11) unsigned NULL, CHANGE `queue_id` `queue_id` int(11) unsigned NULL; "; @@ -665,7 +666,7 @@ class Migrator { } global $wpdb; - $scheduledTasksSubscribersTable = "{$this->prefix}scheduled_task_subscribers"; + $scheduledTasksSubscribersTable = esc_sql("{$this->prefix}scheduled_task_subscribers"); // Remove default CURRENT_TIMESTAMP from created_at $updateCreatedAtQuery = " ALTER TABLE `$scheduledTasksSubscribersTable` @@ -674,11 +675,11 @@ class Migrator { $wpdb->query($updateCreatedAtQuery); // Add updated_at column in case it doesn't exist - $updatedAtColumnExists = $wpdb->get_results(" + $updatedAtColumnExists = $wpdb->get_results($wpdb->prepare(" SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS - WHERE table_name = '$scheduledTasksSubscribersTable' AND column_name = 'updated_at'; - "); + WHERE table_name = %s AND column_name = 'updated_at'; + ", $scheduledTasksSubscribersTable)); if (empty($updatedAtColumnExists)) { $addUpdatedAtQuery = " ALTER TABLE `$scheduledTasksSubscribersTable` @@ -698,17 +699,17 @@ class Migrator { $dbName = Env::$dbName; $statisticsTables = [ - "{$this->prefix}statistics_clicks", - "{$this->prefix}statistics_opens", + esc_sql("{$this->prefix}statistics_clicks"), + esc_sql("{$this->prefix}statistics_opens"), ]; foreach ($statisticsTables as $statisticsTable) { - $oldStatisticsIndexExists = $wpdb->get_results(" + $oldStatisticsIndexExists = $wpdb->get_results($wpdb->prepare(" SELECT DISTINCT INDEX_NAME FROM INFORMATION_SCHEMA.STATISTICS - WHERE TABLE_SCHEMA = '{$dbName}' - AND TABLE_NAME = '$statisticsTable' + WHERE TABLE_SCHEMA = %s + AND TABLE_NAME = %s AND INDEX_NAME='newsletter_id_subscriber_id' - "); + ", $dbName, $statisticsTable)); if (!empty($oldStatisticsIndexExists)) { $dropIndexQuery = " ALTER TABLE `{$statisticsTable}` @@ -728,7 +729,7 @@ class Migrator { return false; } - $dynamicSegmentFiltersTable = "{$this->prefix}dynamic_segment_filters"; + $dynamicSegmentFiltersTable = esc_sql("{$this->prefix}dynamic_segment_filters"); $dynamicSegmentFilters = $wpdb->get_results(" SELECT id, filter_data, filter_type, `action` FROM {$dynamicSegmentFiltersTable} @@ -758,7 +759,7 @@ class Migrator { return false; } - $dynamicSegmentFiltersTable = "{$this->prefix}dynamic_segment_filters"; + $dynamicSegmentFiltersTable = esc_sql("{$this->prefix}dynamic_segment_filters"); $filterType = DynamicSegmentFilterData::TYPE_WOOCOMMERCE; $action = WooCommerceProduct::ACTION_PRODUCT; $dynamicSegmentFilters = $wpdb->get_results(" @@ -798,15 +799,15 @@ class Migrator { return false; } - $dynamicSegmentFiltersTable = "{$this->prefix}dynamic_segment_filters"; + $dynamicSegmentFiltersTable = esc_sql("{$this->prefix}dynamic_segment_filters"); $filterType = DynamicSegmentFilterData::TYPE_WOOCOMMERCE; $action = WooCommerceCategory::ACTION_CATEGORY; - $dynamicSegmentFilters = $wpdb->get_results(" + $dynamicSegmentFilters = $wpdb->get_results($wpdb->prepare(" SELECT `id`, `filter_data`, `filter_type`, `action` FROM {$dynamicSegmentFiltersTable} - WHERE `filter_type` = '{$filterType}' - AND `action` = '{$action}' - ", ARRAY_A); + WHERE `filter_type` = %s + AND `action` = %s + ", $filterType, $action), ARRAY_A); foreach ($dynamicSegmentFilters as $dynamicSegmentFilter) { $filterData = unserialize($dynamicSegmentFilter['filter_data']); @@ -838,15 +839,15 @@ class Migrator { return false; } - $dynamicSegmentFiltersTable = "{$this->prefix}dynamic_segment_filters"; + $dynamicSegmentFiltersTable = esc_sql("{$this->prefix}dynamic_segment_filters"); $filterType = DynamicSegmentFilterData::TYPE_WOOCOMMERCE_SUBSCRIPTION; $action = WooCommerceSubscription::ACTION_HAS_ACTIVE; - $dynamicSegmentFilters = $wpdb->get_results(" + $dynamicSegmentFilters = $wpdb->get_results($wpdb->prepare(" SELECT `id`, `filter_data`, `filter_type`, `action` FROM {$dynamicSegmentFiltersTable} - WHERE `filter_type` = '{$filterType}' - AND `action` = '{$action}' - ", ARRAY_A); + WHERE `filter_type` = %s + AND `action` = %s + ", $filterType, $action), ARRAY_A); foreach ($dynamicSegmentFilters as $dynamicSegmentFilter) { $filterData = unserialize($dynamicSegmentFilter['filter_data']); @@ -877,7 +878,7 @@ class Migrator { return false; } - $dynamicSegmentFiltersTable = "{$this->prefix}dynamic_segment_filters"; + $dynamicSegmentFiltersTable = esc_sql("{$this->prefix}dynamic_segment_filters"); $filterType = DynamicSegmentFilterData::TYPE_EMAIL; $dynamicSegmentFilters = $wpdb->get_results(" SELECT `id`, `filter_data`, `filter_type`, `action` diff --git a/mailpoet/lib/Config/Populator.php b/mailpoet/lib/Config/Populator.php index 3481969e08..64a460ba11 100644 --- a/mailpoet/lib/Config/Populator.php +++ b/mailpoet/lib/Config/Populator.php @@ -530,17 +530,17 @@ class Populator { } } - private function rowExists($table, $columns) { + private function rowExists(string $tableName, array $columns): bool { global $wpdb; - $conditions = array_map(function($key) { - return $key . '=%s'; + $conditions = array_map(function($key) use ($columns) { + return "$key='{$columns[$key]}'"; }, array_keys($columns)); - return $wpdb->get_var($wpdb->prepare( - "SELECT COUNT(*) FROM $table WHERE " . implode(' AND ', $conditions), - array_values($columns) - )) > 0; + $table = esc_sql($tableName); + return $wpdb->get_var( + "SELECT COUNT(*) FROM $table WHERE " . implode(' AND ', $conditions) + ) > 0; } private function insertRow($table, $row) { @@ -575,7 +575,6 @@ class Populator { $conditions = implode(' AND ', $conditions); - $sql = "DELETE FROM `$table` WHERE $conditions"; return $wpdb->query( $wpdb->prepare( "DELETE t1 FROM $table t1, $table t2 WHERE t1.id < t2.id AND $conditions", @@ -614,8 +613,7 @@ class Populator { } $tables = [ScheduledTask::$_table, SendingQueue::$_table]; foreach ($tables as $table) { - $query = "UPDATE `%s` SET meta = NULL WHERE meta = 'null'"; - $wpdb->query(sprintf($query, $table)); + $wpdb->query("UPDATE `$table` SET meta = NULL WHERE meta = 'null'"); } return true; } @@ -652,12 +650,12 @@ class Populator { if (version_compare((string)$this->settings->get('db_version', '3.42.1'), '3.42.0', '>')) { return false; } - $query = "UPDATE `%s` SET last_subscribed_at = GREATEST(COALESCE(confirmed_at, 0), COALESCE(created_at, 0)) WHERE status != '%s' AND last_subscribed_at IS NULL;"; - $wpdb->query(sprintf( - $query, - Subscriber::$_table, + $table = esc_sql(Subscriber::$_table); + $query = $wpdb->prepare( + "UPDATE `{$table}` SET last_subscribed_at = GREATEST(COALESCE(confirmed_at, 0), COALESCE(created_at, 0)) WHERE status != %s AND last_subscribed_at IS NULL;", Subscriber::STATUS_UNCONFIRMED - )); + ); + $wpdb->query($query); return true; } @@ -886,19 +884,14 @@ class Populator { ) ); if ($premiumTableExists) { + $table = esc_sql(Newsletter::$_table); $query = " UPDATE - `%s` as n - JOIN %s as ped ON n.id=ped.newsletter_id + `{$table}` as n + JOIN `$premiumTableName` as ped ON n.id=ped.newsletter_id SET n.ga_campaign = ped.ga_campaign "; - $wpdb->query( - sprintf( - $query, - Newsletter::$_table, - $premiumTableName - ) - ); + $wpdb->query($query); } return true; } diff --git a/mailpoet/lib/Cron/Workers/SendingQueue/Migration.php b/mailpoet/lib/Cron/Workers/SendingQueue/Migration.php index 40bd9dfa77..bbf0d4048e 100644 --- a/mailpoet/lib/Cron/Workers/SendingQueue/Migration.php +++ b/mailpoet/lib/Cron/Workers/SendingQueue/Migration.php @@ -85,7 +85,7 @@ class Migration extends SimpleWorker { private function checkUnmigratedColumnsExist() { global $wpdb; - $existingColumns = $wpdb->get_col('DESC ' . SendingQueueModel::$_table); + $existingColumns = $wpdb->get_col('DESC ' . esc_sql(SendingQueueModel::$_table)); return in_array('type', $existingColumns); } @@ -145,12 +145,13 @@ class Migration extends SimpleWorker { )); // link the queue with the task via task_id $newTaskId = $wpdb->insert_id; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps - $wpdb->query(sprintf( - 'UPDATE %1$s SET `task_id` = %2$s WHERE `id` = %3$s', - MP_SENDING_QUEUES_TABLE, + $table = esc_sql(MP_SENDING_QUEUES_TABLE); + $query = $wpdb->prepare( + "UPDATE `$table` SET `task_id` = %s WHERE `id` = %s", $newTaskId, $queue['id'] - )); + ); + $wpdb->query($query); } } } @@ -195,10 +196,10 @@ class Migration extends SimpleWorker { $migratedUnprocessedCount = ScheduledTaskSubscriber::getUnprocessedCount($taskId); $migratedProcessedCount = ScheduledTaskSubscriber::getProcessedCount($taskId); - $subscribers = $wpdb->get_var(sprintf( - 'SELECT `subscribers` FROM %1$s WHERE `task_id` = %2$d ' . - 'AND (`count_processed` > %3$d OR `count_to_process` > %4$d)', - MP_SENDING_QUEUES_TABLE, + $table = MP_SENDING_QUEUES_TABLE; + $subscribers = $wpdb->get_var($wpdb->prepare( + "SELECT `subscribers` FROM `$table` WHERE `task_id` = %d + AND (`count_processed` > %d OR `count_to_process` > %d)", $taskId, $migratedUnprocessedCount, $migratedProcessedCount diff --git a/mailpoet/lib/WooCommerce/Helper.php b/mailpoet/lib/WooCommerce/Helper.php index 02211c25b7..43daf58d8a 100644 --- a/mailpoet/lib/WooCommerce/Helper.php +++ b/mailpoet/lib/WooCommerce/Helper.php @@ -63,10 +63,10 @@ class Helper { public function getOrdersCountCreatedBefore($dateTime) { global $wpdb; - $result = $wpdb->get_var(" + $result = $wpdb->get_var($wpdb->prepare(" SELECT DISTINCT count(p.ID) FROM {$wpdb->prefix}posts as p - WHERE p.post_type = 'shop_order' AND p.post_date < '{$dateTime}' - "); + WHERE p.post_type = 'shop_order' AND p.post_date < %s + "), $dateTime); return (int)$result; }