CVE-2022-31058: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') (CWE-89)
value: Tuleap is a Free & Open Source Suite to improve management of software developments and collaboration. In versions prior to 13.9.99.95 Tuleap does not sanitize properly user inputs when constructing the SQL query to retrieve data for the tracker reports. An attacker with the capability to create a new tracker can execute arbitrary SQL queries. Users are advised to upgrade. There is no known workaround for this issue.
CWE description: The product constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component. Without sufficient removal or quoting of SQL syntax in user-controllable inputs, the generated SQL query can cause those inputs to be interpreted as SQL instead of ordinary user data.
CVSS v3 base score: 7.2
Published: 2022-06-29T18:15Z
Repository: https://github.com/Enalean/tuleap
Language: PHP
Files changed: plugins/agiledashboard/include/AgileDashboard/FormElement/Burnup.php, plugins/testmanagement/include/TestManagement/Step/Execution/Field/StepExecution.php, plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field.php, plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Alphanum.php, plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_ArtifactId.php, plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_ArtifactLink.php, plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Burndown.php, plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Computed.php, plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_CrossReferences.php, plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Date.php, plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_File.php, plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_LastModifiedBy.php, plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_LastUpdateDate.php, plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List.php, plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List_Bind.php, plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List_Bind_Null.php, plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List_Bind_Static.php, plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List_Bind_Ugroups.php, plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List_Bind_Users.php, plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Numeric.php, plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_PerTrackerArtifactIdclass.php, plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_PermissionsOnArtifact.php, plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Priority.php, plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_SubmittedBy.php, plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_SubmittedOn.php, plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Text.php, plugins/tracker/include/Tracker/Report/Tracker_Report_Renderer_Table.class.php, plugins/tracker_encryption/include/Tracker_FormElement_Field_Encrypted.class.php
Commit date: 2022-06-09 17:52:00 +0200
Commit message: request #27166: Naming a field "id" breaks the report

Field names are now prefixed in the SQL query in order to avoid conflict
with the static SQL columns we always retrieve.

Change-Id: I7eaaf95e24f52f6e52647d679b5058bc09139b5f

Diff: diff --git a/plugins/agiledashboard/include/AgileDashboard/FormElement/Burnup.php b/plugins/agiledashboard/include/AgileDashboard/FormElement/Burnup.php
index ee7bf815777d..ae4aef5b06b8 100644
--- a/plugins/agiledashboard/include/AgileDashboard/FormElement/Burnup.php
+++ b/plugins/agiledashboard/include/AgileDashboard/FormElement/Burnup.php
@@ -283,12 +283,13 @@ class Burnup extends Tracker_FormElement_Field implements Tracker_FormElement_Fi
 
     public function getQueryFrom()
     {
     }
 
