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
--- /dev/null
+.. 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.
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,
)
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