Skip to content

fix: add missing ownership checks to tag, report-log, and dataset-schema endpoints - #43390

Open
rusackas wants to merge 12 commits into
masterfrom
fix/tag-report-log-dataset-schema-checks
Open

fix: add missing ownership checks to tag, report-log, and dataset-schema endpoints#43390
rusackas wants to merge 12 commits into
masterfrom
fix/tag-report-log-dataset-schema-checks

Conversation

@rusackas

Copy link
Copy Markdown
Member

SUMMARY

  • Tag-relationship deletion now checks per-object access on each object being untagged, instead of only checking the objects being added.
  • Bulk tag deletion by name now requires admin or creator, and refuses to delete system-generated tags outright.
  • The user-registrations API is now admin-only and read-only, matching how the legacy view for the same resource is already restricted.
  • The report-execution-log API now scopes results to schedules the caller can access, via a base filter, matching the pattern the sibling schedule-CRUD API already uses.
  • Dataset drill-info responses no longer include creator/modifier email addresses in the nested user object.

TESTING INSTRUCTIONS

pytest tests/unit_tests/tags/ tests/unit_tests/security/ tests/unit_tests/reports/ tests/unit_tests/datasets/ — new/extended tests per change, each verified to fail pre-fix and pass post-fix.

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration
  • Introduces new feature or API
  • Removes existing feature or API

Superset Dev added 5 commits August 21, 2026 00:37
TagDAO.create_tag_relationship deleted every tag association absent
from the submitted objects_to_tag set (or all associations when the
field was empty/omitted) with no per-object authorization check,
while the addition path already filtered by
current_user_can_modify_object. Apply the same check to the deletion
branch, looking up each about-to-be-removed object with
skip_base_filter=True so associations on objects the caller cannot
modify are left alone instead of being stripped.
DeleteTagsCommand.validate only checked that each named tag existed
before TagDAO.delete_tags removed it, cascading every org-wide
association with no ownership check. Require the caller to be an
admin or the tag's creator, and refuse to delete system-generated
tags (type:*, editor:*, favorited_by:*) through this route at all.
The REST counterpart of the "User Registrations" FAB view was never
added to ADMIN_ONLY_VIEW_MENUS, so role sync granted its list/get/
delete permissions to stock Gamma and Alpha, exposing pending
registrants' PII and letting non-admins cancel registrations. Add
"UserRegistrationsRestAPI" to the admin-only allowlist and restrict
the API to its GET/GET_LIST/INFO routes so write handlers are never
registered at all, even if the allowlist entry regresses later.
ReportExecutionLogRestApi declared no base_filters, so the only
scoping on its list/item routes was the caller-chosen schedule pk
folded into the rison filters -- any role with generic ReportSchedule
read could iterate every schedule's logs, including alert result
values and database error messages for schedules it doesn't own. Add
ReportExecutionLogFilter, scoping directly on
ReportExecutionLog.report_schedule_id (mirroring
ReportScheduleFilter on the sibling schedule API), and apply it as a
base filter on both routes.
The drill_info-local UserSchema declared an email field, and
DatasetDrillInfoSchema nests it unfiltered for both created_by and
changed_by regardless of the endpoint's select_columns contract. Any
user with dataset-read access -- Gamma is sufficient -- received
maintainer email addresses. Drop email from the schema so it is
never serialized, matching the dashboard/RLS user schemas which
expose names only.
@dosubot dosubot Bot added the authentication:access-control Rlated to access control label Aug 21, 2026
@bito-code-review

bito-code-review Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #e6ba68

Actionable Suggestions - 0
Review Details
  • Files reviewed - 15 · Commit Range: 599697b..c91505e
    • superset/commands/tag/delete.py
    • superset/daos/tag.py
    • superset/datasets/schemas.py
    • superset/reports/filters.py
    • superset/reports/logs/api.py
    • superset/security/api.py
    • superset/security/manager.py
    • tests/integration_tests/datasets/api_tests.py
    • tests/integration_tests/tags/commands_tests.py
    • tests/unit_tests/datasets/schema_tests.py
    • tests/unit_tests/reports/filters_test.py
    • tests/unit_tests/reports/logs_api_test.py
    • tests/unit_tests/security/api_test.py
    • tests/unit_tests/tags/commands/delete_test.py
    • tests/unit_tests/tags/commands/update_test.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

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

  • /review - Manually triggers an incremental AI Review.

  • /review full - 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

@github-actions github-actions Bot added api Related to the REST API preset-io labels Aug 21, 2026
@netlify

netlify Bot commented Aug 21, 2026