-    public function getQuerySelect()
+    public function getQuerySelect(): string
     {
+        return '';
     }
 
     public function getRESTValue(PFUser $user, Tracker_Artifact_Changeset $changeset)
     {
         $artifact = $changeset->getArtifact();
diff --git a/plugins/testmanagement/include/TestManagement/Step/Execution/Field/StepExecution.php b/plugins/testmanagement/include/TestManagement/Step/Execution/Field/StepExecution.php
index 68bdd67825d3..d8ac49852c5b 100644
--- a/plugins/testmanagement/include/TestManagement/Step/Execution/Field/StepExecution.php
+++ b/plugins/testmanagement/include/TestManagement/Step/Execution/Field/StepExecution.php
@@ -121,11 +121,11 @@ class StepExecution extends Tracker_FormElement_Field implements TrackerFormElem
     public function getCriteriaWhere($criteria)
     {
         return '';
     }
 
-    public function getQuerySelect()
+    public function getQuerySelect(): string
     {
         return '';
     }
 
     protected function getCriteriaDao()
diff --git a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field.php b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field.php
index ab101cde2cb0..4f1495185fd0 100644
--- a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field.php
+++ b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field.php
@@ -39,10 +39,12 @@ use Tuleap\Tracker\Workflow\SimpleMode\State\TransitionRetriever;
  */
 
 // phpcs:ignore PSR1.Classes.ClassDeclaration.MissingNamespace, Squiz.Classes.ValidClassName.NotCamelCaps
 abstract class Tracker_FormElement_Field extends Tracker_FormElement implements Tracker_Report_Field, Tracker_FormElement_IAcceptFieldVisitor
 {
+    public const PREFIX_NAME_SQL_COLUMN = 'user_defined_';
+
     protected $has_errors = false;
 
     /**
      * Display the field value as a criteria
      * @return string
@@ -201,19 +203,23 @@ abstract class Tracker_FormElement_Field extends Tracker_FormElement implements
 
         $value = $cache->get($criteria->getReport()->getId());
         $this->updateCriteriaValue($criteria, $value);
     }
 
+    final public function getQuerySelectName(): string
+    {
+        return \Tuleap\DB\DBFactory::getMainTuleapDBConnection()->getDB()->escapeIdentifier(self::PREFIX_NAME_SQL_COLUMN . $this->name);
+    }
+
     /**
      * Get the "select" statement to retrieve field values
-     * @return string
      * @see getQueryFrom
      */
-    public function getQuerySelect()
+    public function getQuerySelect(): string
     {
         $R = 'R_' . $this->id;
-        return "$R.value_id AS `" . $this->name . "`";
+        return "$R.value_id AS " . $this->getQuerySelectName();
     }
 
     /**
      * Get the "from" statement to retrieve field values
      * You can join on artifact AS a, tracker_changeset AS c
@@ -227,19 +233,19 @@ abstract class Tracker_FormElement_Field extends Tracker_FormElement implements
     }
 
     /**
      * Get the "order by" statement to retrieve field values
      */
-    public function getQueryOrderby()
+    public function getQueryOrderby(): string
     {
-        return '`' . $this->name . '`';
+        return $this->getQuerySelectName();
     }
 
     /**
      * Get the "group by" statement to retrieve field values
      */
-    public function getQueryGroupby()
+    public function getQueryGroupby(): string
     {
         $R = 'R_' . $this->id;
         return "$R.value_id";
     }
 
diff --git a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Alphanum.php b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Alphanum.php
index 294982606e46..de539c140252 100644
--- a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Alphanum.php
+++ b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Alphanum.php
@@ -68,13 +68,12 @@ abstract class Tracker_FormElement_Field_Alphanum extends Tracker_FormElement_Fi
     }
 
     /**
      * Get the "group by" statement to retrieve field values
      */
-    public function getQueryGroupby()
+    public function getQueryGroupby(): string
     {
-        $R1 = 'R1_' . $this->id;
         $R2 = 'R2_' . $this->id;
         return "$R2.value";
     }
 
     /**
diff --git a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_ArtifactId.php b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_ArtifactId.php
index d687db0084af..e66f22e8995b 100644
--- a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_ArtifactId.php
+++ b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_ArtifactId.php
@@ -36,24 +36,24 @@ class Tracker_FormElement_Field_ArtifactId extends Tracker_FormElement_Field_Int
             return $this->buildMatchExpression("c.artifact_id", $criteria_value);
         }
         return '';
     }
 
-    public function getQuerySelect()
+    public function getQuerySelect(): string
     {
-        return "a.id AS `" . $this->name . "`";
+        return "a.id AS " . $this->getQuerySelectName();
     }
 
     public function getQueryFrom()
     {
         return '';
     }
 
     /**
      * Get the "group by" statement to retrieve field values
      */
-    public function getQueryGroupby()
+    public function getQueryGroupby(): string
     {
         return "a.id";
     }
 
     public function getFullRESTValue(PFUser $user, Tracker_Artifact_Changeset $changeset)
diff --git a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_ArtifactLink.php b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_ArtifactLink.php
index 67bf8dd9ff3e..c15dfb125e61 100644
--- a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_ArtifactLink.php
+++ b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_ArtifactLink.php
@@ -416,11 +416,11 @@ class Tracker_FormElement_Field_ArtifactLink extends Tracker_FormElement_Field
     public function getCriteriaWhere($criteria)
     {
         return '';
     }
 
-    public function getQuerySelect()
+    public function getQuerySelect(): string
     {
         return '';
     }
 
     public function getQueryFrom()
diff --git a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Burndown.php b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Burndown.php
index 3819e5e0953b..7dc00cab50e4 100644
--- a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Burndown.php
+++ b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Burndown.php
@@ -445,12 +445,13 @@ class Tracker_FormElement_Field_Burndown extends Tracker_FormElement_Field imple
 
     public function getCriteriaWhere($criteria)
     {
     }
 
-    public function getQuerySelect()
+    public function getQuerySelect(): string
     {
+        return '';
     }
 
     public function getQueryFrom()
     {
     }
diff --git a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Computed.php b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Computed.php
index ff7a520fa36c..174f4d39e3a0 100644
--- a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Computed.php
+++ b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Computed.php
@@ -625,11 +625,11 @@ class Tracker_FormElement_Field_Computed extends Tracker_FormElement_Field_Float
     public function getCriteriaWhere($criteria)
     {
         return '';
     }
 
-    public function getQuerySelect()
+    public function getQuerySelect(): string
     {
         return '';
     }
 
     public function getQueryFrom()
diff --git a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_CrossReferences.php b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_CrossReferences.php
index a04850d9d167..347a8cfde2a0 100644
--- a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_CrossReferences.php
+++ b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_CrossReferences.php
@@ -137,11 +137,11 @@ class Tracker_FormElement_Field_CrossReferences extends Tracker_FormElement_Fiel
     public function getCriteriaWhere($criteria)
     {
         return '';
     }
 
-    public function getQuerySelect()
+    public function getQuerySelect(): string
     {
         return '';
     }
 
     public function getQueryFrom()
diff --git a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Date.php b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Date.php
index 9e1a10718181..8491dfcb1051 100644
--- a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Date.php
+++ b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Date.php
@@ -411,15 +411,14 @@ class Tracker_FormElement_Field_Date extends Tracker_FormElement_Field
     public function getCriteriaWhere($criteria)
     {
         return '';
     }
 
-    public function getQuerySelect()
+    public function getQuerySelect(): string
     {
-        $R1 = 'R1_' . $this->id;
         $R2 = 'R2_' . $this->id;
-        return "$R2.value AS `" . $this->name . "`";
+        return "$R2.value AS " . $this->getQuerySelectName();
     }
 
     public function getQueryFrom()
     {
         $R1 = 'R1_' . $this->id;
@@ -430,13 +429,12 @@ class Tracker_FormElement_Field_Date extends Tracker_FormElement_Field
                 ) ON ($R1.changeset_id = c.id AND $R1.field_id = " . $this->id . " )";
     }
     /**
      * Get the "group by" statement to retrieve field values
      */
