Skip to content

fix: Drill to Detail for Embedded#39214

Merged
Vitor-Avila merged 3 commits into
masterfrom
fix/d2d-embedded
Apr 9, 2026
Merged

fix: Drill to Detail for Embedded#39214
Vitor-Avila merged 3 commits into
masterfrom
fix/d2d-embedded

Conversation

@Vitor-Avila
Copy link
Copy Markdown
Contributor

@Vitor-Avila Vitor-Avila commented Apr 9, 2026

SUMMARY

#34319 added support for D2D operations (Drill to Detail and Drill By) to Embedded users. This flow was broken after this security fix: #36550.

This PR is fixing this flow, ensuring that required permission validations are applied for Embedded requests. Test coverage was also improved.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

Before

image

After

image

TESTING INSTRUCTIONS

Test coverage added. For manual testing:

  1. Make sure the embedded role has can sample on Datasource perm, as well as other required perms for D2D actions.
  2. Load a dashboard in Embedded mode.
  3. Confirm that D2D operations work properly.

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • 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

@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review Bot commented Apr 9, 2026

Code Review Agent Run #7a943a

Actionable Suggestions - 0
Review Details
  • Files reviewed - 5 · Commit Range: 0bf26f1..0bf26f1
    • superset/security/manager.py
    • superset/views/datasource/utils.py
    • superset/views/datasource/views.py
    • tests/integration_tests/datasource_tests.py
    • tests/integration_tests/security/guest_token_security_tests.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 9, 2026

Codecov Report

❌ Patch coverage is 71.42857% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.38%. Comparing base (c496415) to head (85ac6f7).
⚠️ Report is 5 commits behind head on master.

Files with missing lines Patch % Lines
superset/security/manager.py 64.70% 2 Missing and 4 partials ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##           master   #39214   +/-   ##
=======================================
  Coverage   64.38%   64.38%           
=======================================
  Files        2550     2550           
  Lines      132141   132184   +43     
  Branches    30658    30661    +3     
=======================================
+ Hits        85074    85103   +29     
- Misses      45584    45595   +11     
- Partials     1483     1486    +3     
Flag Coverage Δ
hive 40.04% <9.52%> (+<0.01%) ⬆️
mysql 60.76% <71.42%> (+<0.01%) ⬆️
postgres 60.84% <66.66%> (+<0.01%) ⬆️
presto 41.84% <9.52%> (+<0.01%) ⬆️
python 62.42% <71.42%> (+<0.01%) ⬆️
sqlite 60.47% <57.14%> (+<0.01%) ⬆️
unit 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread tests/integration_tests/datasource_tests.py Outdated
.filter(Slice.id == parent_id)
.one_or_none()
)
and parent_slc in dashboard_.slices
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The multi-layer child access path validates parent_slice_id and child membership in deck_slices, but it never verifies that the requested datasource actually belongs to that child chart. Because datasource authorization is granted when this branch is true, a forged request can pair a valid parent/child chart combination with an unrelated datasource and bypass datasource access checks. Add an explicit lookup of the child slice and ensure its datasource matches the requested datasource before granting access. [security]

Severity Level: Critical 🚨
- ❌ Embedded guests query unauthorized datasets via ChartDataRestApi.data endpoint.
- ❌ Dashboard RBAC users bypass datasource checks for deck_multi children.
- ⚠️ Inconsistent child datasource validation in SupersetSecurityManager.raise_for_access.
Suggested change
and parent_slc in dashboard_.slices
and (
child_slc := self.session.query(Slice)
.filter(Slice.id == slice_id)
.one_or_none()
)
and parent_slc in dashboard_.slices
and child_slc.datasource == datasource
Steps of Reproduction ✅
1. Configure a dashboard with roles or embedded access so that either DASHBOARD_RBAC or
EMBEDDED_SUPERSET is enabled and used (checked in
`SupersetSecurityManager.raise_for_access` at `superset/security/manager.py:162-179` and
`:269-271` via `is_feature_enabled("DASHBOARD_RBAC")` /
`is_feature_enabled("EMBEDDED_SUPERSET")`).

2. On that dashboard, add a `deck_multi` chart whose parent slice has a `deck_slices`
configuration including a child chart ID (validated by
`_validate_child_in_parent_multilayer` at `superset/security/manager.py:702-724`),
ensuring the child chart uses dataset A; also create another dataset B that the same
user/guest does NOT have `datasource_access` or schema access to.

3. As an embedded guest user (feature flag `EMBEDDED_SUPERSET`) or a RBAC viewer of that
dashboard, send a POST request to `ChartDataRestApi.data` at `/api/v1/chart/data`
(`superset/charts/data/api.py:280-99`) with a JSON body that `ChartDataQueryContextSchema`
accepts, where `datasource` points to dataset B (e.g. `{"id": <B_id>, "type": "table"}`)
and `form_data` contains `{"dashboardId": <dashboard_id>, "type": "DRILL_DETAIL",
"slice_id": <child_slice_id>, "parent_slice_id": <parent_slice_id>, ...}` so that
`slice_id` is the valid child from the parent's `deck_slices`.

