diff --git a/superset/sql_parse.py b/superset/sql_parse.py index 6c62872af47..678c3a8101e 100644 --- a/superset/sql_parse.py +++ b/superset/sql_parse.py @@ -88,9 +88,13 @@ logger = logging.getLogger(__name__) # reference: https://sqlparse.readthedocs.io/en/stable/extending/ lex = Lexer.get_default_instance() sqlparser_sql_regex = keywords.SQL_REGEX -sqlparser_sql_regex.insert( - 25, (r"'(?:[^'\\]|\\\\?|'')*'", sqlparse.tokens.String.Single) -) +# Replace the string pattern at position 25 to fix ReDOS vulnerability +# Use two patterns: first for strings ending with backslash, then for general escaped +# strings +# Pattern 1: Match strings that end with a single backslash (like '\') +sqlparser_sql_regex[25] = (r"'[^'\\]*\\'(?!\w)", sqlparse.tokens.String.Single) +# Pattern 2: Match general strings with escape sequences +sqlparser_sql_regex.insert(26, (r"'(?:[^'\\]|\\.|'')*'", sqlparse.tokens.String.Single)) lex.set_SQL_REGEX(sqlparser_sql_regex) diff --git a/tests/unit_tests/sql_parse_tests.py b/tests/unit_tests/sql_parse_tests.py index 23aa6b0b125..5de9d9d87fd 100644 --- a/tests/unit_tests/sql_parse_tests.py +++ b/tests/unit_tests/sql_parse_tests.py @@ -1309,9 +1309,13 @@ def test_sanitize_clause_multiple(): def test_sqlparse_issue_652(): + stmt = sqlparse.parse(r"SELECT 'Don\'t stop' FROM table")[0] + assert len(stmt.tokens) == 7 + assert str(stmt.tokens[2]) == r"'Don\'t stop'" + stmt = sqlparse.parse(r"foo = '\' AND bar = 'baz'")[0] assert len(stmt.tokens) == 5 - assert str(stmt.tokens[0]) == "foo = '\\'" + assert str(stmt.tokens[0]) == r"foo = '\'" @pytest.mark.parametrize(