-    public function getQueryGroupby()
+    public function getQueryGroupby(): string
     {
-        $R1 = 'R1_' . $this->id;
         $R2 = 'R2_' . $this->id;
         return "$R2.value";
     }
 
     protected function getCriteriaDao()
diff --git a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_File.php b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_File.php
index 1cc4eb055eec..8a90c3332c40 100644
--- a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_File.php
+++ b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_File.php
@@ -67,11 +67,11 @@ class Tracker_FormElement_Field_File extends Tracker_FormElement_Field
     public function getCriteriaWhere($criteria)
     {
         return '';
     }
 
-    public function getQuerySelect()
+    public function getQuerySelect(): string
     {
         return '';
     }
 
     public function getQueryFrom()
diff --git a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_LastModifiedBy.php b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_LastModifiedBy.php
index 72b2563811b7..89d42a013aed 100644
--- a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_LastModifiedBy.php
+++ b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_LastModifiedBy.php
@@ -65,13 +65,13 @@ class Tracker_FormElement_Field_LastModifiedBy extends Tracker_FormElement_Field
             }
         }
         return '';
     }
 
-    public function getQuerySelect()
+    public function getQuerySelect(): string
     {
-        return "c.submitted_by AS `" . $this->name . "`";
+        return "c.submitted_by AS " . $this->getQuerySelectName();
     }
 
     public function getQueryFrom()
     {
         return '';
@@ -82,20 +82,15 @@ class Tracker_FormElement_Field_LastModifiedBy extends Tracker_FormElement_Field
         $R1 = 'R1_' . $this->id;
         $R2 = 'R2_' . $this->id;
         return " LEFT JOIN  user AS $R2 ON ($R2.user_id = c.submitted_by ) ";
     }
 
-    public function getQueryGroupby()
+    public function getQueryGroupby(): string
     {
         return '';
     }
 
-    public function getQueryOrderby()
-    {
-        return $this->name;
-    }
-
     public static function getFactoryLabel()
     {
         return dgettext('tuleap-tracker', 'Last Updated By');
     }
 
diff --git a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_LastUpdateDate.php b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_LastUpdateDate.php
index a7ffadb3334c..7d8836dc7a27 100644
--- a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_LastUpdateDate.php
+++ b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_LastUpdateDate.php
@@ -66,14 +66,14 @@ class Tracker_FormElement_Field_LastUpdateDate extends Tracker_FormElement_Field
         }
 
         return '';
     }
 
-    public function getQuerySelect()
+    public function getQuerySelect(): string
     {
         //Last update date is stored in the changeset (the date of the changeset)
-        return "c.submitted_on AS `" . $this->name . "`";
+        return "c.submitted_on AS " . $this->getQuerySelectName();
     }
 
     public function getQueryFrom()
     {
         //Last update date is stored in the changeset (the date of the changeset)
@@ -81,11 +81,11 @@ class Tracker_FormElement_Field_LastUpdateDate extends Tracker_FormElement_Field
     }
 
     /**
      * Get the "group by" statement to retrieve field values
      */
-    public function getQueryGroupby()
+    public function getQueryGroupby(): string
     {
         //Last update date is stored in the changeset (the date of the changeset)
         return 'c.submitted_on';
     }
 
diff --git a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List.php b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List.php
index 7d0619d35b25..33bb520cc1eb 100644
--- a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List.php
+++ b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List.php
@@ -160,13 +160,12 @@ abstract class Tracker_FormElement_Field_List extends Tracker_FormElement_Field
     /**
      * Get the "select" statement to retrieve field values
      *
      * @see getQueryFrom
      *
-     * @return string
      */
