diff --git a/system/Database/Query.php b/system/Database/Query.php index 01d27d8bdfff..a4bb1b2b9de5 100644 --- a/system/Database/Query.php +++ b/system/Database/Query.php @@ -367,46 +367,53 @@ public function debugToolbarDisplay(): string { // Key words we want bolded static $highlight = [ - 'SELECT', - 'DISTINCT', - 'FROM', - 'WHERE', 'AND', - 'LEFT JOIN', - 'RIGHT JOIN', - 'JOIN', - 'ORDER BY', + 'AS', 'ASC', + 'AVG', + 'BY', + 'COUNT', 'DESC', - 'GROUP BY', - 'LIMIT', - 'INSERT', - 'INTO', - 'VALUES', - 'UPDATE', - 'OR', + 'DISTINCT', + 'FROM', + 'GROUP', 'HAVING', - 'OFFSET', - 'NOT IN', 'IN', + 'INNER', + 'INSERT', + 'INTO', + 'IS', + 'JOIN', + 'LEFT', 'LIKE', - 'NOT LIKE', - 'COUNT', + 'LIMIT', 'MAX', 'MIN', + 'NOT', + 'NULL', + 'OFFSET', 'ON', - 'AS', - 'AVG', + 'OR', + 'ORDER', + 'RIGHT', + 'SELECT', 'SUM', + 'UPDATE', + 'VALUES', + 'WHERE', ]; if (empty($this->finalQueryString)) { $this->compileBinds(); // @codeCoverageIgnore } - $sql = $this->finalQueryString; + $sql = esc($this->finalQueryString); - $search = '/\b(?:' . implode('|', $highlight) . ')\b/'; + /** + * @see https://stackoverflow.com/a/20767160 + * @see https://regex101.com/r/hUlrGN/4 + */ + $search = '/\b(?:' . implode('|', $highlight) . ')\b(?![^(')]*'(?:(?:[^(')]*'){2})*[^(')]*$)/'; return preg_replace_callback($search, static function ($matches) { return '' . str_replace(' ', ' ', $matches[0]) . ''; diff --git a/tests/system/Database/BaseQueryTest.php b/tests/system/Database/BaseQueryTest.php index deec43c4f5bb..fe942ffeb187 100644 --- a/tests/system/Database/BaseQueryTest.php +++ b/tests/system/Database/BaseQueryTest.php @@ -363,4 +363,37 @@ public function testSetQueryBinds() $this->assertSame($expected, $query->getQuery()); } + + public function queryKeywords() + { + return [ + 'highlightKeyWords' => [ + 'SELECT `a`.*, `b`.`id` AS `b_id` FROM `a` LEFT JOIN `b` ON `b`.`a_id` = `a`.`id` WHERE `b`.`id` IN ('1') AND `a`.`deleted_at` IS NOT NULL LIMIT 1', + 'SELECT `a`.*, `b`.`id` AS `b_id` FROM `a` LEFT JOIN `b` ON `b`.`a_id` = `a`.`id` WHERE `b`.`id` IN (\'1\') AND `a`.`deleted_at` IS NOT NULL LIMIT 1', + ], + 'ignoreKeyWordsInValues' => [ + 'SELECT * FROM `a` WHERE `a`.`col` = 'SELECT escaped keyword in value' LIMIT 1', + 'SELECT * FROM `a` WHERE `a`.`col` = \'SELECT escaped keyword in value\' LIMIT 1', + ], + 'escapeHtmlValues' => [ + 'SELECT '<s>' FROM dual', + 'SELECT \'\' FROM dual', + ], + ]; + } + + /** + * @dataProvider queryKeywords + * + * @param mixed $expected + * @param mixed $sql + */ + public function testHighlightQueryKeywords($expected, $sql) + { + $query = new Query($this->db); + $query->setQuery($sql); + $query->getQuery(); + + $this->assertSame($expected, $query->debugToolbarDisplay()); + } }