CVE-2021-41148: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') (CWE-89)
value: Tuleap Open ALM is a libre and open source tool for end to end traceability of application and system developments. Prior to version 11.16.99.173 of Community Edition and versions 11.16-6 and 11.15-8 of Enterprise Edition, an attacker with the ability to add one the CI widget to its personal dashboard could execute arbitrary SQL queries. Tuleap Community Edition 11.16.99.173, Tuleap Enterprise Edition 11.16-6, and Tuleap Enterprise Edition 11.15-8 contain a patch 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: 8.8
Published: 2021-10-15T14:15Z
Repository: https://github.com/Enalean/tuleap
Language: PHP
Files changed: plugins/hudson/include/HudsonJobWidget.class.php
Commit date: 2020-06-26 15:53:36 +0200
Commit message: request #15028: The update of the CI job targeted by a widget is vulnerable to blind SQL injections

Change-Id: Ib02a01586740b990feb675a8728a006ea20f7777

Diff: diff --git a/plugins/hudson/include/HudsonJobWidget.class.php b/plugins/hudson/include/HudsonJobWidget.class.php
index f2c1d33b5c6..bd3f51b31c5 100644
--- a/plugins/hudson/include/HudsonJobWidget.class.php
+++ b/plugins/hudson/include/HudsonJobWidget.class.php
@@ -40,21 +40,33 @@ abstract class HudsonJobWidget extends HudsonWidget
         $vId = new Valid_UInt($this->widget_id . '_job_id');
         $vId->setErrorMessage("Can't add empty job id");
         $vId->required();
         if ($request->valid($vId)) {
             $job_id = $request->get($this->widget_id . '_job_id');
-            $sql = 'INSERT INTO plugin_hudson_widget (widget_name, owner_id, owner_type, job_id) VALUES ("' . $this->id . '", ' . $this->owner_id . ", '" . $this->owner_type . "', " . db_escape_int($job_id) . " )";
-            $res = db_query($sql);
-            $content_id = db_insertid($res);
+            $db = \Tuleap\DB\DBFactory::getMainTuleapDBConnection()->getDB();
+            $content_id = (int) $db->insertReturnId(
+                'plugin_hudson_widget',
+                [
+                    'widget_name' => $this->id,
+                    'owner_id'    => $this->owner_id,
+                    'owner_type'  => $this->owner_type,
+                    'job_id'      => $job_id
+                ]
+            );
         }
         return $content_id;
     }
 
     public function destroy($id)
     {
-        $sql = 'DELETE FROM plugin_hudson_widget WHERE id = ' . $id . ' AND owner_id = ' . $this->owner_id . " AND owner_type = '" . $this->owner_type . "'";
-        db_query($sql);
+        $db = \Tuleap\DB\DBFactory::getMainTuleapDBConnection()->getDB();
+        $db->run(
+            'DELETE FROM plugin_hudson_widget WHERE id = ? AND owner_id = ? AND owner_type = ?',
+            $id,
+            $this->owner_id,
+            $this->owner_type,
+        );
     }
 
     public function getPreferences($widget_id)
     {
         $select_id = 'job-' . (int) $widget_id;
@@ -118,12 +130,18 @@ abstract class HudsonJobWidget extends HudsonWidget
     public function updatePreferences(Codendi_Request $request)
     {
         $request->valid(new Valid_String('cancel'));
         if (!$request->exist('cancel')) {
             $job_id = $request->get($this->widget_id . '_job_id');
-            $sql = "UPDATE plugin_hudson_widget SET job_id=" . $job_id . " WHERE owner_id = " . $this->owner_id . " AND owner_type = '" . $this->owner_type . "' AND id = " . (int) $request->get('content_id');
-            $res = db_query($sql);
+            $db = \Tuleap\DB\DBFactory::getMainTuleapDBConnection()->getDB();
+            $db->run(
+                'UPDATE plugin_hudson_widget SET job_id=? WHERE owner_id = ? AND owner_type = ? AND id = ?',
+                $job_id,
+                $this->owner_id,
+                $this->owner_type,
+                $this->content_id,
+            );
         }
         return true;
     }
 
     abstract protected function initContent();
@@ -131,21 +149,21 @@ abstract class HudsonJobWidget extends HudsonWidget
     /**
      * @return int|null
      */
     protected function getJobIdFromWidgetConfiguration()
     {
-        $sql = "SELECT *
-                    FROM plugin_hudson_widget
-                    WHERE widget_name = '" . db_es($this->widget_id) . "'
-                      AND owner_id = " . db_ei($this->owner_id) . "
-                      AND owner_type = '" . db_es($this->owner_type) . "'
-                      AND id = " . db_ei($this->content_id);
-
-        $res = db_query($sql);
-        if ($res && db_numrows($res)) {
-            $data   = db_fetch_array($res);
-            return $data['job_id'];
+        $db = \Tuleap\DB\DBFactory::getMainTuleapDBConnection()->getDB();
+        $job_id = $db->cell(
+            'SELECT job_id FROM plugin_hudson_widget WHERE widget_name = ? AND owner_id = ? AND owner_type = ? AND id = ?',
+            $this->widget_id,
+            $this->owner_id,
+            $this->owner_type,
+            $this->content_id,
+        );
+
+        if ($job_id === false) {
+            return null;
         }
 
-        return null;
+        return $job_id;
     }
 }

Vulnerable code:             $sql = 'INSERT INTO plugin_hudson_widget (widget_name, owner_id, owner_type, job_id) VALUES ("' . $this->id . '", ' . $this->owner_id . ", '" . $this->owner_type . "', " . db_escape_int($job_id) . " )";
            $res = db_query($sql);
            $content_id = db_insertid($res);
        $sql = 'DELETE FROM plugin_hudson_widget WHERE id = ' . $id . ' AND owner_id = ' . $this->owner_id . " AND owner_type = '" . $this->owner_type . "'";
        db_query($sql);
            $sql = "UPDATE plugin_hudson_widget SET job_id=" . $job_id . " WHERE owner_id = " . $this->owner_id . " AND owner_type = '" . $this->owner_type . "' AND id = " . (int) $request->get('content_id');
            $res = db_query($sql);
        $sql = "SELECT *
                    FROM plugin_hudson_widget
                    WHERE widget_name = '" . db_es($this->widget_id) . "'
                      AND owner_id = " . db_ei($this->owner_id) . "
                      AND owner_type = '" . db_es($this->owner_type) . "'
                      AND id = " . db_ei($this->content_id);

        $res = db_query($sql);
        if ($res && db_numrows($res)) {
            $data   = db_fetch_array($res);
            return $data['job_id'];
        return null;
Fixed code:             $db = \Tuleap\DB\DBFactory::getMainTuleapDBConnection()->getDB();
            $content_id = (int) $db->insertReturnId(
                'plugin_hudson_widget',
                [
                    'widget_name' => $this->id,
                    'owner_id'    => $this->owner_id,
                    'owner_type'  => $this->owner_type,
                    'job_id'      => $job_id
                ]
            );
        $db = \Tuleap\DB\DBFactory::getMainTuleapDBConnection()->getDB();
        $db->run(
            'DELETE FROM plugin_hudson_widget WHERE id = ? AND owner_id = ? AND owner_type = ?',
            $id,
            $this->owner_id,
            $this->owner_type,
        );
            $db = \Tuleap\DB\DBFactory::getMainTuleapDBConnection()->getDB();
            $db->run(
                'UPDATE plugin_hudson_widget SET job_id=? WHERE owner_id = ? AND owner_type = ? AND id = ?',
                $job_id,
                $this->owner_id,
                $this->owner_type,
                $this->content_id,
            );
        $db = \Tuleap\DB\DBFactory::getMainTuleapDBConnection()->getDB();
        $job_id = $db->cell(
            'SELECT job_id FROM plugin_hudson_widget WHERE widget_name = ? AND owner_id = ? AND owner_type = ? AND id = ?',
            $this->widget_id,
            $this->owner_id,
            $this->owner_type,
            $this->content_id,
        );

        if ($job_id === false) {
            return null;
        return $job_id;