-    public function getQuerySelect()
+    public function getQuerySelect(): string
     {
         return $this->getBind()->getQuerySelect();
     }
 
     /**
@@ -203,19 +202,19 @@ abstract class Tracker_FormElement_Field_List extends Tracker_FormElement_Field
     }
 
     /**
      * Get the "order by" statement to retrieve field values
      */
-    public function getQueryOrderby()
+    public function getQueryOrderby(): string
     {
         return $this->getBind()->getQueryOrderby();
     }
 
     /**
      * Get the "group by" statement to retrieve field values
      */
-    public function getQueryGroupby()
+    public function getQueryGroupby(): string
     {
         return $this->getBind()->getQueryGroupby();
     }
 
     /**
diff --git a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List_Bind.php b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List_Bind.php
index 26a0579ad5c8..4271891872d8 100644
--- a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List_Bind.php
+++ b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List_Bind.php
@@ -324,14 +324,13 @@ abstract class Tracker_FormElement_Field_List_Bind implements //phpcs:ignore PSR
         );
     }
 
     /**
      * Get the "select" statement to retrieve field values
-     * @return string
      * @see getQueryFrom
      */
-    abstract public function getQuerySelect();
+    abstract public function getQuerySelect(): string;
 
     /**
      * Get the "select" statement to retrieve field values with their decorator if they exist
      * @return string
      * @see getQuerySelect
@@ -391,16 +390,16 @@ abstract class Tracker_FormElement_Field_List_Bind implements //phpcs:ignore PSR
     abstract public function getBindtableSqlFragment();
 
     /**
      * Get the "order by" statement to retrieve field values
      */
-    abstract public function getQueryOrderby();
+    abstract public function getQueryOrderby(): string;
 
     /**
      * Get the "group by" statement to retrieve field values
      */
-    abstract public function getQueryGroupby();
+    abstract public function getQueryGroupby(): string;
 
     public function getSelectOptionStyles($value_id)
     {
         $default_styles = ['classes' => '', 'inline-styles' => ''];
 
diff --git a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List_Bind_Null.php b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List_Bind_Null.php
index 4b6e50a619a3..fbbd9ada49da 100644
--- a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List_Bind_Null.php
+++ b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List_Bind_Null.php
@@ -148,14 +148,13 @@ class Tracker_FormElement_Field_List_Bind_Null extends Tracker_FormElement_Field
         return '';
     }
 
     /**
      * Get the "select" statement to retrieve field values
-     * @return string
      * @see getQueryFrom
      */
-    public function getQuerySelect()
+    public function getQuerySelect(): string
     {
         return '';
     }
 
 
@@ -203,19 +202,19 @@ class Tracker_FormElement_Field_List_Bind_Null extends Tracker_FormElement_Field
     }
 
     /**
      * Get the "order by" statement to retrieve field values
      */
-    public function getQueryOrderby()
+    public function getQueryOrderby(): string
     {
         return '';
     }
 
     /**
      * Get the "group by" statement to retrieve field values
      */
-    public function getQueryGroupby()
+    public function getQueryGroupby(): string
     {
         return '';
     }
 
 
diff --git a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List_Bind_Static.php b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List_Bind_Static.php
index a6f551fe78bc..a2fc8cc8c65d 100644
--- a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List_Bind_Static.php
+++ b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List_Bind_Static.php
@@ -272,18 +272,16 @@ class Tracker_FormElement_Field_List_Bind_Static extends Tracker_FormElement_Fie
         return array_values($criteria_value);
     }
 
     /**
      * Get the "select" statement to retrieve field values
-     * @return string
      * @see getQueryFrom
      */
-    public function getQuerySelect()
+    public function getQuerySelect(): string
     {
-        $R1 = 'R1_' . $this->field->id;
         $R2 = 'R2_' . $this->field->id;
-        return "$R2.id AS `" . $this->field->name . "`";
+        return "$R2.id AS " . $this->field->getQuerySelectName();
     }
 
     /**
      * Get the "select" statement to retrieve field values with the RGB values of their decorator
      * @return string
@@ -332,23 +330,21 @@ class Tracker_FormElement_Field_List_Bind_Static extends Tracker_FormElement_Fie
     }
 
     /**
      * Get the "order by" statement to retrieve field values
      */
-    public function getQueryOrderby()
+    public function getQueryOrderby(): string
     {
-        $R1 = 'R1_' . $this->field->id;
         $R2 = 'R2_' . $this->field->id;
         return $this->is_rank_alpha ? "$R2.label" : "$R2.rank";
     }
 
     /**
      * Get the "group by" statement to retrieve field values
      */
-    public function getQueryGroupby()
+    public function getQueryGroupby(): string
     {
-        $R1 = 'R1_' . $this->field->id;
         $R2 = 'R2_' . $this->field->id;
         return "$R2.id";
     }
 
     /**
diff --git a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List_Bind_Ugroups.php b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List_Bind_Ugroups.php
index e49f218f8810..c7fa046d057a 100644
--- a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List_Bind_Ugroups.php
+++ b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List_Bind_Ugroups.php
@@ -223,19 +223,16 @@ class Tracker_FormElement_Field_List_Bind_Ugroups extends Tracker_FormElement_Fi
         }
     }
 
     /**
      * Get the "select" statement to retrieve field values
-     * @return string
      * @see getQueryFrom
      */