Copy link
Copy Markdown

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit c91505e
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a887868f073ed0008f49e18
😎 Deploy Preview https://deploy-preview-43390--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@codecov

codecov Bot commented Aug 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 77.96610% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.79%. Comparing base (f7d505e) to head (9da5553).
⚠️ Report is 18 commits behind head on master.

Files with missing lines Patch % Lines
superset/reports/filters.py 50.00% 3 Missing and 1 partial ⚠️
superset/commands/tag/delete.py 80.00% 2 Missing and 1 partial ⚠️
superset/daos/tag.py 25.00% 3 Missing ⚠️
superset/tags/api.py 83.33% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #43390      +/-   ##
==========================================
- Coverage   66.84%   66.79%   -0.05%     
==========================================
  Files        2876     2876              
  Lines      164066   164445     +379     
  Branches    37863    37940      +77     
==========================================
+ Hits       109666   109849     +183     
- Misses      52226    52403     +177     
- Partials     2174     2193      +19     
Flag Coverage Δ
hive 38.08% <44.06%> (-0.03%) ⬇️
mysql 57.77% <77.96%> (-0.05%) ⬇️
postgres 57.81% <77.96%> (-0.05%) ⬇️
presto 40.01% <44.06%> (-0.04%) ⬇️
python 59.24% <77.96%> (-0.06%) ⬇️
sqlite 57.50% <77.96%> (-0.05%) ⬇️
unit 100.00% <ø> (ø)

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

☔ View full report in Codecov by Harness.
📢 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 superset/commands/tag/delete.py
@bito-code-review

Copy link
Copy Markdown
Contributor

The flagged issue is correct. The validate method in superset/commands/tag/delete.py performs a lookup using TagDAO.find_by_name(tag_name) without normalizing the input, while the underlying deletion logic likely handles whitespace differently, leading to inconsistencies.

To resolve this, you should normalize the tag_name (e.g., using .strip()) before passing it to TagDAO.find_by_name and when reporting errors. Here is the suggested fix for superset/commands/tag/delete.py:

        for tag_name in self._tags:
            normalized_name = tag_name.strip()
            tag = TagDAO.find_by_name(normalized_name)
            # Validate tag exists
            if not tag:
                exceptions.append(TagNotFoundError(normalized_name))
                continue
            # ... (rest of the logic using normalized_name)

I have checked the other comments on this PR. Would you like me to fetch all comments, validate them, and implement fixes for the rest as well?

superset/commands/tag/delete.py

for tag_name in self._tags:
            normalized_name = tag_name.strip()
            tag = TagDAO.find_by_name(normalized_name)
            # Validate tag exists
            if not tag:
                exceptions.append(TagNotFoundError(normalized_name))
                continue

Superset Dev and others added 2 commits August 21, 2026 09:55
test_delete_tags_command_requires_authorization logged in as GAMMA
without first logging out of the preceding ADMIN session. Superset's
login view is a no-op when a session is already authenticated, so the
test kept running as ADMIN throughout, and the ownership check it was
meant to exercise never actually saw a non-owner. Add the same
self.logout() + self.login() pairing already used elsewhere in this
test suite for mid-test user switches.

Also normalize tag_name in DeleteTagsCommand.validate() before the
TagDAO.find_by_name() lookup, matching the strip() TagDAO.delete_tags()
already applies, so a name with surrounding whitespace is validated
and deleted consistently (per automated review feedback on the PR).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Drop an over-specific rationale phrase from a regression-test docstring.
@rusackas rusackas added the merge-if-green If approved and tests are green, please go ahead and merge it for me label Aug 21, 2026
Comment thread superset/commands/tag/delete.py
Comment thread superset/reports/filters.py Outdated
Comment thread superset/security/api.py Outdated
Comment thread superset/datasets/schemas.py
@bito-code-review

Copy link
Copy Markdown
Contributor

Bito Automatic Review Failed - Technical Failure

Bito encountered technical difficulties while generating code feedback . To retry, type /review in a comment and save. If the issue persists, contact support@bito.ai and provide the following details:

Agent Run ID: dc916a4c-c4b2-4ac0-9df0-506d82af83ad

Superset Dev and others added 5 commits August 21, 2026 13:16
TagRestApi exposed the FAB-generated DELETE /api/v1/tag/<pk> route
unmodified, which deletes the row via the datamodel directly and never
runs DeleteTagsCommand's admin-or-creator and system-tag checks that
gate the bulk_delete route. Override the pk route to look the tag up
and run it through DeleteTagsCommand instead, so both delete paths
share the same validation rather than duplicating it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…queries

