From 3281c9fec3dd0e6ac60f9af6ab5b412df5d8354d Mon Sep 17 00:00:00 2001 From: Yaniv Michael Kaul Date: Sun, 22 Mar 2026 17:07:17 +0200 Subject: [PATCH 1/2] Fix CQL injection in Connection.set_keyspace_blocking and set_keyspace_async Escape double quotes in keyspace names when constructing USE statements to prevent CQL injection. A keyspace name containing '"' would produce malformed or injectable CQL (e.g., USE "foo"bar"). This is the Python equivalent of the vulnerability fixed in the Go driver (gocql#783). The fix escapes '"' as '""' per CQL quoted-identifier rules, matching the existing escape_name() function in cassandra/metadata.py. --- cassandra/connection.py | 6 ++++-- tests/unit/test_connection.py | 37 ++++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/cassandra/connection.py b/cassandra/connection.py index 72b273ec37..c045b36cb3 100644 --- a/cassandra/connection.py +++ b/cassandra/connection.py @@ -1658,7 +1658,8 @@ def set_keyspace_blocking(self, keyspace): if not keyspace or keyspace == self.keyspace: return - query = QueryMessage(query='USE "%s"' % (keyspace,), + from cassandra.metadata import escape_name + query = QueryMessage(query='USE %s' % (escape_name(keyspace),), consistency_level=ConsistencyLevel.ONE) try: result = self.wait_for_response(query) @@ -1712,7 +1713,8 @@ def set_keyspace_async(self, keyspace, callback): callback(self, None) return - query = QueryMessage(query='USE "%s"' % (keyspace,), + from cassandra.metadata import escape_name + query = QueryMessage(query='USE %s' % (escape_name(keyspace),), consistency_level=ConsistencyLevel.ONE) def process_result(result): diff --git a/tests/unit/test_connection.py b/tests/unit/test_connection.py index 6ac63ff761..a67b7e4678 100644 --- a/tests/unit/test_connection.py +++ b/tests/unit/test_connection.py @@ -25,7 +25,8 @@ ConnectionException, ConnectionShutdown, DefaultEndPoint, ShardAwarePortGenerator) from cassandra.marshal import uint8_pack, uint32_pack, int32_pack from cassandra.protocol import (write_stringmultimap, write_int, write_string, - SupportedMessage, ProtocolHandler) + SupportedMessage, ProtocolHandler, ResultMessage, + RESULT_KIND_SET_KEYSPACE) from tests.util import wait_until, assertRegex import pytest @@ -256,6 +257,40 @@ def test_set_keyspace_blocking(self): c.set_keyspace_blocking('ks') assert c.keyspace == 'ks' + def test_set_keyspace_blocking_escapes_quotes(self): + """ + Test that set_keyspace_blocking properly escapes double quotes in + keyspace names to prevent CQL injection. This is the Python equivalent + of the vulnerability fixed in the Go driver: + https://github.com/scylladb/gocql/pull/783 + """ + c = self.make_connection() + c.wait_for_response = Mock(return_value=ResultMessage(kind=RESULT_KIND_SET_KEYSPACE)) + + c.set_keyspace_blocking('my"ks') + query_msg = c.wait_for_response.call_args[0][0] + assert query_msg.query == 'USE "my""ks"', ( + "Double quotes in keyspace name must be escaped as double-double quotes") + + def test_set_keyspace_async_escapes_quotes(self): + """ + Test that set_keyspace_async properly escapes double quotes in + keyspace names to prevent CQL injection. + """ + c = self.make_connection() + c.lock = Lock() + c.in_flight = 0 + c.max_request_id = 100 + c.get_request_id = Mock(return_value=1) + c.send_msg = Mock() + + callback = Mock() + c.set_keyspace_async('my"ks', callback) + + query_msg = c.send_msg.call_args[0][0] + assert query_msg.query == 'USE "my""ks"', ( + "Double quotes in keyspace name must be escaped as double-double quotes") + def test_set_connection_class(self): cluster = Cluster(connection_class='test') assert 'test' == cluster.connection_class From d7d34a62cad8a4daf3f3ee48fc5d41baedbb8ee4 Mon Sep 17 00:00:00 2001 From: Yaniv Michael Kaul Date: Wed, 25 Mar 2026 17:26:29 +0200 Subject: [PATCH 2/2] Add unit test for Session.set_keyspace double-quote escaping Test that Session.set_keyspace properly escapes double quotes in keyspace names (e.g. 'my"ks' -> USE "my""ks") to prevent CQL injection. Also verifies simple keyspace names are not unnecessarily quoted. Requested in review of PR #758. --- tests/unit/test_cluster.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/unit/test_cluster.py b/tests/unit/test_cluster.py index 49208ac53e..cd8c7765af 100644 --- a/tests/unit/test_cluster.py +++ b/tests/unit/test_cluster.py @@ -251,6 +251,33 @@ def test_default_serial_consistency_level_legacy(self, *_): assert f.message.serial_consistency_level == cl_override + + @mock_session_pools + def test_set_keyspace_escapes_quotes(self, *_): + """ + Test that Session.set_keyspace properly escapes double quotes in + keyspace names to prevent CQL injection. + Requested in review of PR #758. + """ + c = Cluster(protocol_version=4) + s = Session(c, [Host("127.0.0.1", SimpleConvictionPolicy, host_id=uuid.uuid4())]) + c.connection_class.initialize_reactor() + + s.execute = Mock() + + s.set_keyspace('my"ks') + query = s.execute.call_args[0][0] + assert query == 'USE "my""ks"', ( + "Double quotes in keyspace name must be escaped as double-double quotes, " + "got: %r" % query) + + # Also verify a simple keyspace name doesn't get unnecessarily quoted + s.execute.reset_mock() + s.set_keyspace('simple_ks') + query = s.execute.call_args[0][0] + assert query == 'USE simple_ks', ( + "Simple keyspace names should not be quoted, got: %r" % query) + class ProtocolVersionTests(unittest.TestCase): def test_protocol_downgrade_test(self):