-    public function getQuerySelect()
+    public function getQuerySelect(): string
     {
-        $R1 = 'R1_' . $this->field->id;
         $R2 = 'R2_' . $this->field->id;
-        $R3 = 'R3_' . $this->field->id;
-        return "$R2.id AS `" . $this->field->name . "`";
+        return "$R2.id AS " . $this->field->getQuerySelectName();
     }
 
     /**
      * Get the "from" statement to retrieve field values
      * You can join on artifact AS a, tracker_changeset AS c
@@ -263,24 +260,21 @@ class Tracker_FormElement_Field_List_Bind_Ugroups extends Tracker_FormElement_Fi
     }
 
     /**
      * Get the "order by" statement to retrieve field values
      */
-    public function getQueryOrderby()
+    public function getQueryOrderby(): string
     {
-        $uh = UserHelper::instance();
-        $R1 = 'R1_' . $this->field->id;
         $R2 = 'R2_' . $this->field->id;
         return "$R2.ugroup_id";
     }
 
     /**
      * Get the "group by" statement to retrieve field values
      */
-    public function getQueryGroupby()
+    public function getQueryGroupby(): string
     {
-        $R1 = 'R1_' . $this->field->id;
         $R2 = 'R2_' . $this->field->id;
         return "$R2.id";
     }
 
     /**
diff --git a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List_Bind_Users.php b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List_Bind_Users.php
index 9840793a7419..290451c1ce15 100644
--- a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List_Bind_Users.php
+++ b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_List_Bind_Users.php
@@ -416,19 +416,16 @@ class Tracker_FormElement_Field_List_Bind_Users extends Tracker_FormElement_Fiel
         }
     }
 
     /**
      * Get the "select" statement to retrieve field values
-     * @return string
      * @see getQueryFrom
      */
-    public function getQuerySelect()
+    public function getQuerySelect(): string
     {
-        $R1 = 'R1_' . $this->field->id;
         $R2 = 'R2_' . $this->field->id;
-        $R3 = 'R3_' . $this->field->id;
-        return "$R2.user_id AS `" . $this->field->name . "`";
+        return "$R2.user_id AS " . $this->field->getQuerySelectName();
     }
 
     /**
      * Get the "from" statement to retrieve field values
      * You can join on artifact AS a, tracker_changeset AS c
@@ -450,24 +447,22 @@ class Tracker_FormElement_Field_List_Bind_Users extends Tracker_FormElement_Fiel
     }
 
     /**
      * Get the "order by" statement to retrieve field values
      */
-    public function getQueryOrderby()
+    public function getQueryOrderby(): string
     {
         $uh = UserHelper::instance();
-        $R1 = 'R1_' . $this->field->id;
         $R2 = 'R2_' . $this->field->id;
         return $R2 . "." . str_replace('user.', '', $uh->getDisplayNameSQLOrder());
     }
 
     /**
      * Get the "group by" statement to retrieve field values
      */
-    public function getQueryGroupby()
+    public function getQueryGroupby(): string
     {
-        $R1 = 'R1_' . $this->field->id;
         $R2 = 'R2_' . $this->field->id;
         return "$R2.user_id";
     }
 
     /**
diff --git a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Numeric.php b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Numeric.php
index 87e41960fd27..0ab314693bfa 100644
--- a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Numeric.php
+++ b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Numeric.php
@@ -82,15 +82,14 @@ abstract class Tracker_FormElement_Field_Numeric extends Tracker_FormElement_Fie
             return $row['value'];
         }
         return 0;
     }
 
-    public function getQuerySelect()
+    public function getQuerySelect(): string
     {
-        $R1 = 'R1_' . $this->id;
         $R2 = 'R2_' . $this->id;
-        return "$R2.value AS `" . $this->name . "`";
+        return "$R2.value AS " . $this->getQuerySelectName();
     }
 
     /**
      * Fetch sql snippets needed to compute aggregate functions on this field.
      *
diff --git a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_PerTrackerArtifactIdclass.php b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_PerTrackerArtifactIdclass.php
index 62fa994c8b3e..e85d8d62b2b9 100644
--- a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_PerTrackerArtifactIdclass.php
+++ b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_PerTrackerArtifactIdclass.php
@@ -33,19 +33,19 @@ class Tracker_FormElement_Field_PerTrackerArtifactId extends Tracker_FormElement
             return $this->buildMatchExpression("artifact.per_tracker_artifact_id", $criteria_value);
         }
         return '';
     }
 
-    public function getQuerySelect()
+    public function getQuerySelect(): string
     {
-        return "a.per_tracker_artifact_id AS `" . $this->name . "`";
+        return "a.per_tracker_artifact_id AS " . $this->getQuerySelectName();
     }
 
     /**
      * Get the "group by" statement to retrieve field values
      */