ReportExecutionLogFilter bypassed its ownership scoping for anyone
with can_access_all_datasources, which is also granted to stock Alpha
(ALPHA_ONLY_PERMISSIONS includes all_datasource_access), letting a
non-editor Alpha user read every schedule's evaluated alert values and
database errors regardless of ownership. Key the bypass off
can_access_all_queries instead, the admin-only permission this
codebase already uses to gate the equivalent per-execution data on
SQL Lab query history (superset.queries.filters.QueryFilter).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Restricting UserRegistrationsRestAPI to read-only routes also removed
DELETE, but the User Registrations admin page still renders a delete
action calling DELETE /api/v1/security/user_registrations/<id>,
breaking that existing Admin workflow with a 405. Add
RouteMethod.DELETE back to include_route_methods; POST/PUT stay
excluded. The class remains fully gated Admin-only via
ADMIN_ONLY_VIEW_MENUS, which keys off the view-menu name and therefore
covers every permission on the class, not just specific ones, so
restoring DELETE does not grant non-Admin roles anything.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
DatasetDrillInfoSchema.editors nested the shared SubjectResponseSchema,
which includes secondary_label. For a user-backed Subject,
user-subject synchronization (superset.subjects.sync.sync_user_subject)
stores that user's email in secondary_label, so any dataset reader
still received every editor's email through this field even after
UserSchema dropped its own email field for created_by/changed_by.
Add a drill_info-local editor schema that excludes secondary_label,
mirroring the UserSchema precedent.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…rror subclasses

DeleteTagsCommand.validate() composited TagDeleteFailedError instances
(plain CommandException subclasses, no normalized_messages()) into the
TagInvalidError it raises. That crashed with AttributeError whenever a
caller invoked normalized_messages() on the result, as the new
single-object DELETE /api/v1/tag/<pk> route does. Switch the composited
"tag not found" and "cannot delete" cases to ValidationError-derived
exceptions (reusing TagNotFoundValidationError and adding
TagDeleteForbiddenValidationError) so the aggregation works for both the
new route and bulk_delete.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@bito-code-review

bito-code-review Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #528b83

Actionable Suggestions - 0
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • tests/integration_tests/tags/api_tests.py - 1
Review Details
  • Files reviewed - 15 · Commit Range: c91505e..9da5553
    • superset/commands/tag/delete.py
    • tests/integration_tests/tags/commands_tests.py
    • tests/unit_tests/tags/commands/update_test.py
    • superset/tags/api.py
    • tests/integration_tests/tags/api_tests.py
    • tests/unit_tests/tags/api_test.py
    • superset/reports/filters.py
    • tests/unit_tests/reports/filters_test.py
    • superset/security/api.py
    • tests/unit_tests/security/api_test.py
    • superset/datasets/schemas.py
    • tests/integration_tests/datasets/api_tests.py
    • tests/unit_tests/datasets/schema_tests.py
    • superset/commands/tag/exceptions.py
    • tests/unit_tests/tags/commands/delete_test.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

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

  • /review - Manually triggers an incremental AI Review.

  • /review full - 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

Comment on lines +154 to +163
# System-generated tags (type:*, editor:*, favorited_by:*) are
# maintained by Superset itself and must not be deletable through
# the bulk route.
if tag.type is not None and tag.type != TagType.custom:
exceptions.append(
TagDeleteForbiddenValidationError(
f"Tag {tag_name} is a system tag and cannot be deleted"
)
)
continue

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.

Btw, while I was working on the Subject feature, I noticed these system generated tags are apparently not used anywhere(!). So while we're at it, I suggest we consider removing them all together if they're not used at all.

@sha174n sha174n left a comment

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.

Reviewed the ownership and field-exposure boundaries and it looks complete. The report-execution-log base filter is applied on both read routes (the API is GET/GET_LIST only), so a foreign id 404s, and the admin path is keyed on the admin-only permission. Tag mutations are all covered: additions validated in the create/update commands, the relationship-removal path now runs the modify check with skip_base_filter so an inaccessible object reaches the check rather than resolving to None, and both bulk and single-pk delete route through DeleteTagsCommand (admin-or-creator plus a system-tag guard). drill_info no longer includes email/secondary_label across created_by/changed_by/editors. One behavior note for reviewers: bulk/single tag delete now requires admin-or-creator, a deliberate tightening; per-object association delete still works for anyone with object access. LGTM.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api Related to the REST API authentication:access-control Rlated to access control merge-if-green If approved and tests are green, please go ahead and merge it for me preset-io size/XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants