diff --git a/cinder/db/sqlalchemy/api.py b/cinder/db/sqlalchemy/api.py index a3fd2c6f261..64b05d8ce3d 100644 --- a/cinder/db/sqlalchemy/api.py +++ b/cinder/db/sqlalchemy/api.py @@ -8289,7 +8289,7 @@ def _process_messages_filters(query, filters): return query -def _messages_get_query(context, project_only=False): +def _messages_get_query(context, project_only=True): return model_query(context, models.Message, project_only=project_only) diff --git a/cinder/tests/unit/test_db_api.py b/cinder/tests/unit/test_db_api.py index 7d1e1cf5733..948a1d021a1 100644 --- a/cinder/tests/unit/test_db_api.py +++ b/cinder/tests/unit/test_db_api.py @@ -2882,6 +2882,50 @@ def _create_fake_messages(self, m_id, time): 'project_id': 'fake_id', 'expires_at': time}) + def _create_message(self, project_id): + message_id = uuidutils.generate_uuid() + expires_at = timeutils.utcnow() + datetime.timedelta(days=1) + db.message_create(self.context, + {'id': message_id, + 'event_id': message_id, + 'message_level': 'error', + 'project_id': project_id, + 'expires_at': expires_at}) + return message_id + + def test_message_get_all_is_project_scoped(self): + project_id = 'project1' + other_project_id = 'project2' + project_message_id = self._create_message(project_id) + other_project_message_id = self._create_message(other_project_id) + user_context = context.RequestContext('user1', project_id, + is_admin=False) + + messages = db.message_get_all(user_context) + + self.assertEqual({project_message_id}, + {message['id'] for message in messages}) + self.assertNotIn(other_project_message_id, + {message['id'] for message in messages}) + # self.context is an admin context and should see both projects. + admin_messages = db.message_get_all(self.context) + self.assertEqual({project_message_id, other_project_message_id}, + {message['id'] for message in admin_messages}) + + def test_message_get_is_project_scoped(self): + project_id = 'project1' + other_project_id = 'project2' + project_message_id = self._create_message(project_id) + other_project_message_id = self._create_message(other_project_id) + user_context = context.RequestContext('user1', project_id, + is_admin=False) + + message = db.message_get(user_context, project_message_id) + self.assertEqual(project_message_id, message['id']) + self.assertRaises(exception.MessageNotFound, + db.message_get, user_context, + other_project_message_id) + def test_cleanup_expired_messages(self): now = timeutils.utcnow() # message expired 1 day ago diff --git a/releasenotes/notes/bug-2160671-cve-2026-93856-2823294ed5a19f9d.yaml b/releasenotes/notes/bug-2160671-cve-2026-93856-2823294ed5a19f9d.yaml new file mode 100644 index 00000000000..9e11fe4c7b4 --- /dev/null +++ b/releasenotes/notes/bug-2160671-cve-2026-93856-2823294ed5a19f9d.yaml @@ -0,0 +1,14 @@ +--- +fixes: + - | + `Bug #2160671 `_: + Fixed a cross-project information disclosure issue in the Block Storage + API v3.3 (CVE-2026-93856), where a user with the project 'member' or + 'reader' role making the "List messages" call could find the non-expired + user messages for other projects included in the response. +security: + - | + This release contains a fix for CVE-2026-93856, `Cross-project + information disclosure in the Cinder Messages API`. See + `Bug #2160671 `_ + for details.