From 951ba594ce818ca25486c58e2f25500fcdd2a86c Mon Sep 17 00:00:00 2001 From: Mattias Michaux Date: Sun, 8 May 2016 10:02:25 +0200 Subject: [PATCH 1/8] Fixed regression when search for location types. --- CRM/Utils/Rule.php | 7 ++++--- tests/phpunit/CRM/Utils/TypeTest.php | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CRM/Utils/Rule.php b/CRM/Utils/Rule.php index 3251d26bbefb..efb64eb8589e 100644 --- a/CRM/Utils/Rule.php +++ b/CRM/Utils/Rule.php @@ -100,11 +100,12 @@ public static function mysqlColumnName($str) { return FALSE; } - // Ensure it only contains valid characters (alphanumeric and underscores). + // Ensure it only contains valid characters (alphanumeric, hyphens and + // underscores). // // MySQL permits column names that don't match this (eg containing spaces), // but CiviCRM won't create those ... - if (!preg_match('/^\w{1,64}(\.\w{1,64})?$/i', $str)) { + if (!preg_match('/^[\w-]{1,64}(\.[\w-]{1,64})?$/i', $str)) { return FALSE; } @@ -138,7 +139,7 @@ public static function mysqlOrderBy($str) { // at all, so we split and loop over. $parts = explode(',', $str); foreach ($parts as $part) { - if (!preg_match('/^((\w{1,64})((\.)(\w{1,64}))?( (asc|desc))?)$/i', trim($part))) { + if (!preg_match('/^(([\w-]{1,64})((\.)([\w-]{1,64}))?( (asc|desc))?)$/i', trim($part))) { return FALSE; } } diff --git a/tests/phpunit/CRM/Utils/TypeTest.php b/tests/phpunit/CRM/Utils/TypeTest.php index 4e51944f3133..793a5334bc12 100644 --- a/tests/phpunit/CRM/Utils/TypeTest.php +++ b/tests/phpunit/CRM/Utils/TypeTest.php @@ -40,7 +40,7 @@ public function validateDataProvider() { array('civicrm_column_name', 'MysqlColumnName', 'civicrm_column_name'), array('table.civicrm_column_name', 'MysqlColumnName', 'table.civicrm_column_name'), array('table.civicrm_column_name.toomanydots', 'MysqlColumnName', NULL), - array('invalid-column-name', 'MysqlColumnName', NULL), + array('Home-street_address', 'MysqlColumnName', 'Home-street_address'), array('column_name, sleep(5)', 'MysqlColumnName', NULL), array(str_repeat('a', 64), 'MysqlColumnName', str_repeat('a', 64)), array(str_repeat('a', 65), 'MysqlColumnName', NULL), From 10dae37ab086330633ce908365d90ec5b57c5fa1 Mon Sep 17 00:00:00 2001 From: Mattias Michaux Date: Sun, 8 May 2016 10:44:32 +0200 Subject: [PATCH 2/8] Fixed test failure. --- tests/phpunit/CRM/Utils/TypeTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/CRM/Utils/TypeTest.php b/tests/phpunit/CRM/Utils/TypeTest.php index 793a5334bc12..9271c5071b57 100644 --- a/tests/phpunit/CRM/Utils/TypeTest.php +++ b/tests/phpunit/CRM/Utils/TypeTest.php @@ -93,7 +93,7 @@ public function escapeDataProvider() { array('civicrm_column_name', 'MysqlColumnName', '`civicrm_column_name`'), array('table.civicrm_column_name', 'MysqlColumnName', '`table`.`civicrm_column_name`'), array('table.civicrm_column_name.toomanydots', 'MysqlColumnName', NULL), - array('invalid-column-name', 'MysqlColumnName', NULL), + array('Home-street_address', 'MysqlColumnName', 'Home-street_address'), array('column_name, sleep(5)', 'MysqlColumnName', NULL), array('asc', 'MysqlOrderByDirection', 'asc'), array('DESC', 'MysqlOrderByDirection', 'desc'), From 976b4f906096048dec07ea432ed0ca384b7ee045 Mon Sep 17 00:00:00 2001 From: Mattias Michaux Date: Sun, 8 May 2016 14:55:30 +0200 Subject: [PATCH 3/8] Fixed escape test failure. --- tests/phpunit/CRM/Utils/TypeTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/CRM/Utils/TypeTest.php b/tests/phpunit/CRM/Utils/TypeTest.php index 9271c5071b57..367f08e299e2 100644 --- a/tests/phpunit/CRM/Utils/TypeTest.php +++ b/tests/phpunit/CRM/Utils/TypeTest.php @@ -93,7 +93,7 @@ public function escapeDataProvider() { array('civicrm_column_name', 'MysqlColumnName', '`civicrm_column_name`'), array('table.civicrm_column_name', 'MysqlColumnName', '`table`.`civicrm_column_name`'), array('table.civicrm_column_name.toomanydots', 'MysqlColumnName', NULL), - array('Home-street_address', 'MysqlColumnName', 'Home-street_address'), + array('Home-street_address', 'MysqlColumnName', '`Home-street_address`'), array('column_name, sleep(5)', 'MysqlColumnName', NULL), array('asc', 'MysqlOrderByDirection', 'asc'), array('DESC', 'MysqlOrderByDirection', 'desc'), From a33b83c5cd9095cd2c1c9e2241bd9d5d4c141206 Mon Sep 17 00:00:00 2001 From: Mattias Michaux Date: Thu, 12 May 2016 09:42:08 +0200 Subject: [PATCH 4/8] Added support for both aliased and non-aliased column names. --- CRM/Core/Page/AJAX.php | 2 +- CRM/Utils/Rule.php | 16 +++++++--------- CRM/Utils/Sort.php | 2 +- CRM/Utils/Type.php | 20 +++++++++++--------- tests/phpunit/CRM/Utils/TypeTest.php | 26 ++++++++++++++++---------- 5 files changed, 36 insertions(+), 30 deletions(-) diff --git a/CRM/Core/Page/AJAX.php b/CRM/Core/Page/AJAX.php index 84185152f176..5830a624cf5f 100644 --- a/CRM/Core/Page/AJAX.php +++ b/CRM/Core/Page/AJAX.php @@ -222,7 +222,7 @@ public static function defaultSortAndPagerParams($defaultOffset = 0, $defaultRow $sortMapper = array(); if (isset($_GET['columns'])) { foreach ($_GET['columns'] as $key => $value) { - $sortMapper[$key] = CRM_Utils_Type::validate($value['data'], 'MysqlColumnName'); + $sortMapper[$key] = CRM_Utils_Type::validate($value['data'], 'MysqlColumnNameOrAlias'); }; } diff --git a/CRM/Utils/Rule.php b/CRM/Utils/Rule.php index efb64eb8589e..3e6dbd0c4eea 100644 --- a/CRM/Utils/Rule.php +++ b/CRM/Utils/Rule.php @@ -88,24 +88,22 @@ public static function variable($str) { } /** - * Validate an acceptable column name for sorting results. + * Validate that a string is a valid MySQL column name or alias. * * @param $str * * @return bool */ - public static function mysqlColumnName($str) { + public static function mysqlColumnNameOrAlias($str) { // Check not empty. if (empty($str)) { return FALSE; } - // Ensure it only contains valid characters (alphanumeric, hyphens and - // underscores). - // - // MySQL permits column names that don't match this (eg containing spaces), - // but CiviCRM won't create those ... - if (!preg_match('/^[\w-]{1,64}(\.[\w-]{1,64})?$/i', $str)) { + // Ensure the string contains only valid characters: + // For column names: alphanumeric and underscores + // For aliases: backticks, alphanumeric hyphens and underscores. + if (!preg_match('^((`[\w-]{1,64}`|\w{1,64})\.)?(`[\w-]{1,64}`|\w{1,64})$/i', $str)) { return FALSE; } @@ -139,7 +137,7 @@ public static function mysqlOrderBy($str) { // at all, so we split and loop over. $parts = explode(',', $str); foreach ($parts as $part) { - if (!preg_match('/^(([\w-]{1,64})((\.)([\w-]{1,64}))?( (asc|desc))?)$/i', trim($part))) { + if (!preg_match('/^((`[\w-]{1,64}`|\w{1,64})\.)?(`[\w-]{1,64}`|\w{1,64})( (asc|desc))?$/i', trim($part))) { return FALSE; } } diff --git a/CRM/Utils/Sort.php b/CRM/Utils/Sort.php index 2b5765f23598..fec2d400e32d 100644 --- a/CRM/Utils/Sort.php +++ b/CRM/Utils/Sort.php @@ -121,7 +121,7 @@ public function __construct(&$vars, $defaultSortOrder = NULL) { foreach ($vars as $weight => $value) { $this->_vars[$weight] = array( - 'name' => CRM_Utils_Type::validate($value['sort'], 'MysqlColumnName'), + 'name' => CRM_Utils_Type::validate($value['sort'], 'MysqlColumnNameOrAlias'), 'direction' => CRM_Utils_Array::value('direction', $value), 'title' => $value['name'], ); diff --git a/CRM/Utils/Type.php b/CRM/Utils/Type.php index 44e8f17a07e8..43fa00a5b29d 100644 --- a/CRM/Utils/Type.php +++ b/CRM/Utils/Type.php @@ -268,8 +268,9 @@ public static function escape($data, $type, $abort = TRUE) { } break; - case 'MysqlColumnName': - if (CRM_Utils_Rule::mysqlColumnName($data)) { + case 'mysqlColumnNameOrAlias': + if (CRM_Utils_Rule::mysqlColumnNameOrAlias($data)) { + $data = str_replace('`', '', $data); $parts = explode('.', $data); $data = '`' . implode('`.`', $parts) . '`'; @@ -287,7 +288,7 @@ public static function escape($data, $type, $abort = TRUE) { if (CRM_Utils_Rule::mysqlOrderBy($data)) { $parts = explode(',', $data); foreach ($parts as &$part) { - $part = preg_replace_callback('/(?:([\w]+)(?:(?:\.)([\w]+))?(?: (asc|desc))?)/i', array('CRM_Utils_Type', 'mysqlOrderByCallback'), trim($part)); + $part = preg_replace_callback('/^(?:(?:((?:`[\w-]{1,64}`|\w{1,64}))(?:\.))?(`[\w-]{1,64}`|\w{1,64})(?: (asc|desc))?)$/i', array('CRM_Utils_Type', 'mysqlOrderByCallback'), trim($part)); } return implode(', ', $parts); } @@ -396,8 +397,8 @@ public static function validate($data, $type, $abort = TRUE, $name = 'One of par } break; - case 'MysqlColumnName': - if (CRM_Utils_Rule::mysqlColumnName($data)) { + case 'mysqlColumnNameOrAlias': + if (CRM_Utils_Rule::mysqlColumnNameOrAlias($data)) { return $data; } break; @@ -432,15 +433,16 @@ public static function validate($data, $type, $abort = TRUE, $name = 'One of par */ public static function mysqlOrderByCallback($matches) { $output = ''; + $matches = str_replace('`', '', $matches); - // Column or table name. + // Table name. if (isset($matches[1])) { - $output .= '`' . $matches[1] . '`'; + $output .= '`' . $matches[1] . '`.'; } - // Column name in case there is a table. + // Column name. if (isset($matches[2]) && $matches[2]) { - $output .= '.`' . $matches[2] . '`'; + $output .= '`' . $matches[2] . '`'; } // Sort order. diff --git a/tests/phpunit/CRM/Utils/TypeTest.php b/tests/phpunit/CRM/Utils/TypeTest.php index 367f08e299e2..781b2d16ab66 100644 --- a/tests/phpunit/CRM/Utils/TypeTest.php +++ b/tests/phpunit/CRM/Utils/TypeTest.php @@ -37,21 +37,26 @@ public function validateDataProvider() { array(-10, 'Positive', NULL), array('-10', 'Positive', NULL), array('-10foo', 'Positive', NULL), - array('civicrm_column_name', 'MysqlColumnName', 'civicrm_column_name'), - array('table.civicrm_column_name', 'MysqlColumnName', 'table.civicrm_column_name'), - array('table.civicrm_column_name.toomanydots', 'MysqlColumnName', NULL), - array('Home-street_address', 'MysqlColumnName', 'Home-street_address'), - array('column_name, sleep(5)', 'MysqlColumnName', NULL), - array(str_repeat('a', 64), 'MysqlColumnName', str_repeat('a', 64)), - array(str_repeat('a', 65), 'MysqlColumnName', NULL), - array(str_repeat('a', 64) . '.' . str_repeat('a', 64), 'MysqlColumnName', str_repeat('a', 64) . '.' . str_repeat('a', 64)), - array(str_repeat('a', 64) . '.' . str_repeat('a', 65), 'MysqlColumnName', NULL), - array(str_repeat('a', 65) . '.' . str_repeat('a', 64), 'MysqlColumnName', NULL), + array('civicrm_column_name', 'mysqlColumnNameOrAlias', 'civicrm_column_name'), + array('table.civicrm_column_name', 'mysqlColumnNameOrAlias', 'table.civicrm_column_name'), + array('table.civicrm_column_name.toomanydots', 'mysqlColumnNameOrAlias', NULL), + array('Home-street_address', 'mysqlColumnNameOrAlias', NULL), + array('`Home-street_address`', 'mysqlColumnNameOrAlias', '`Home-street_address`'), + array('table.`Home-street_address`', 'mysqlColumnNameOrAlias', 'table.`Home-street_address`'), + array('`table-alias`.`Home-street_address`', 'mysqlColumnNameOrAlias', '`table-alias`.`Home-street_address`'), + array('`table-alias`.column', 'mysqlColumnNameOrAlias', '`table-alias`.column'), + array('column_name, sleep(5)', 'mysqlColumnNameOrAlias', NULL), + array(str_repeat('a', 64), 'mysqlColumnNameOrAlias', str_repeat('a', 64)), + array(str_repeat('a', 65), 'mysqlColumnNameOrAlias', NULL), + array(str_repeat('a', 64) . '.' . str_repeat('a', 64), 'mysqlColumnNameOrAlias', str_repeat('a', 64) . '.' . str_repeat('a', 64)), + array(str_repeat('a', 64) . '.' . str_repeat('a', 65), 'mysqlColumnNameOrAlias', NULL), + array(str_repeat('a', 65) . '.' . str_repeat('a', 64), 'mysqlColumnNameOrAlias', NULL), array('asc', 'MysqlOrderByDirection', 'asc'), array('DESC', 'MysqlOrderByDirection', 'desc'), array('DESCc', 'MysqlOrderByDirection', NULL), array('table.civicrm_column_name desc', 'MysqlOrderBy', 'table.civicrm_column_name desc'), array('table.civicrm_column_name desc,other_column, another_column desc', 'MysqlOrderBy', 'table.civicrm_column_name desc,other_column, another_column desc'), + array('table.`Home-street_address` asc, `table-alias`.`Home-street_address` desc,`table-alias`.column', 'MysqlOrderBy', 'table.`Home-street_address` asc, `table-alias`.`Home-street_address` desc,`table-alias`.column'), ); } @@ -100,6 +105,7 @@ public function escapeDataProvider() { array('DESCc', 'MysqlOrderByDirection', NULL), array('table.civicrm_column_name desc', 'MysqlOrderBy', '`table`.`civicrm_column_name` desc'), array('table.civicrm_column_name desc,other_column,another_column desc', 'MysqlOrderBy', '`table`.`civicrm_column_name` desc, `other_column`, `another_column` desc'), + array('table.`Home-street_address` asc, `table-alias`.`Home-street_address` desc,`table-alias`.column', 'MysqlOrderBy', '`table`.`Home-street_address` asc, `table-alias`.`Home-street_address` desc, `table-alias`.`column`'), ); } From f19a5565fb917b4490d910ee96e5088fe1e83e5f Mon Sep 17 00:00:00 2001 From: Mattias Michaux Date: Thu, 12 May 2016 10:50:08 +0200 Subject: [PATCH 5/8] Fixed tests, checks and naming. --- CRM/Utils/Rule.php | 2 +- CRM/Utils/Type.php | 6 ++--- tests/phpunit/CRM/Utils/TypeTest.php | 39 ++++++++++++++-------------- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/CRM/Utils/Rule.php b/CRM/Utils/Rule.php index 3e6dbd0c4eea..b6f4f7e6dc20 100644 --- a/CRM/Utils/Rule.php +++ b/CRM/Utils/Rule.php @@ -103,7 +103,7 @@ public static function mysqlColumnNameOrAlias($str) { // Ensure the string contains only valid characters: // For column names: alphanumeric and underscores // For aliases: backticks, alphanumeric hyphens and underscores. - if (!preg_match('^((`[\w-]{1,64}`|\w{1,64})\.)?(`[\w-]{1,64}`|\w{1,64})$/i', $str)) { + if (!preg_match('/^((`[\w-]{1,64}`|\w{1,64})\.)?(`[\w-]{1,64}`|\w{1,64})$/i', $str)) { return FALSE; } diff --git a/CRM/Utils/Type.php b/CRM/Utils/Type.php index 43fa00a5b29d..f49f2127011d 100644 --- a/CRM/Utils/Type.php +++ b/CRM/Utils/Type.php @@ -268,7 +268,7 @@ public static function escape($data, $type, $abort = TRUE) { } break; - case 'mysqlColumnNameOrAlias': + case 'MysqlColumnNameOrAlias': if (CRM_Utils_Rule::mysqlColumnNameOrAlias($data)) { $data = str_replace('`', '', $data); $parts = explode('.', $data); @@ -397,7 +397,7 @@ public static function validate($data, $type, $abort = TRUE, $name = 'One of par } break; - case 'mysqlColumnNameOrAlias': + case 'MysqlColumnNameOrAlias': if (CRM_Utils_Rule::mysqlColumnNameOrAlias($data)) { return $data; } @@ -436,7 +436,7 @@ public static function mysqlOrderByCallback($matches) { $matches = str_replace('`', '', $matches); // Table name. - if (isset($matches[1])) { + if (isset($matches[1]) && $matches[1]) { $output .= '`' . $matches[1] . '`.'; } diff --git a/tests/phpunit/CRM/Utils/TypeTest.php b/tests/phpunit/CRM/Utils/TypeTest.php index 781b2d16ab66..0bd51b3ff697 100644 --- a/tests/phpunit/CRM/Utils/TypeTest.php +++ b/tests/phpunit/CRM/Utils/TypeTest.php @@ -37,20 +37,20 @@ public function validateDataProvider() { array(-10, 'Positive', NULL), array('-10', 'Positive', NULL), array('-10foo', 'Positive', NULL), - array('civicrm_column_name', 'mysqlColumnNameOrAlias', 'civicrm_column_name'), - array('table.civicrm_column_name', 'mysqlColumnNameOrAlias', 'table.civicrm_column_name'), - array('table.civicrm_column_name.toomanydots', 'mysqlColumnNameOrAlias', NULL), - array('Home-street_address', 'mysqlColumnNameOrAlias', NULL), - array('`Home-street_address`', 'mysqlColumnNameOrAlias', '`Home-street_address`'), - array('table.`Home-street_address`', 'mysqlColumnNameOrAlias', 'table.`Home-street_address`'), - array('`table-alias`.`Home-street_address`', 'mysqlColumnNameOrAlias', '`table-alias`.`Home-street_address`'), - array('`table-alias`.column', 'mysqlColumnNameOrAlias', '`table-alias`.column'), - array('column_name, sleep(5)', 'mysqlColumnNameOrAlias', NULL), - array(str_repeat('a', 64), 'mysqlColumnNameOrAlias', str_repeat('a', 64)), - array(str_repeat('a', 65), 'mysqlColumnNameOrAlias', NULL), - array(str_repeat('a', 64) . '.' . str_repeat('a', 64), 'mysqlColumnNameOrAlias', str_repeat('a', 64) . '.' . str_repeat('a', 64)), - array(str_repeat('a', 64) . '.' . str_repeat('a', 65), 'mysqlColumnNameOrAlias', NULL), - array(str_repeat('a', 65) . '.' . str_repeat('a', 64), 'mysqlColumnNameOrAlias', NULL), + array('civicrm_column_name', 'MysqlColumnNameOrAlias', 'civicrm_column_name'), + array('table.civicrm_column_name', 'MysqlColumnNameOrAlias', 'table.civicrm_column_name'), + array('table.civicrm_column_name.toomanydots', 'MysqlColumnNameOrAlias', NULL), + array('Home-street_address', 'MysqlColumnNameOrAlias', NULL), + array('`Home-street_address`', 'MysqlColumnNameOrAlias', '`Home-street_address`'), + array('table.`Home-street_address`', 'MysqlColumnNameOrAlias', 'table.`Home-street_address`'), + array('`table-alias`.`Home-street_address`', 'MysqlColumnNameOrAlias', '`table-alias`.`Home-street_address`'), + array('`table-alias`.column', 'MysqlColumnNameOrAlias', '`table-alias`.column'), + array('column_name, sleep(5)', 'MysqlColumnNameOrAlias', NULL), + array(str_repeat('a', 64), 'MysqlColumnNameOrAlias', str_repeat('a', 64)), + array(str_repeat('a', 65), 'MysqlColumnNameOrAlias', NULL), + array(str_repeat('a', 64) . '.' . str_repeat('a', 64), 'MysqlColumnNameOrAlias', str_repeat('a', 64) . '.' . str_repeat('a', 64)), + array(str_repeat('a', 64) . '.' . str_repeat('a', 65), 'MysqlColumnNameOrAlias', NULL), + array(str_repeat('a', 65) . '.' . str_repeat('a', 64), 'MysqlColumnNameOrAlias', NULL), array('asc', 'MysqlOrderByDirection', 'asc'), array('DESC', 'MysqlOrderByDirection', 'desc'), array('DESCc', 'MysqlOrderByDirection', NULL), @@ -95,11 +95,12 @@ public function escapeDataProvider() { array('-3', 'ContactReference', NULL), // Escape function is meant for sql, not xss array('