-    public function getQueryGroupby()
+    public function getQueryGroupby(): string
     {
         return "a.per_tracker_artifact_id";
     }
 
     public function fetchChangesetValue($artifact_id, $changeset_id, $value, $report = null, $from_aid = null)
diff --git a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_PermissionsOnArtifact.php b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_PermissionsOnArtifact.php
index 6d1ef95d15e9..e3536843be44 100644
--- a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_PermissionsOnArtifact.php
+++ b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_PermissionsOnArtifact.php
@@ -442,14 +442,13 @@ class Tracker_FormElement_Field_PermissionsOnArtifact extends Tracker_FormElemen
     {
         return '';
     }
 
      /**
-     * @return string
      * @see getQueryFrom
      */
-    public function getQuerySelect()
+    public function getQuerySelect(): string
     {
         return '';
     }
 
     /**
diff --git a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Priority.php b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Priority.php
index eee49d8a238f..46c663edac77 100644
--- a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Priority.php
+++ b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Priority.php
@@ -89,16 +89,15 @@ class Tracker_FormElement_Field_Priority extends Tracker_FormElement_Field_Integ
         return $result;
     }
 
     /**
      * Get the "select" statement to retrieve field values
-     * @return string
      * @see getQueryFrom
      */
-    public function getQuerySelect()
+    public function getQuerySelect(): string
     {
-        return "R_{$this->id}.rank AS `$this->name`";
+        return "R_{$this->id}.rank AS " . $this->getQuerySelectName();
     }
 
     /**
      * Get the "from" statement to retrieve field values
      * You can join on artifact AS a, tracker_changeset AS c
diff --git a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_SubmittedBy.php b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_SubmittedBy.php
index 3a7291e27a05..8a92d91a53d7 100644
--- a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_SubmittedBy.php
+++ b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_SubmittedBy.php
@@ -62,14 +62,14 @@ class Tracker_FormElement_Field_SubmittedBy extends Tracker_FormElement_Field_Li
             }
         }
         return '';
     }
 
-    public function getQuerySelect()
+    public function getQuerySelect(): string
     {
         // SubmittedOn is stored in the artifact
-        return "a.submitted_by AS `" . $this->name . "`";
+        return "a.submitted_by AS " . $this->getQuerySelectName();
     }
 
     public function getQueryFrom()
     {
         // SubmittedOn is stored in the artifact
@@ -83,22 +83,22 @@ class Tracker_FormElement_Field_SubmittedBy extends Tracker_FormElement_Field_Li
     }
 
     /**
      * Get the "group by" statement to retrieve field values
      */
-    public function getQueryGroupby()
+    public function getQueryGroupby(): string
     {
         // SubmittedOn is stored in the artifact
         return 'a.submitted_by';
     }
 
     /**
      * Get the "order by" statement to retrieve field values
      */
-    public function getQueryOrderby()
+    public function getQueryOrderby(): string
     {
-        return $this->name;
+        return $this->getQuerySelectName();
     }
 
     public static function getFactoryLabel()
     {
         return dgettext('tuleap-tracker', 'Submitted By');
diff --git a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_SubmittedOn.php b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_SubmittedOn.php
index 0b12706f39ae..eca47ba48e0f 100644
--- a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_SubmittedOn.php
+++ b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_SubmittedOn.php
@@ -65,14 +65,14 @@ class Tracker_FormElement_Field_SubmittedOn extends Tracker_FormElement_Field_Da
             );
         }
         return '';
     }
 
-    public function getQuerySelect()
+    public function getQuerySelect(): string
     {
         // SubmittedOn is stored in the artifact
-        return "a.submitted_on AS `" . $this->name . "`";
+        return "a.submitted_on AS " . $this->getQuerySelectName();
     }
 
     public function getQueryFrom()
     {
         // SubmittedOn is stored in the artifact
@@ -80,11 +80,11 @@ class Tracker_FormElement_Field_SubmittedOn extends Tracker_FormElement_Field_Da
     }
 
     /**
      * Get the "group by" statement to retrieve field values
      */
-    public function getQueryGroupby()
+    public function getQueryGroupby(): string
     {
         // SubmittedOn is stored in the artifact
         return 'a.submitted_on';
     }
 
diff --git a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Text.php b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Text.php
index e278ce35197f..b64ba3adf11a 100644
--- a/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Text.php
+++ b/plugins/tracker/include/Tracker/FormElement/Tracker_FormElement_Field_Text.php
@@ -81,15 +81,14 @@ class Tracker_FormElement_Field_Text extends Tracker_FormElement_Field_Alphanum
     public function getCriteriaWhere($criteria)
     {
         return '';
     }
 
