diff --git a/classes/RequestSql.php b/classes/RequestSql.php index cb4ee0ef8..087044308 100644 --- a/classes/RequestSql.php +++ b/classes/RequestSql.php @@ -227,8 +227,20 @@ class RequestSqlCore extends ObjectModel if (in_array($attr['expr_type'], array('operator', 'const'))) { continue; } - if ($attribut = $this->cutAttribute($attr['base_expr'], $from)) { - $tab[] = $attribut; + + if (!empty($attr['sub_tree'])) { + foreach ($attr['sub_tree'] as $tree_item) { + if ($tree_item['expr_type'] !== 'colref') { + continue; + } + if ($attribut = $this->cutAttribute($tree_item['base_expr'], $from)) { + $tab[] = $attribut; + } + } + } else { + if ($attribut = $this->cutAttribute($attr['base_expr'], $from)) { + $tab[] = $attribut; + } } } @@ -279,16 +291,25 @@ class RequestSqlCore extends ObjectModel { if ($alias) { foreach ($tables as $table) { - if (isset($table['alias']) && isset($table['table']) && $table['alias']['no_quotes'] == $alias) { - return array($table['table']); + if (!isset($table['alias'])) { + continue; } + $table_alias = $table['alias']['no_quotes']; + if ($table_alias != $alias && !(is_array($table_alias) && $table_alias['parts'][0] == $alias)) { + continue; + } + if (!isset($table['table'])) { + // derived table (subquery) - no real table name to verify columns against + return array(false); + } + return array($table['table']); } } elseif (count($tables) > 1) { if ($attr !== null) { $tab = array(); foreach ($tables as $table) { if ($this->attributExistInTable($attr, $table['table'])) { - $tab = $table['table']; + $tab[] = $table['table']; } } if (count($tab) == 1) { @@ -322,6 +343,10 @@ class RequestSqlCore extends ObjectModel if (is_array($table) && (count($table) == 1)) { $table = $table[0]; } + if (!$table) { + // derived table (subquery) - no real table to verify against, assume valid + return true; + } $attributs = $this->getAttributesByTable($table); foreach ($attributs as $attribut) { if ($attribut['Field'] == trim($attr, ' `')) { @@ -382,22 +407,14 @@ class RequestSqlCore extends ObjectModel return false; } if ($table['ref_type'] == 'ON' && (trim($table['join_type']) == 'LEFT' || trim($table['join_type']) == 'JOIN')) { - if ($attrs = $this->cutJoin($table['ref_clause'], $from)) { - foreach ($attrs as $attr) { - if (!$this->attributExistInTable($attr['attribut'], $attr['table'])) { - $this->error_sql['checkedFrom']['attribut'] = array($attr['attribut'], implode(', ', $attr['table'])); - return false; - } - } - } else { - if (isset($this->error_sql['returnNameTable'])) { - $this->error_sql['checkedFrom'] = $this->error_sql['returnNameTable']; - return false; - } else { - $this->error_sql['checkedFrom'] = false; + $attrs = $this->cutJoin($table['ref_clause'], $from); + foreach ($attrs as $attr) { + if (!$this->attributExistInTable($attr['attribut'], $attr['table'])) { + $this->error_sql['checkedFrom']['attribut'] = array($attr['attribut'], implode(', ', $attr['table'])); return false; } } + // derived-table joins (subquery as table) can't be resolved by cutJoin; nothing to check, not an error } } return true; @@ -416,7 +433,7 @@ class RequestSqlCore extends ObjectModel $nb = count($select); for ($i = 0; $i < $nb; $i++) { $attribut = $select[$i]; - if ($attribut['base_expr'] != '*' && !preg_match('/\.$/', $attribut['base_expr'])) { + if ($attribut['base_expr'] != '*' && !preg_match('/\.\*$/', $attribut['base_expr'])) { if ($attribut['expr_type'] == 'colref') { if ($attr = $this->cutAttribute(trim($attribut['base_expr']), $from)) { if (!$this->attributExistInTable($attr['attribut'], $attr['table'])) { @@ -433,6 +450,14 @@ class RequestSqlCore extends ObjectModel } } } + + while (is_array($attribut['sub_tree']) && isset($attribut['sub_tree'][0])) { + if ($attribut['expr_type'] === 'function' && in_array(strtoupper($attribut['base_expr']), $this->tested['unauthorized'])) { + $this->error_sql['checkedSelect']['function'] = $attribut['base_expr']; + return false; + } + $attribut = $attribut['sub_tree'][0]; + } } elseif ($in) { $this->error_sql['checkedSelect']['*'] = false; return false; @@ -454,7 +479,7 @@ class RequestSqlCore extends ObjectModel $nb = count($where); for ($i = 0; $i < $nb; $i++) { $attribut = $where[$i]; - if ($attribut['expr_type'] == 'colref' || $attribut['expr_type'] == 'reserved') { + if ($attribut['expr_type'] == 'colref') { if ($attr = $this->cutAttribute(trim($attribut['base_expr']), $from)) { if (!$this->attributExistInTable($attr['attribut'], $attr['table'])) { $this->error_sql['checkedWhere']['attribut'] = array($attr['attribut'], implode(', ', $attr['table'])); @@ -469,6 +494,11 @@ class RequestSqlCore extends ObjectModel return false; } } + } elseif ($attribut['expr_type'] == 'reserved') { + if ($attribut['base_expr'] !== 'EXISTS' || !isset($where[$i + 1]) || $where[$i + 1]['expr_type'] !== 'subquery') { + $this->error_sql['checkedWhere'] = isset($this->error_sql['returnNameTable']) ? $this->error_sql['returnNameTable'] : false; + return false; + } } elseif ($attribut['expr_type'] == 'operator') { if (!in_array(strtoupper($attribut['base_expr']), $this->tested['operator'])) { $this->error_sql['checkedWhere']['operator'] = array($attribut['base_expr']); @@ -531,7 +561,7 @@ class RequestSqlCore extends ObjectModel public function checkedOrder($order, $from) { $order = $order[0]; - if ($order['type'] == 'expression') { + if ($order['expr_type'] == 'colref') { if ($attr = $this->cutAttribute(trim($order['base_expr']), $from)) { if (!$this->attributExistInTable($attr['attribut'], $attr['table'])) { $this->error_sql['checkedOrder']['attribut'] = array($attr['attribut'], implode(', ', $attr['table'])); @@ -560,7 +590,7 @@ class RequestSqlCore extends ObjectModel public function checkedGroupBy($group, $from) { $group = $group[0]; - if ($group['type'] == 'expression') { + if ($group['expr_type'] == 'colref') { if ($attr = $this->cutAttribute(trim($group['base_expr']), $from)) { if (!$this->attributExistInTable($attr['attribut'], $attr['table'])) { $this->error_sql['checkedGroupBy']['attribut'] = array($attr['attribut'], implode(', ', $attr['table'])); @@ -587,7 +617,7 @@ class RequestSqlCore extends ObjectModel */ public function checkedLimit($limit) { - if (!preg_match('#^[0-9]+$#', trim($limit['start'])) || !preg_match('#^[0-9]+$#', trim($limit['end']))) { + if (!preg_match('#^[0-9]+$#', trim($limit['offset'])) || !preg_match('#^[0-9]+$#', trim($limit['rowcount']))) { $this->error_sql['checkedLimit'] = false; return false; }