}
// Ensure it only contains valid characters (alphanumeric 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)) {
return FALSE;
}
return TRUE;
}
+ /**
+ * Validate that a string is valid order by clause.
+ *
+ * @param $str
+ * @return bool
+ */
+ public static function mysqlOrderBy($str) {
+ // Making a regex for a comma separated list is quite hard and not readable
+ // 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))) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+ }
+
/**
* @param $str
*
case 'MysqlOrderByDirection':
if (CRM_Utils_Rule::mysqlOrderByDirection($data)) {
- return $data;
+ return strtolower($data);
+ }
+ break;
+
+ case 'MysqlOrderBy':
+ 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));
+ }
+ return implode(', ', $parts);
}
break;
}
break;
+ case 'MysqlColumnName':
+ if (CRM_Utils_Rule::mysqlColumnName($data)) {
+ return $data;
+ }
+ break;
+ case 'MysqlOrderByDirection':
+ if (CRM_Utils_Rule::mysqlOrderByDirection($data)) {
+ return strtolower($data);
+ }
+ break;
+ case 'MysqlOrderBy':
+ if (CRM_Utils_Rule::mysqlOrderBy($data)) {
+ return $data;
+ }
+ break;
+
default:
CRM_Core_Error::fatal("Cannot recognize $type for $data");
break;
return NULL;
}
+ /**
+ * preg_replace_callback for MysqlOrderBy escape.
+ */
+ public static function mysqlOrderByCallback($matches) {
+ $output = '';
+ // Column or table name.
+ if (isset($matches[1])) {
+ $output .= '`' . $matches[1] . '`';
+ }
+ // Column name in case there is a table.
+ if (isset($matches[2]) && $matches[2]) {
+ $output .= '.`' . $matches[2] . '`';
+ }
+ // Sort order.
+ if (isset($matches[3]) && $matches[3]) {
+ $output .= ' ' . $matches[3];
+ }
+ return $output;
+ }
+
}
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('invalid-column-name', 'MysqlColumnName', NULL),
+ 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('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('-3', 'ContactReference', NULL),
// Escape function is meant for sql, not xss
array('<p onclick="alert(\'xss\');">Hello</p>', 'Memo', '<p onclick=\\"alert(\\\'xss\\\');\\">Hello</p>'),
+ 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('column_name, sleep(5)', 'MysqlColumnName', 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'),
);
}