Hello

', 'Memo', '

Hello

'), - array('civicrm_column_name', 'MysqlColumnName', '`civicrm_column_name`'), - array('table.civicrm_column_name', 'MysqlColumnName', '`table`.`civicrm_column_name`'), - array('table.civicrm_column_name.toomanydots', 'MysqlColumnName', NULL), - array('Home-street_address', 'MysqlColumnName', '`Home-street_address`'), - array('column_name, sleep(5)', 'MysqlColumnName', NULL), + array('civicrm_column_name', 'MysqlColumnNameOrAlias', '`civicrm_column_name`'), + array('table.civicrm_column_name', 'MysqlColumnNameOrAlias', '`table`.`civicrm_column_name`'), + array('table.civicrm_column_name.toomanydots', 'MysqlColumnNameOrAlias', NULL), + array('Home-street_address', 'MysqlColumnNameOrAlias', NULL), + array('`Home-street_address`', 'MysqlColumnNameOrAlias', '`Home-street_address`'), + array('column_name, sleep(5)', 'MysqlColumnNameOrAlias', NULL), array('asc', 'MysqlOrderByDirection', 'asc'), array('DESC', 'MysqlOrderByDirection', 'desc'), array('DESCc', 'MysqlOrderByDirection', NULL), From 668c2561a924a6abefa23b56c89a26fa493e94f6 Mon Sep 17 00:00:00 2001 From: Mattias Michaux Date: Tue, 24 May 2016 07:29:06 +0200 Subject: [PATCH 6/8] Readded escaping to CRM_Utils_Sort and fixed str_replace in CRM_Contact_BAO_Query->searchQuery() --- CRM/Contact/BAO/Query.php | 4 ++-- CRM/Utils/Sort.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CRM/Contact/BAO/Query.php b/CRM/Contact/BAO/Query.php index d0c25c75175b..235b3713ed88 100644 --- a/CRM/Contact/BAO/Query.php +++ b/CRM/Contact/BAO/Query.php @@ -4573,8 +4573,8 @@ public function searchQuery( if (!empty($orderBy)) { // this is special case while searching for // change log CRM-1718 - if (preg_match('/sort_name/i', $orderBy)) { - $orderBy = str_replace('sort_name', 'contact_a.sort_name', $orderBy); + if (preg_match('/`sort_name`/i', $orderBy)) { + $orderBy = str_replace('`sort_name`', '`contact_a`.`sort_name`', $orderBy); } $orderBy = CRM_Utils_Type::escape($orderBy, 'String'); diff --git a/CRM/Utils/Sort.php b/CRM/Utils/Sort.php index fec2d400e32d..55e2b97704f8 100644 --- a/CRM/Utils/Sort.php +++ b/CRM/Utils/Sort.php @@ -152,11 +152,11 @@ public function orderBy() { $this->_vars[$this->_currentSortID]['direction'] == self::DONTCARE ) { $this->_vars[$this->_currentSortID]['name'] = str_replace(' ', '_', $this->_vars[$this->_currentSortID]['name']); - return $this->_vars[$this->_currentSortID]['name'] . ' asc'; + return CRM_Utils_Type::escape($this->_vars[$this->_currentSortID]['name'], 'MysqlColumnNameOrAlias') . ' asc'; } else { $this->_vars[$this->_currentSortID]['name'] = str_replace(' ', '_', $this->_vars[$this->_currentSortID]['name']); - return $this->_vars[$this->_currentSortID]['name'] . ' desc'; + return CRM_Utils_Type::escape($this->_vars[$this->_currentSortID]['name'], 'MysqlColumnNameOrAlias') . ' desc'; } } From 21858a195870452cce55a7e6a020b112d56d3d37 Mon Sep 17 00:00:00 2001 From: Mattias Michaux Date: Tue, 24 May 2016 23:51:41 +0200 Subject: [PATCH 7/8] Loosen the MysqlColumnNameOrAlias validation. --- CRM/Utils/Rule.php | 4 ++-- CRM/Utils/Type.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CRM/Utils/Rule.php b/CRM/Utils/Rule.php index b6f4f7e6dc20..9306b4b36fe1 100644 --- a/CRM/Utils/Rule.php +++ b/CRM/Utils/Rule.php @@ -103,7 +103,7 @@ public static function mysqlColumnNameOrAlias($str) { // Ensure the string contains only valid characters: // For column names: alphanumeric and underscores // For aliases: backticks, alphanumeric hyphens and underscores. - if (!preg_match('/^((`[\w-]{1,64}`|\w{1,64})\.)?(`[\w-]{1,64}`|\w{1,64})$/i', $str)) { + if (!preg_match('/^((`[\w-]{1,64}`|[\w–]{1,64})\.)?(`[\w-]{1,64}`|[\w–]{1,64})$/i', $str)) { return FALSE; } @@ -137,7 +137,7 @@ public static function mysqlOrderBy($str) { // at all, so we split and loop over. $parts = explode(',', $str); foreach ($parts as $part) { - if (!preg_match('/^((`[\w-]{1,64}`|\w{1,64})\.)?(`[\w-]{1,64}`|\w{1,64})( (asc|desc))?$/i', trim($part))) { + if (!preg_match('/^((`[\w-]{1,64}`|[\w–]{1,64})\.)?(`[\w-]{1,64}`|[\w–]{1,64})( (asc|desc))?$/i', trim($part))) { return FALSE; } } diff --git a/CRM/Utils/Type.php b/CRM/Utils/Type.php index f49f2127011d..374975d21b7b 100644 --- a/CRM/Utils/Type.php +++ b/CRM/Utils/Type.php @@ -288,7 +288,7 @@ public static function escape($data, $type, $abort = TRUE) { if (CRM_Utils_Rule::mysqlOrderBy($data)) { $parts = explode(',', $data); foreach ($parts as &$part) { - $part = preg_replace_callback('/^(?:(?:((?:`[\w-]{1,64}`|\w{1,64}))(?:\.))?(`[\w-]{1,64}`|\w{1,64})(?: (asc|desc))?)$/i', array('CRM_Utils_Type', 'mysqlOrderByCallback'), trim($part)); + $part = preg_replace_callback('/^(?:(?:((?:`[\w-]{1,64}`|[\w–]{1,64}))(?:\.))?(`[\w-]{1,64}`|[\w–]{1,64})(?: (asc|desc))?)$/i', array('CRM_Utils_Type', 'mysqlOrderByCallback'), trim($part)); } return implode(', ', $parts); } From dd78a9ade77a09d477124f72d15dee17ac88ad35 Mon Sep 17 00:00:00 2001 From: Mattias Michaux Date: Wed, 25 May 2016 00:04:38 +0200 Subject: [PATCH 8/8] Fixed wrong dash. --- CRM/Utils/Rule.php | 4 ++-- CRM/Utils/Type.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CRM/Utils/Rule.php b/CRM/Utils/Rule.php index 9306b4b36fe1..0ea7c32e3cc3 100644 --- a/CRM/Utils/Rule.php +++ b/CRM/Utils/Rule.php @@ -103,7 +103,7 @@ public static function mysqlColumnNameOrAlias($str) { // Ensure the string contains only valid characters: // For column names: alphanumeric and underscores // For aliases: backticks, alphanumeric hyphens and underscores. - if (!preg_match('/^((`[\w-]{1,64}`|[\w–]{1,64})\.)?(`[\w-]{1,64}`|[\w–]{1,64})$/i', $str)) { + if (!preg_match('/^((`[\w-]{1,64}`|[\w-]{1,64})\.)?(`[\w-]{1,64}`|[\w-]{1,64})$/i', $str)) { return FALSE; } @@ -137,7 +137,7 @@ public static function mysqlOrderBy($str) { // at all, so we split and loop over. $parts = explode(',', $str); foreach ($parts as $part) { - if (!preg_match('/^((`[\w-]{1,64}`|[\w–]{1,64})\.)?(`[\w-]{1,64}`|[\w–]{1,64})( (asc|desc))?$/i', trim($part))) { + if (!preg_match('/^((`[\w-]{1,64}`|[\w-]{1,64})\.)?(`[\w-]{1,64}`|[\w-]{1,64})( (asc|desc))?$/i', trim($part))) { return FALSE; } } diff --git a/CRM/Utils/Type.php b/CRM/Utils/Type.php index 374975d21b7b..db7b0ea2d42f 100644 --- a/CRM/Utils/Type.php +++ b/CRM/Utils/Type.php @@ -288,7 +288,7 @@ public static function escape($data, $type, $abort = TRUE) { if (CRM_Utils_Rule::mysqlOrderBy($data)) { $parts = explode(',', $data); foreach ($parts as &$part) { - $part = preg_replace_callback('/^(?:(?:((?:`[\w-]{1,64}`|[\w–]{1,64}))(?:\.))?(`[\w-]{1,64}`|[\w–]{1,64})(?: (asc|desc))?)$/i', array('CRM_Utils_Type', 'mysqlOrderByCallback'), trim($part)); + $part = preg_replace_callback('/^(?:(?:((?:`[\w-]{1,64}`|[\w-]{1,64}))(?:\.))?(`[\w-]{1,64}`|[\w-]{1,64})(?: (asc|desc))?)$/i', array('CRM_Utils_Type', 'mysqlOrderByCallback'), trim($part)); } return implode(', ', $parts); }