CVE-2018-25088 (published 2023-07-18T13:15Z)

A vulnerability, which was classified as critical, was found in Blue Yonder postgraas_server up to 2.0.0b2. Affected is the function _create_pg_connection/create_postgres_db of the file postgraas_server/backends/postgres_cluster/postgres_cluster_driver.py of the component PostgreSQL Backend Handler. The manipulation leads to sql injection. Upgrading to version 2.0.0 is able to address this issue. The patch is identified as 7cd8d016edc74a78af0d81c948bfafbcc93c937c. It is recommended to upgrade the affected component. VDB-234246 is the identifier assigned to this vulnerability.

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/blue-yonder/postgraas_server
commit_details: repo_url: https://github.com/blue-yonder/postgraas_server
author: Matthias Bach
author_date: 2018-01-30 09:52:28+01:00
msg: Harden the database creation against SQL injections

If using the PostgreSQL backend, database creation was previously vulnerable to SQL injections. This commit fixes these.

There are two tests which no longer behave as expected and have been disabled for now. They use usernames that previously broke the SQL statement but have now become valid. We might want to think about restricting the possible username somewhat again.
repository: repo_url: https://github.com/blue-yonder/postgraas_server
repo_name: blue-yonder/postgraas_server
description: Postgraas is a super simple PostgreSQL-as-a-service
repo_language: Python
owner: blue-yonder
file_changes: filename: postgres_cluster_driver.py
change_type: ModificationType.MODIFY
diff: @@ -1,5 +1,6 @@
 import psycopg2
 from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT
+from psycopg2.sql import SQL, Identifier
 
 
 def _create_pg_connection(config):
@@ -19,9 +20,9 @@ def _create_pg_connection(config):
 def check_db_or_user_exists(db_name, db_user, config):
     with _create_pg_connection(config) as con:
         with con.cursor() as cur:
-            cur.execute("SELECT 1 FROM pg_database WHERE datname='{}';".format(db_name))
+            cur.execute("SELECT 1 FROM pg_database WHERE datname=%s;", (db_name, ))
             db_exists = cur.fetchone() is not None
-            cur.execute("SELECT 1 FROM pg_roles WHERE rolname='{}';".format(db_user))
+            cur.execute("SELECT 1 FROM pg_roles WHERE rolname=%s;", (db_user, ))
             user = cur.fetchone()
             user_exists = user is not None
             return db_exists or user_exists
@@ -33,23 +34,29 @@ def create_postgres_db(connection_dict, config):
     with _create_pg_connection(config) as con:
         con.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
         with con.cursor() as cur:
-            create_role = "CREATE USER {db_username} WITH PASSWORD '{db_pwd}';".format(**connection_dict)
-            drop_role = "DROP ROLE {db_username};".format(**connection_dict)
-            grant_role = 'GRANT {db_username} TO "{postgraas_user}";'.format(
-                db_username=connection_dict['db_username'], postgraas_user=get_normalized_username(config['username'])
-            )
-            create_database = "CREATE DATABASE {db_name} OWNER {db_username};".format(**connection_dict)
             try:
-                cur.execute(create_role)
-                cur.execute(grant_role)
+                cur.execute(SQL("CREATE USER {} WITH PASSWORD %s;").format(
+                    Identifier(connection_dict['db_username']),
+                ), (
+                    connection_dict['db_pwd'],
+                ))
+                cur.execute(SQL("GRANT {} TO {};").format(
+                    Identifier(connection_dict['db_username']),
+                    Identifier(get_normalized_username(config['username'])),
+                ))
             except psycopg2.ProgrammingError as e:
                 raise ValueError(e.args[0])
             # cleanup role in case database creation fails
-            # saidly 'CREATE DATABASE' cannot run inside a transaction block
+            # sadly 'CREATE DATABASE' cannot run inside a transaction block
             try:
-                cur.execute(create_database)
+                cur.execute(SQL("CREATE DATABASE {} OWNER {};").format(
+                    Identifier(connection_dict['db_name']),
+                    Identifier(connection_dict['db_username']),
+                ))
             except psycopg2.ProgrammingError as e:
-                cur.execute(drop_role)
+                cur.execute(SQL("DROP ROLE {};").format(
+                    Identifier(connection_dict['db_username']),
+                ))
                 raise ValueError(e.args[0])
 
 

programming_language: Python
filename: test_postgres_cluster_driver.py
change_type: ModificationType.MODIFY
diff: @@ -193,6 +193,7 @@ class TestPostgraasApi(PostgraasApiTestBase):
                                       backend_config)
         assert ("database or user already exists" in json.loads(response.get_data(as_text=True))['description']) is True
 
+    @pytest.mark.xfail(reason='Username now valid due to hardening against SQL injections.')
     def test_create_postgres_instance_bad_username(self):
         backend_config = CONFIGS[self.backend]['backend']
         db_credentials = {

programming_language: Python
filename: test_backend_behaviour.py
change_type: ModificationType.MODIFY
diff: @@ -167,6 +167,7 @@ class TestPostgraasApi():
         self.this_app.postgraas_backend.delete(db_entry)
         assert True
 
+    @pytest.mark.xfail(reason='Username now valid due to hardening against SQL injections.')
     def test_create_postgraas_bad_username(self):
         db_credentials = {
             "db_name": 'tests_postgraas_instance_name',

programming_language: Python