CVE-2023-40954 (published 2023-12-15T01:15Z)

A SQL injection vulnerability in Grzegorz Marczynski Dynamic Progress Bar (aka web_progress) v. 11.0 through 11.0.2, v12.0 through v12.0.2, v.13.0 through v13.0.2, v.14.0 through v14.0.2.1, v.15.0 through v15.0.2, and v16.0 through v16.0.2.1 allows a remote attacker to gain privileges via the recency parameter in models/web_progress.py component.

CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-89
cwe_name: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
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.
extended_description: 
            
            
         
         

Fix information:
repo_url: https://github.com/gmarczynski/odoo-web-progress
commit_details: repo_url: https://github.com/gmarczynski/odoo-web-progress
author: Grzegorz Marczynski
author_date: 2023-08-10 21:06:20+02:00
msg: [FIX] Escape parameter of SQL query, closes #34
repository: repo_url: https://github.com/gmarczynski/odoo-web-progress
repo_name: gmarczynski/odoo-web-progress
description: Dynamic Progress Bar for Odoo Waiting Screen
repo_language: Python
owner: gmarczynski
file_changes: filename: web_progress.py
change_type: ModificationType.MODIFY
diff: @@ -165,7 +165,7 @@ class WebProgress(models.TransientModel):
         """
         query = """
         SELECT code, array_agg(state) FROM web_progress
-        WHERE create_date > timezone('utc', now()) - INTERVAL '{recency} SECOND'
+        WHERE create_date > timezone('utc', now()) - INTERVAL '%s SECOND'
               AND recur_depth = 0 {user_id}
         GROUP BY code
         """.format(
@@ -175,7 +175,7 @@ class WebProgress(models.TransientModel):
                 user_id=self.env.user.id,
             ) or '')
         # superuser has right to see (and cancel) progress of everybody
-        self.env.cr.execute(query)
+        self.env.cr.execute(query, (recency, ))
         result = self.env.cr.fetchall()
         ret = [{
             'code': r[0],

programming_language: Python
filename: test_web_progress.py
change_type: ModificationType.MODIFY
diff: @@ -1,5 +1,7 @@
 from odoo.tests import common, tagged
-from odoo import exceptions
+from odoo import exceptions, api, registry
+from odoo.tools import mute_logger
+from psycopg2.errors import SyntaxError
 import uuid
 import logging
 from ..models.web_progress import last_report_time
@@ -115,3 +117,32 @@ class WebProgressTest(common.SavepointCase):
         self.partner_ids.web_progress_percent(0, "Start")
         self.partner_ids.web_progress_percent(50, "Middle")
         self.partner_ids.web_progress_percent(100, "End")
+
+
+class WebProgressTestAllProgress(common.SavepointCase):
+    at_install = True
+    post_install = False
+
+    @mute_logger('odoo.sql_db')
+    def test_get_all_progress(self):
+        """
+        Check call to get_all_progress without and with parameters.
+        Verify if the parameter is properly escaped in the internal SQL query.
+        """
+        progress_code = str(uuid.uuid4())
+        partner_obj = self.env['res.partner'].with_context(progress_code=progress_code)
+        partner_obj.web_progress_percent(0, "Start")
+        with api.Environment.manage():
+            with registry(self.env.cr.dbname).cursor() as new_cr:
+                # Create a new environment with a new cursor
+                new_env = api.Environment(new_cr, self.env.uid, self.env.context)
+                progress_obj = self.env['web.progress'].with_env(new_env)
+                res = progress_obj.get_all_progress()
+                self.assertEqual(res, [{'code': progress_code}])
+                res = progress_obj.get_all_progress(0)
+                self.assertEqual(res, [])
+                with self.assertRaises(SyntaxError) as e:
+                    progress_obj.get_all_progress("0 SECOND' GROUP BY code; "
+                                                  "SELECT code, array_agg(state) FROM web_progress "
+                                                  "WHERE create_date > timezone('utc', now()) - INTERVAL '10")
+                new_cr.rollback()

programming_language: Python