-    public function getQuerySelect()
+    public function getQuerySelect(): string
     {
-        $R1 = 'R1_' . $this->id;
         $R2 = 'R2_' . $this->id;
-        return "$R2.value AS `" . $this->name . "`";
+        return "$R2.value AS " . $this->getQuerySelectName();
     }
 
     public function getQueryFrom()
     {
         $R1 = 'R1_' . $this->id;
diff --git a/plugins/tracker/include/Tracker/Report/Tracker_Report_Renderer_Table.class.php b/plugins/tracker/include/Tracker/Report/Tracker_Report_Renderer_Table.class.php
index 67c3ce71e0b8..a2e823110ec7 100644
--- a/plugins/tracker/include/Tracker/Report/Tracker_Report_Renderer_Table.class.php
+++ b/plugins/tracker/include/Tracker/Report/Tracker_Report_Renderer_Table.class.php
@@ -1211,11 +1211,11 @@ class Tracker_Report_Renderer_Table extends Tracker_Report_Renderer implements T
                         $html .= '<i class="fa fa-edit"></i>';
                         $html .= '</td>';
                     }
                     foreach ($columns as $key => $column) {
                         if ($column['field']->isUsed()) {
-                            $field_name = $column['field']->name;
+                            $field_name = Tracker_FormElement_Field::PREFIX_NAME_SQL_COLUMN . $column['field']->name;
                             $value      = isset($row[$field_name]) ? $row[$field_name] : null;
                             $html      .= '<td data-column-id="' . $purifier->purify($key) . '">';
 
                             if (isset($column['artlink_nature'])) {
                                 $html .= $column['field']->fetchChangesetValueForType(
diff --git a/plugins/tracker_encryption/include/Tracker_FormElement_Field_Encrypted.class.php b/plugins/tracker_encryption/include/Tracker_FormElement_Field_Encrypted.class.php
index 7f76b5eeec78..df23a39d4e86 100644
--- a/plugins/tracker_encryption/include/Tracker_FormElement_Field_Encrypted.class.php
+++ b/plugins/tracker_encryption/include/Tracker_FormElement_Field_Encrypted.class.php
@@ -183,15 +183,15 @@ class Tracker_FormElement_Field_Encrypted extends Tracker_FormElement_Field impl
         return "LEFT JOIN ( tracker_changeset_value AS $R1
                     INNER JOIN tracker_changeset_value_encrypted AS $R2 ON ($R2.changeset_value_id = $R1.id)
                 ) ON ($R1.changeset_id = c.id AND $R1.field_id = " . $this->id . " )";
     }
 
-    public function getQuerySelect()
+    public function getQuerySelect(): string
     {
         $R2 = 'R2_' . $this->id;
 
-        return "$R2.value AS `" . $this->name . "`";
+        return "$R2.value AS " . $this->getQuerySelectName();
     }
 
     /**
      * @param Tracker_Report_Criteria $criteria
      *

Vulnerable code:     public function getQuerySelect()
    public function getQuerySelect()
     * @return string
    public function getQuerySelect()
        return "$R.value_id AS `" . $this->name . "`";
    public function getQueryOrderby()
        return '`' . $this->name . '`';
    public function getQueryGroupby()
    public function getQueryGroupby()
        $R1 = 'R1_' . $this->id;
    public function getQuerySelect()
        return "a.id AS `" . $this->name . "`";
    public function getQueryGroupby()
    public function getQuerySelect()
    public function getQuerySelect()
    public function getQuerySelect()
    public function getQuerySelect()
    public function getQuerySelect()
        $R1 = 'R1_' . $this->id;
        return "$R2.value AS `" . $this->name . "`";
    public function getQueryGroupby()
        $R1 = 'R1_' . $this->id;
    public function getQuerySelect()
    public function getQuerySelect()
        return "c.submitted_by AS `" . $this->name . "`";
    public function getQueryGroupby()
    public function getQueryOrderby()
    {
        return $this->name;
    }

    public function getQuerySelect()
        return "c.submitted_on AS `" . $this->name . "`";
    public function getQueryGroupby()
     * @return string
    public function getQuerySelect()
    public function getQueryOrderby()
    public function getQueryGroupby()
     * @return string
    abstract public function getQuerySelect();
    abstract public function getQueryOrderby();
    abstract public function getQueryGroupby();
     * @return string
    public function getQuerySelect()
    public function getQueryOrderby()
    public function getQueryGroupby()
     * @return string
    public function getQuerySelect()
        $R1 = 'R1_' . $this->field->id;
        return "$R2.id AS `" . $this->field->name . "`";
    public function getQueryOrderby()
        $R1 = 'R1_' . $this->field->id;
    public function getQueryGroupby()
        $R1 = 'R1_' . $this->field->id;
     * @return string
    public function getQuerySelect()
        $R1 = 'R1_' . $this->field->id;
        $R3 = 'R3_' . $this->field->id;
        return "$R2.id AS `" . $this->field->name . "`";
    public function getQueryOrderby()
        $uh = UserHelper::instance();
        $R1 = 'R1_' . $this->field->id;
    public function getQueryGroupby()
        $R1 = 'R1_' . $this->field->id;
     * @return string
    public function getQuerySelect()
        $R1 = 'R1_' . $this->field->id;
        $R3 = 'R3_' . $this->field->id;
        return "$R2.user_id AS `" . $this->field->name . "`";
    public function getQueryOrderby()
        $R1 = 'R1_' . $this->field->id;
    public function getQueryGroupby()
        $R1 = 'R1_' . $this->field->id;
    public function getQuerySelect()
        $R1 = 'R1_' . $this->id;
        return "$R2.value AS `" . $this->name . "`";
    public function getQuerySelect()
        return "a.per_tracker_artifact_id AS `" . $this->name . "`";
    public function getQueryGroupby()
     * @return string
    public function getQuerySelect()
     * @return string
    public function getQuerySelect()
        return "R_{$this->id}.rank AS `$this->name`";
    public function getQuerySelect()
        return "a.submitted_by AS `" . $this->name . "`";
    public function getQueryGroupby()
    public function getQueryOrderby()
        return $this->name;
    public function getQuerySelect()
        return "a.submitted_on AS `" . $this->name . "`";
    public function getQueryGroupby()
    public function getQuerySelect()
        $R1 = 'R1_' . $this->id;
        return "$R2.value AS `" . $this->name . "`";
                            $field_name = $column['field']->name;
    public function getQuerySelect()
        return "$R2.value AS `" . $this->name . "`";
Fixed code:     public function getQuerySelect(): string
        return '';
    public function getQuerySelect(): string
    public const PREFIX_NAME_SQL_COLUMN = 'user_defined_';

    final public function getQuerySelectName(): string
    {
        return \Tuleap\DB\DBFactory::getMainTuleapDBConnection()->getDB()->escapeIdentifier(self::PREFIX_NAME_SQL_COLUMN . $this->name);
    }

    public function getQuerySelect(): string
        return "$R.value_id AS " . $this->getQuerySelectName();
    public function getQueryOrderby(): string
        return $this->getQuerySelectName();
    public function getQueryGroupby(): string
    public function getQueryGroupby(): string
    public function getQuerySelect(): string
        return "a.id AS " . $this->getQuerySelectName();
    public function getQueryGroupby(): string
    public function getQuerySelect(): string
    public function getQuerySelect(): string
        return '';
    public function getQuerySelect(): string
    public function getQuerySelect(): string
    public function getQuerySelect(): string
        return "$R2.value AS " . $this->getQuerySelectName();
    public function getQueryGroupby(): string
    public function getQuerySelect(): string
    public function getQuerySelect(): string
        return "c.submitted_by AS " . $this->getQuerySelectName();
    public function getQueryGroupby(): string
    public function getQuerySelect(): string
        return "c.submitted_on AS " . $this->getQuerySelectName();
    public function getQueryGroupby(): string
    public function getQuerySelect(): string
    public function getQueryOrderby(): string
    public function getQueryGroupby(): string
    abstract public function getQuerySelect(): string;
    abstract public function getQueryOrderby(): string;
    abstract public function getQueryGroupby(): string;
    public function getQuerySelect(): string
    public function getQueryOrderby(): string
    public function getQueryGroupby(): string
    public function getQuerySelect(): string
        return "$R2.id AS " . $this->field->getQuerySelectName();
    public function getQueryOrderby(): string
    public function getQueryGroupby(): string
    public function getQuerySelect(): string
        return "$R2.id AS " . $this->field->getQuerySelectName();
    public function getQueryOrderby(): string
    public function getQueryGroupby(): string
    public function getQuerySelect(): string
        return "$R2.user_id AS " . $this->field->getQuerySelectName();
    public function getQueryOrderby(): string
    public function getQueryGroupby(): string
    public function getQuerySelect(): string
        return "$R2.value AS " . $this->getQuerySelectName();
    public function getQuerySelect(): string
        return "a.per_tracker_artifact_id AS " . $this->getQuerySelectName();
    public function getQueryGroupby(): string
    public function getQuerySelect(): string
    public function getQuerySelect(): string
        return "R_{$this->id}.rank AS " . $this->getQuerySelectName();
    public function getQuerySelect(): string
        return "a.submitted_by AS " . $this->getQuerySelectName();
    public function getQueryGroupby(): string
    public function getQueryOrderby(): string
        return $this->getQuerySelectName();
    public function getQuerySelect(): string
        return "a.submitted_on AS " . $this->getQuerySelectName();
    public function getQueryGroupby(): string
    public function getQuerySelect(): string
        return "$R2.value AS " . $this->getQuerySelectName();
                            $field_name = Tracker_FormElement_Field::PREFIX_NAME_SQL_COLUMN . $column['field']->name;
    public function getQuerySelect(): string
        return "$R2.value AS " . $this->getQuerySelectName();