]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
fix backtracking hang in hstore literal parser
authordxbjavid <dxbjavid@gmail.com>
Fri, 12 Jun 2026 11:19:04 +0000 (07:19 -0400)
committerMichael Bayer <mike_mp@zzzcomputing.com>
Mon, 15 Jun 2026 14:28:56 +0000 (14:28 +0000)
Fixed regular expression in the pure Python hstore result processor,
used when ``use_native_hstore=False`` is set, which could hang on
malformed hstore text containing unterminated quoted segments with
backslashes.  Pull request courtesy dxbjavid.

Fixes: #13370
Closes: #13371
Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/13371
Pull-request-sha: f5eddae11c435c78f326b70a15357cbaf6d09337

Change-Id: I0d2d7565dc88f56a73b41e2ad20ca1c5a6f738bb

doc/build/changelog/unreleased_20/13370.rst [new file with mode: 0644]
lib/sqlalchemy/dialects/postgresql/hstore.py
test/dialect/postgresql/test_types.py

diff --git a/doc/build/changelog/unreleased_20/13370.rst b/doc/build/changelog/unreleased_20/13370.rst
new file mode 100644 (file)
index 0000000..26e8945
--- /dev/null
@@ -0,0 +1,8 @@
+.. change::
+    :tags: bug, postgresql
+    :tickets: 13370
+
+    Fixed regular expression in the pure Python hstore result processor,
+    used when ``use_native_hstore=False`` is set, which could hang on
+    malformed hstore text containing unterminated quoted segments with
+    backslashes.  Pull request courtesy dxbjavid.
index 738c27814a30b93af4435c94575faa331d5bcd97..0a25f1fc9b6448f1382aa5dfd48c4a3047d1ada7 100644 (file)
@@ -319,12 +319,12 @@ class _HStoreMatrixFunction(sqlfunc.GenericFunction[Any]):
 HSTORE_PAIR_RE = re.compile(
     r"""
 (
-  "(?P<key> (\\ . | [^"])* )"       # Quoted key
+  "(?P<key> (\\ . | [^"\\])* )"     # Quoted key
 )
 [ ]* => [ ]*    # Pair operator, optional adjoining whitespace
 (
     (?P<value_null> NULL )          # NULL value
-  | "(?P<value> (\\ . | [^"])* )"   # Quoted value
+  | "(?P<value> (\\ . | [^"\\])* )" # Quoted value
 )
 """,
     re.VERBOSE,
index 2f5df25d87b4d57fb5a659f85ef3588b8bd4e390..ce1994fa6996977e5f3376652df92390d04f9f52 100644 (file)
@@ -4152,6 +4152,16 @@ class HStoreTest(AssertsCompiledSQL, fixtures.TestBase):
         )
         eq_(proc('"\\\\\\"a"=>"\\\\\\"1"'), {'\\"a': '\\"1'})
 
+    def test_result_deserialize_malformed_no_backtracking(self):
+        # a quoted segment with a long run of backslashes and no closing
+        # quote used to make HSTORE_PAIR_RE backtrack catastrophically; the
+        # parser should reject it promptly instead of hanging
+        dialect = postgresql.dialect(use_native_hstore=False)
+        proc = self.test_table.c.hash.type._cached_result_processor(
+            dialect, None
+        )
+        assert_raises(ValueError, proc, '"' + "\\" * 40 + "!")
+
     def test_bind_serialize_psycopg2(self):
         from sqlalchemy.dialects.postgresql import psycopg2