Ujjwaljain16 opened a new pull request, #43985:
URL: https://github.com/apache/superset/pull/43985

   ### SUMMARY
   
   `Query.get_extra_cache_keys()` (superset/models/sql_lab.py) — the method that
   supplies extra, RLS-sensitive cache-key material for a SQL Lab ad-hoc query
   used as a chart's datasource — unconditionally returned `[]`. `SqlaTable`
   already includes RLS predicates from a virtual dataset's underlying tables
   in its own `get_extra_cache_keys()` via `collect_rls_predicates_for_sql()`;
   `Query` never got the same treatment.
   
   This is adjacent to, but distinct from, #33346. **The original #33346
   query-execution vulnerability is already fixed** on current master:
   `ExploreMixin.get_from_clause` (shared by `SqlaTable` virtual datasets and
   `Query` alike) parses the raw SQL for referenced tables and injects those
   tables' RLS predicates at query-construction time, and fails closed rather
   than running unfiltered if that can't be safely resolved. That fix was not
   tied to #33346 in its commit history. A regression test for that
   already-fixed path is added here since none existed for the `Query`
   datasource type specifically. This PR fixes an adjacent RLS cache-key
   isolation gap affecting the same SQL Lab ad-hoc query flow.
   
   `Query.is_rls_supported` is (correctly) `False`: an ad-hoc SQL Lab query has
   no RLS rules directly attached to it as a dataset, so the dataset-level
   `security_manager.get_rls_cache_key()` mechanism correctly contributes
   nothing. But that's a different axis from "the RLS applicable to the
   *tables this query's SQL references*" cache-key material, which `Query` was
   simply missing.
   
   **Why this matters**: a chart built from a SQL Lab query can never be
   *saved* as a Slice/dashboard — `CreateChartCommand.validate()` explicitly
   rejects any `datasource_type != TABLE` for exactly this reason (it can't
   render on reload). It *can*, however, be viewed by a second user via a
   shared Explore permalink: `check_query_access` grants a non-author the
   same catalog/schema/`datasource_access` checks as any other datasource,
   not a free pass. If two such viewers have different RLS on the query's
   underlying tables, and query-result caching is enabled (the docs recommend
   `DATA_CACHE_CONFIG`/Redis for production), the second viewer's request
   could hit the first viewer's cache entry.
   
   **Fix**: wire `Query.get_extra_cache_keys()` up to
   `collect_rls_predicates_for_sql()` — the same helper `SqlaTable` already
   uses for virtual datasets — mirroring that existing pattern exactly. No new
   abstractions.
   
   ### BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF
   
   N/A (backend-only, no UI change). Before/after values of
   `Query.get_extra_cache_keys()` for two viewers with differing RLS on the
   same underlying table, verified live on Postgres:
   
   | | gamma (restricted) | admin (unrestricted) |
   |---|---|---|
   | Before | `[]` | `[]` |
   | After | `["(col2 = 'a')"]` | `[]` |
   
   ### TESTING INSTRUCTIONS
   
   ```
   pytest 
tests/integration_tests/security/row_level_security_tests.py::test_rls_filter_applies_to_sqllab_adhoc_query
   pytest 
tests/integration_tests/security/row_level_security_tests.py::test_rls_filter_in_sqllab_adhoc_query_cache_key
   ```
   
   - `test_rls_filter_applies_to_sqllab_adhoc_query`: regression coverage for
     #33346's already-fixed query-construction path (previously untested for
     the `Query` datasource type specifically).
   - `test_rls_filter_in_sqllab_adhoc_query_cache_key`: regression coverage for
     this fix; fails without it (`Query.get_extra_cache_keys()` returns `[]`
     for both a restricted and unrestricted viewer), passes with it.
   
   Verified on real Postgres via the project's `pytest-runner` CI-parity
   environment, before and after the fix. Full
   `row_level_security_tests.py` suite: 51 passed (1 pre-existing, unrelated
   failure confirmed identical without this change). `sql_lab_test.py` +
   `rls_test.py` unit suites: 27 passed, including a test that documents real
   RLS enforcement never goes through this cache-key helper — confirming this
   change cannot affect query correctness, only cache isolation. `ruff
   format`/`ruff check`: clean. `pylint`: 10.00/10. `mypy`: no new errors
   introduced.
   
   ### ADDITIONAL INFORMATION
   
   - [ ] Has associated issue: adjacent to #33346
   - [ ] Required feature flags:
   - [ ] Changes UI
   - [ ] Includes DB Migration (follow approval process in 
[SIP-59](https://github.com/apache/superset/issues/13351))
     - [ ] Migration is atomic, supports rollback & is backwards-compatible
     - [ ] Confirm DB migration upgrade and downgrade tested
     - [ ] Runtime estimates and downtime expectations provided
   - [ ] Introduces new feature or API
   - [ ] Removes existing feature or API
   
   ### SCOPE LIMITATIONS
   
   - Does not change `Query.cache_timeout` (`0`, which Superset's caching
     layer treats as "never expire," not "disabled" — only `-1` disables
     caching). A stale-but-now-correctly-isolated cache entry can still
     outlive its RLS context if a user's own role changes later; that's a
     separate, broader caching-freshness question, not a cross-user bypass,
     and is out of scope here.
   - Does not address a separate, pre-existing case-sensitivity gap in
     `get_predicates_for_table`'s table-name matching (confirmed to affect
     virtual datasets too, not specific to this code path) — tracked
     separately, not touched by this PR.
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to