From 20944af18b65f4a6cc95e169f3c2f637c502267d Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 4 Jan 2026 17:27:44 +0000 Subject: [PATCH 1/2] fix(security): detect tables in nested VALUES clauses for RLS This change addresses a vulnerability where Row Level Security (RLS) could be bypassed by nesting a subquery within a VALUES clause. The existing `traverse_scope` logic in `sqlglot` did not reliably extract tables from such constructs. The fix adds an explicit traversal of `exp.Values` nodes in `extract_tables_from_statement` to find any hidden tables, while employing a heuristic to avoid extracting Common Table Expressions (CTEs) as physical tables. Added `tests/unit_tests/sql/parse_tests_vulnerability.py` to verify the fix. Existing tests in `tests/unit_tests/sql/parse_tests.py` pass. --- superset/sql/parse.py | 16 ++++++++ .../sql/parse_tests_vulnerability.py | 41 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 tests/unit_tests/sql/parse_tests_vulnerability.py diff --git a/superset/sql/parse.py b/superset/sql/parse.py index c750e0551b01..14830c9e876b 100644 --- a/superset/sql/parse.py +++ b/superset/sql/parse.py @@ -1373,6 +1373,22 @@ def extract_tables_from_statement( if isinstance(source, exp.Table) and not is_cte(source, scope) ] + # Use of `traverse_scope` doesn't always catch tables in subqueries within + # `VALUES` clauses, so we need to search for them explicitly. + # See https://github.com/apache/superset/issues/31599 for more details. + cte_names = { + cte.alias + for cte in statement.find_all(exp.CTE) + if isinstance(cte.alias, str) + } + for value in statement.find_all(exp.Values): + for table in value.find_all(exp.Table): + # If the table is not a CTE, it should be treated as a source. + # This is a heuristic, as we can't easily check for CTEs in scopes + # without traversing them. + if table.name not in cte_names: + sources.append(table) + return { Table( source.name, diff --git a/tests/unit_tests/sql/parse_tests_vulnerability.py b/tests/unit_tests/sql/parse_tests_vulnerability.py new file mode 100644 index 000000000000..91437c8edfc3 --- /dev/null +++ b/tests/unit_tests/sql/parse_tests_vulnerability.py @@ -0,0 +1,41 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +import unittest +from superset.sql.parse import SQLStatement + +class TestSqlParseVulnerability(unittest.TestCase): + def test_values_subquery_extraction(self): + """ + Test that tables hidden inside a VALUES clause with a nested subquery are extracted. + """ + sql = "SUM(revenue) + (SELECT \"r\" FROM (VALUES((SELECT COUNT(*) FROM sales WHERE country='CA'))) v(\"r\"))" + statement = SQLStatement(sql, engine="postgresql") + tables = statement.tables + + self.assertIn("sales", [t.table for t in tables], "Should extract 'sales' table from nested VALUES subquery") + + def test_normal_subquery_extraction(self): + """ + Test that normal subqueries work as expected. + """ + sql = "SELECT * FROM (SELECT * FROM sales) AS sub" + statement = SQLStatement(sql, engine="postgresql") + tables = statement.tables + self.assertIn("sales", [t.table for t in tables]) + +if __name__ == '__main__': + unittest.main() From 84e8b89d7ceebb9d6183688d79ca770bf19bf493 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 4 Jan 2026 17:59:37 +0000 Subject: [PATCH 2/2] fix(security): detect tables in nested VALUES clauses for RLS This change addresses a vulnerability where Row Level Security (RLS) could be bypassed by nesting a subquery within a VALUES clause. The existing `traverse_scope` logic in `sqlglot` did not reliably extract tables from such constructs when the SQL expression was processed raw (e.g., in certain Query datasource paths). The fix adds an explicit traversal of `exp.Values` nodes in `extract_tables_from_statement` to find any hidden tables, while employing a heuristic to avoid extracting Common Table Expressions (CTEs) as physical tables. Added `tests/unit_tests/sql/parse_tests_vulnerability.py` to verify the fix. Existing tests in `tests/unit_tests/sql/parse_tests.py` pass.