4. The request flows through `ChartDataCommand.validate`
(`superset/commands/chart/data/get_data_command.py:39-40`) which calls
`QueryContext.raise_for_access` (`superset/common/query_context.py:18`), which delegates
to `QueryContextProcessor.raise_for_access` and then
`security_manager.raise_for_access(query_context=self._query_context)`
(`superset/common/query_context_processor.py:525-537);` inside
`SupersetSecurityManager.raise_for_access` (`superset/security/manager.py:145-180` and
`:2445-2647`), the user fails the normal schema/datasource/ownership checks, but passes
the multi-layer child branch at lines `2644-35` because `parent_slc` is in
`dashboard_.slices` and `_validate_child_in_parent_multilayer` returns True, and there is
currently no `child_slc.datasource == datasource` validation—so no
`SupersetSecurityException` is raised and the query executes against dataset B, returning
unauthorized data.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/security/manager.py
**Line:** 2672:2672
**Comment:**
	*Security: The multi-layer child access path validates `parent_slice_id` and child membership in `deck_slices`, but it never verifies that the requested datasource actually belongs to that child chart. Because datasource authorization is granted when this branch is true, a forged request can pair a valid parent/child chart combination with an unrelated datasource and bypass datasource access checks. Add an explicit lookup of the child slice and ensure its datasource matches the requested datasource before granting access.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is outside the scope of this PR. I'll tag @msyavuz here as he originally worked on this logic. Let me know if this is a concern, and if I can help with anything

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The child slice is validated against the parent's config via _validate_child_in_parent_multilayer this ensures the slice_id is actually a child of the parent_slice_id in the deck_multi chart's deck_slices param. This is a false positive

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for confirming it!

)
or (
# Chart.
form_data.get("type") != "NATIVE_FILTER"
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only real changes to this block are:

  1. I noticed we were validating form_data.get("type") != "NATIVE_FILTER" more than once, so I moved it to the top for single validation;
  2. Re-named the method from has_drill_by_access to has_drill_access.

I can undo #1 if we think it's better

@Vitor-Avila Vitor-Avila requested a review from msyavuz April 9, 2026 05:46
@github-actions github-actions Bot removed the embedded label Apr 9, 2026
@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review Bot commented Apr 9, 2026

Code Review Agent Run #3b26c4

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: 0bf26f1..1694d43
    • tests/integration_tests/datasource_tests.py
    • tests/integration_tests/security_tests.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

slc := self.session.query(Slice)
.filter(Slice.id == slice_id)
.one_or_none()
# Direct chart access (no parent)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

holly molly, maybe it's time to refactor this method ...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try tackling that in a follow up. I believe the logic on its own is correct, but we could move each validation block to their own helper method (like drill validation is now)

Copy link
Copy Markdown
Member

@betodealmeida betodealmeida left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great!

`not form_data.get("slice_id")` is True for both absent keys (D2D)
and `0` (Drill By sentinel), which could cause a malformed Drill By
request to be evaluated under the more permissive D2D path. Using
`is None` makes the intent explicit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Member

@mistercrunch mistercrunch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the full diff — this correctly fixes D2D for embedded users by:

  1. Broadening has_drill_by_accesshas_drill_access to handle both Drill to Detail and Drill By
  2. Plumbing dashboardId through /samplesget_samplesQueryContextFactory so form_data carries the dashboard context needed for embedded access validation
  3. Properly nesting the drill access check under the type != "NATIVE_FILTER" branch (previously it was a top-level or that could theoretically fire for native filter requests)

Test coverage is thorough — both happy paths and negative cases for D2D and Drill By.

I pushed one small fix in 85ac6f7: changed not form_data.get("slice_id") to form_data.get("slice_id") is None (same for chart_id) in has_drill_access. The not check treats 0 (Drill By sentinel) and absent/None (D2D) identically, which could let a malformed Drill By request (slice_id=0, no chart_id) take the more permissive D2D path. Using is None makes the intent explicit.

One suggestion for a follow-up: the boolean expression in raise_for_access (lines ~2589–2690) is now 10+ levels deep with walrus operators. Extracting each access path into named helpers (_check_native_filter_access, _check_chart_access, _check_drill_access, etc.) would make future security reviews much more tractable.

Nice work @Vitor-Avila 👍

@Vitor-Avila Vitor-Avila merged commit c7955a3 into master Apr 9, 2026
65 checks passed
@Vitor-Avila Vitor-Avila deleted the fix/d2d-embedded branch April 9, 2026 20:01
@bito-code-review
Copy link
Copy Markdown
Contributor

Bito Automatic Review Skipped – PR Already Merged

Bito scheduled an automatic review for this pull request, but the review was skipped because this PR was merged before the review could be run.
No action is needed if you didn't intend to review it. To get a review, you can type /review in a comment and save it

michael-s-molina pushed a commit that referenced this pull request Apr 13, 2026
Co-authored-by: Maxime Beauchemin <maximebeauchemin@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit c7955a3)
qfcwell pushed a commit to qfcwell/superset that referenced this pull request May 12, 2026
Co-authored-by: Maxime Beauchemin <maximebeauchemin@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions github-actions Bot added 🍒 6.1.0 Cherry-picked to 6.1.0 🏷️ bot A label used by `supersetbot` to keep track of which PR where auto-tagged with release labels labels May 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🏷️ bot A label used by `supersetbot` to keep track of which PR where auto-tagged with release labels dashboard:drill-to-detail size/L 🍒 6.1.0 Cherry-picked to 6.1.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants