Skip to content

fix: add missing access checks to semantic-layer and theme endpoints - #43389

Open
rusackas wants to merge 8 commits into
masterfrom
fix/semantic-layer-theme-access-checks
Open

fix: add missing access checks to semantic-layer and theme endpoints#43389
rusackas wants to merge 8 commits into
masterfrom
fix/semantic-layer-theme-access-checks

Conversation

@rusackas

Copy link
Copy Markdown
Member

SUMMARY

  • The semantic-layer detail/runtime-schema/views endpoints now check per-object access before returning a layer's configuration, matching the pattern already used elsewhere in the module.
  • Semantic-layer/view update and delete commands now require editorship, matching their sibling commands.
  • Theme writes now require the Alpha role rather than the default Gamma grant, matching how CssTemplate is already treated.
  • Updating or importing a theme that's currently in the active system-default/system-dark slot now requires admin, matching the existing delete-time check.
  • The SVG sanitizer's script-tag stripping regex now handles closing tags with attributes/whitespace and cleans up any leftover open/close fragments.

TESTING INSTRUCTIONS

pytest tests/unit_tests/semantic_layers/ tests/unit_tests/commands/semantic_layer/ tests/unit_tests/themes/ tests/unit_tests/commands/theme/ tests/unit_tests/utils/test_core.py — 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 and others added 4 commits August 21, 2026 00:38
…ration

Add SemanticLayer.raise_for_access(), mirroring the existing
SemanticView.raise_for_access(), and call it from the layer GET, runtime
schema, and views endpoints before returning or exercising a layer's
decrypted configuration. Previously these endpoints resolved the layer by
UUID with no per-object permission check.

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

Add security_manager.raise_for_editorship() checks to
UpdateSemanticLayerCommand.validate, DeleteSemanticLayerCommand.validate,
and CreateSemanticViewCommand.validate, mirroring the pattern already used
by the sibling Update/Delete/BulkDeleteSemanticViewCommand classes.
Previously these commands only checked existence/uniqueness, so anyone with
generic write access to semantic layers could modify or delete any layer.
Wire the new SemanticLayerForbiddenError into 403 responses on the layer
PUT/DELETE routes, and handle SemanticViewForbiddenError per-item in the
bulk semantic-view create loop so one forbidden item doesn't abort the
whole batch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…system theme, and harden script-tag stripping

Theme was missing from GAMMA_READ_ONLY_MODEL_VIEWS, so default Gamma role
sync granted full Theme CRUD instead of read-only access like the sibling
CssTemplate resource; move Theme writes to Alpha+ to match.

UpdateThemeCommand.validate only rejected edits to is_system themes, not
the theme currently promoted to the live system-default/dark slot
(is_system_default/is_system_dark), which SetSystemDefaultThemeCommand
never marks is_system. Add a non-admin guard on those flags so only an
admin can edit the theme actively rendered for every user; map the new
SystemThemeInUseError to a 403 in the PUT route.

sanitize_svg_content's script-tag regex only matched a bare literal
</script> closer, so a closing tag with trailing attributes/whitespace
(e.g. </script foo>), which browsers still treat as a valid closer,
survived removal. Make the closing-tag pattern tolerant of that, and add
a second pass that strips any leftover <script>/</script> fragment,
including an unterminated opener with no matching closer.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
import_theme's overwrite branch only checked can_write('Theme') before
replacing an existing theme matched by UUID, unlike UpdateThemeCommand and
DeleteThemeCommand which both refuse to touch system-protected themes.
Since seeded themes have stable, publicly-listable UUIDs, an import
bundle with overwrite=true could replace a system theme, or the theme
currently in the system-default/dark slot, bypassing the protection the
direct update/delete routes already enforce. Raise ThemeImportError
before binding the existing row's id when is_system, is_system_default,
or is_system_dark is set.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@dosubot dosubot Bot added authentication:access-control Rlated to access control change:backend Requires changing the backend labels Aug 21, 2026
@bito-code-review

bito-code-review Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #d2d62c

Actionable Suggestions - 0
Additional Suggestions - 3
  • superset/commands/semantic_layer/update.py - 1
    • Duplicated authorization block across commands · Line 120-123
      The authorization block (try/except raising SemanticLayerForbiddenError) is duplicated verbatim from UpdateSemanticViewCommand and DeleteSemanticLayerCommand. Consider consolidating into a reusable `_check_editorship` helper or base class method to reduce maintenance divergence.
  • superset/commands/theme/update.py - 1
    • Misleading error message · Line 24-24
      The `SystemThemeInUseError` exception message says 'Cannot delete theme...' but here it's raised during an update operation. Users reading logs or debugging issues may be confused by the mismatch between the error text and the actual operation performed.
  • tests/unit_tests/themes/commands_test.py - 1
    • Missing return type annotation · Line 140-144
      Add explicit `-> None` return type annotation to `test_theme_is_gamma_read_only` to comply with project typing standards (adaptive rule 7819).
Filtered by Review Rules

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

  • superset/commands/semantic_layer/create.py - 1
    • Incomplete authorization test coverage · Line 98-105
  • superset/themes/api.py - 1
Review Details
  • Files reviewed - 17 · Commit Range: 176257e..3f645b4
    • superset/commands/semantic_layer/create.py
    • superset/commands/semantic_layer/delete.py
    • superset/commands/semantic_layer/update.py
    • superset/commands/theme/import_themes.py
    • superset/commands/theme/update.py
    • superset/security/manager.py
    • superset/semantic_layers/api.py
    • superset/semantic_layers/models.py
    • superset/themes/api.py
    • superset/utils/core.py
    • tests/unit_tests/commands/semantic_layer/create_test.py
    • tests/unit_tests/commands/semantic_layer/delete_test.py
    • tests/unit_tests/commands/semantic_layer/update_test.py
    • tests/unit_tests/commands/theme/test_import_themes.py
    • tests/unit_tests/semantic_layers/api_test.py
    • tests/unit_tests/themes/commands_test.py
    • tests/unit_tests/utils/test_core.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
@codecov

codecov Bot commented Aug 21, 2026

Copy link
Copy Markdown

Codecov Report

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

Files with missing lines Patch % Lines
superset/commands/semantic_layer/delete.py 0.00% 7 Missing ⚠️
superset/commands/semantic_layer/create.py 0.00% 5 Missing ⚠️
superset/commands/semantic_layer/update.py 0.00% 5 Missing ⚠️
superset/commands/theme/import_themes.py 0.00% 2 Missing and 2 partials ⚠️
superset/commands/theme/update.py 33.33% 1 Missing and 1 partial ⚠️
superset/themes/api.py 0.00% 2 Missing ⚠️
superset/utils/core.py 0.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #43389      +/-   ##
==========================================
- Coverage   66.84%   66.79%   -0.05%     
==========================================
  Files        2876     2876              
  Lines      164066   164435     +369     
  Branches    37863    37946      +83     
==========================================
+ Hits       109666   109838     +172     
- Misses      52226    52401     +175     
- Partials     2174     2196      +22     
Flag Coverage Δ
hive 38.06% <12.12%> (-0.05%) ⬇️
mysql 57.74% <19.69%> (-0.09%) ⬇️
postgres 57.77% <19.69%> (-0.09%) ⬇️
presto 39.99% <12.12%> (-0.05%) ⬇️
python 59.23% <59.09%> (-0.06%) ⬇️
sqlite 57.46% <19.69%> (-0.09%) ⬇️
unit 100.00% <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.

@netlify

netlify Bot commented Aug 21, 2026

Copy link
Copy Markdown

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 3f645b4
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a8878540ab0690008c7d13d
😎 Deploy Preview https://deploy-preview-43389--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.

Comment thread tests/unit_tests/utils/test_core.py
@bito-code-review

Copy link
Copy Markdown
Contributor

The flagged issue is correct. The regression test for unterminated script tags currently only verifies the removal of the script tag itself, which is insufficient because the payload (e.g., alert('xss')) could remain in the output. To resolve this, update the test assertion to verify that the payload is also removed, ensuring the sanitizer is fully effective.

To implement this, update the test case in tests/unit_tests/utils/test_core.py to include an assertion that checks for the absence of the malicious payload string in the sanitized output, similar to how the paired-script test is implemented.

Comment thread superset/semantic_layers/api.py
Comment thread superset/commands/theme/import_themes.py Outdated
Superset Dev and others added 2 commits August 21, 2026 10:00
…tale permissions test, allow admin theme import overwrite

- Add unit tests for SemanticLayer.raise_for_access (all-datasources,
  perm-granted, perm-denied, no-perm-denied paths) to satisfy the
  100%-coverage gate on superset/semantic_layers.
- Update test_non_admin_cannot_set_system_default to log in as an Alpha
  user, since Theme was moved into GAMMA_READ_ONLY_MODEL_VIEWS: Gamma
  now gets rejected at the route-permission layer with FAB's generic
  message before reaching the admin-only check, while Alpha (which still
  has generic Theme write access) reaches the check and gets the specific
  message. Add a companion test asserting Gamma is blocked outright.
- Mirror UpdateThemeCommand's admin carve-out in import_theme(): only
  reject overwriting the active system-default/dark theme via import for
  non-admins, so admins can still re-import the theme rendered for every
  user, same as they can update it directly.
- Document the 403 response on the semantic-layer endpoints that gained
  a raise_for_access()/raise_for_editorship() check, per review feedback.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…nated-script XSS gap in SVG sanitizer

- Add the missing 403 OpenAPI response to the semantic-view structure and
  delete endpoints, matching the raise_for_access/response_403 calls they
  already make.
- Fix sanitize_svg_content to drop everything after an unterminated
  <script> opener instead of just the tag itself, since browsers treat an
  unterminated <script> as consuming the rest of the document. Strengthen
  the regression test to assert the payload text is gone too.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@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
raise SemanticLayerNotFoundError()

try:
security_manager.raise_for_editorship(layer)

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.

SemanticLayer has no editors relationship, and the create path does not assign an editor subject. With the default extra-editor resolver, this check therefore rejects every non-admin—even one granted the existing write permission—when they try to add a semantic view to a layer. Should this use the semantic-layer access/ownership policy (or persist editors) instead of generic editorship?

SemanticLayer and SemanticView have no `editors` relationship, so
raise_for_editorship rejected every non-admin caller unconditionally,
including a layer's own creator. Replace the raise_for_editorship calls
in the semantic-layer create/update/delete commands with
current_user_can_modify_object, a helper that falls back to comparing
the resource's created_by against the current user when the resource
has no editors to check.

The helper is moved from commands/tag/utils.py (where it was added for
tag-object writes) to the shared commands/utils.py so both the tag and
semantic-layer commands can use it.

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

@bito-code-review bito-code-review Bot 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.

Code Review Agent Run #7d6154

Actionable Suggestions - 1
  • tests/unit_tests/commands/theme/test_import_themes.py - 1
    • Missing admin dark-slot overwrite test · Line 95-120
Review Details
  • Files reviewed - 18 · Commit Range: 3f645b4..8d53c29
    • superset/commands/theme/import_themes.py
    • superset/semantic_layers/api.py
    • tests/integration_tests/themes/test_theme_api_permissions.py
    • tests/unit_tests/commands/theme/test_import_themes.py
    • tests/unit_tests/semantic_layers/models_test.py
    • superset/utils/core.py
    • tests/unit_tests/utils/test_core.py
    • superset/commands/semantic_layer/create.py
    • superset/commands/semantic_layer/delete.py
    • superset/commands/semantic_layer/update.py
    • superset/commands/tag/create.py
    • superset/commands/tag/update.py
    • superset/commands/tag/utils.py
    • superset/commands/utils.py
    • tests/unit_tests/commands/semantic_layer/create_test.py
    • tests/unit_tests/commands/semantic_layer/delete_test.py
    • tests/unit_tests/commands/semantic_layer/update_test.py
    • tests/unit_tests/commands/test_utils.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 thread tests/unit_tests/commands/theme/test_import_themes.py
Mirrors the existing is_system_default test for full branch coverage
of the admin carve-out.
@bito-code-review

bito-code-review Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #6178a7

Actionable Suggestions - 0
Review Details
  • Files reviewed - 1 · Commit Range: 8d53c29..7ebd635
    • tests/unit_tests/commands/theme/test_import_themes.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

@sha174n

sha174n commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

The per-object access checks on the detail/views/runtime-schema endpoints and the editorship checks on update/delete look correct and fail-closed. Two items against the original scope before this is fully complete: (1) _serialize_layer still returns the fully decrypted configuration to any caller who passes the access check; since that blob holds the datasource connection settings, it would be worth masking the sensitive fields on serialization the way the Database API already does, rather than returning them in cleartext. (2) get_list, get, and runtime_schema do not carry the is_feature_enabled("SEMANTIC_LAYERS") guard that structure/views/delete do, so those routes stay reachable when the feature is off and the flag handling is inconsistent across the API. Worth adding for parity.

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 change:backend Requires changing the backend 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