Skip to content

refactor: remove obsolete Flask flash messaging system#35237

Merged
mistercrunch merged 7 commits into
masterfrom
test-env
Sep 25, 2025
Merged

refactor: remove obsolete Flask flash messaging system#35237
mistercrunch merged 7 commits into
masterfrom
test-env

Conversation

@mistercrunch
Copy link
Copy Markdown
Member

@mistercrunch mistercrunch commented Sep 23, 2025

Summary

The Flask flash message system is a legacy feature from when Superset was a server-rendered application. In the current SPA architecture, these messages are never displayed to users because:

  • Flash messages only render on page load via FlashProvider's componentDidMount
  • Modern UI interactions use async API calls that don't trigger page reloads
  • The main consumer (/explore/ POST endpoint) is already marked @deprecated

All user-facing notifications are already handled by the frontend toast system, including chart save operations (see saveModalActions.ts) which dispatches success toasts for:

  • Chart saved/overwritten
  • Chart added to dashboard
  • New dashboard created with chart

Changes

Backend

  • Removed all flash() calls from views (14 occurrences in 4 files)
  • Converted error flashes to JSON responses or logging
  • Removed redirect_with_flash utility function
  • Fixed open redirect vulnerability in dashboard access denial

Frontend

  • Deleted FlashProvider component and tests
  • Removed flash_messages from CommonBootstrapData type
  • Cleaned up context providers and test fixtures
  • Removed unused getBootstrapData imports

Security Fixes

  • Fixed open redirect vulnerability by using url_for() instead of request.url
  • Dashboard access denial now uses safe URL construction

Code Cleanup

  • Removed unnecessary pass statements and comments
  • Converted permalink errors to JSON responses for consistency
  • Verified no tests depend on flash functionality

BREAKING CHANGE

Removes flask.flash() messaging infrastructure. However, no actual functionality is lost as the frontend already handles all notifications through its own toast system.

TESTING INSTRUCTIONS

  1. Save a chart from Explore - verify toast notifications appear
  2. Add chart to dashboard - verify success message
  3. Try accessing a dashboard without permissions - verify proper redirect
  4. Run frontend tests: npm test
  5. Run backend tests: pytest

ADDITIONAL INFORMATION

The application now relies entirely on client-side toast notifications for user feedback, which properly support async operations and provide a consistent UX.

@github-actions github-actions Bot added the api Related to the REST API label Sep 23, 2025
@dosubot dosubot Bot added change:backend Requires changing the backend change:frontend Requires changing the frontend labels Sep 23, 2025
Copy link
Copy Markdown

@korbit-ai korbit-ai Bot left a comment

Choose a reason for hiding this comment

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

Review by Korbit AI

Korbit automatically attempts to detect when you fix issues in new commits.
Category Issue Status
Performance Redundant string conversion in logging ▹ view 🧠 Not in scope
Files scanned
File Path Reviewed
superset-frontend/src/components/index.ts
superset-frontend/src/explore/types.ts
superset-frontend/src/constants.ts
superset/views/auth.py
superset-frontend/src/views/RootContextProviders.tsx
superset-frontend/src/types/bootstrapTypes.ts
superset-frontend/src/embedded/EmbeddedContextProviders.tsx
superset-frontend/src/dashboard/actions/hydrate.js
superset/views/utils.py
superset/views/base.py
superset/views/core.py

Explore our documentation to understand the languages and file types we support and the files we ignore.

Check out our docs on how you can make Korbit work best for you and your team.

Loving Korbit!? Share us on LinkedIn Reddit and X

Comment thread superset/views/base.py
Comment thread superset/views/core.py Fixed
BREAKING CHANGE: Removes flask.flash() messaging infrastructure

The Flask flash message system is a legacy feature from when Superset was a
server-rendered application. In the current SPA architecture, these messages
are never displayed to users because:
- Flash messages only render on page load via FlashProvider's componentDidMount
- Modern UI interactions use async API calls that don't trigger page reloads
- The main consumer (/explore/ POST endpoint) is already marked @deprecated

All user-facing notifications are already handled by the frontend toast system,
including chart save operations (see saveModalActions.ts) which dispatches
success toasts for:
- Chart saved/overwritten
- Chart added to dashboard
- New dashboard created with chart

Changes:
- Backend: Removed all flash() calls from views (14 occurrences in 4 files)
- Backend: Converted error flashes to JSON responses or logging
- Backend: Removed redirect_with_flash utility function
- Backend: Fixed open redirect vulnerability in dashboard access denial
- Frontend: Deleted FlashProvider component and tests
- Frontend: Removed flash_messages from CommonBootstrapData type
- Frontend: Cleaned up context providers and test fixtures
- Frontend: Removed unused getBootstrapData imports

Security fixes:
- Fixed open redirect vulnerability by using url_for() instead of request.url
- Dashboard access denial now uses safe URL construction

Code cleanup:
- Removed unnecessary pass statements and comments
- Converted permalink errors to JSON responses for consistency
- Verified no tests depend on flash functionality

The application now relies entirely on client-side toast notifications for
user feedback, which properly support async operations and provide a
consistent UX.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
superset_can_csv: findPermission('can_csv', 'Superset', roles),
common: {
// legacy, please use state.common instead
flash_messages: common?.flash_messages,
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.

Question from review session: Where are server-side messages being processed/summoned now that they're not provided as part of the bootstrap data?

Copy link
Copy Markdown
Member Author

@mistercrunch mistercrunch Sep 24, 2025

Choose a reason for hiding this comment

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

Answer is case by case, but short answer is "as they should" from async calls. Checked that most had been re-implemented the right way since they were used. Also confirmed that old mechanics are to surface "on the next page load", which has very few pre-determined triggers, stuff like logging in maybe.

Comment thread superset/views/auth.py
if not reg:
logger.error(LOGMSG_ERR_SEC_NO_REGISTER_HASH, activation_hash)
flash(as_unicode(self.false_error_message), "danger")
logger.error("Registration activation failed: %s", self.false_error_message)
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.

@mistercrunch this is one of the errors that used to be (rightly) surfaced to users, but now might silent in the UI. These (and other?) server-side messages might no longer appear on the frontend.

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.

Relayed: We just need to make sure we have a frontend counterpart for all server side messages that were deleted.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Let me confirm that logging in is a SPA operation as it should be as opposed to a backend-round-trip.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Login screen is the only place where this seems to actually work (round-trip + timely flash). Let me refactor this nonsense!

Copy link
Copy Markdown
Member Author

@mistercrunch mistercrunch Sep 24, 2025

Choose a reason for hiding this comment

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

Mitigated with a toast, but had to keep the round trip (same as now) at least for now

After removing Flask's flash message system, login errors weren't being displayed.
This adds a temporary solution using sessionStorage and toast notifications to show
'Invalid username or password' when login fails.

Implementation:
- Set a flag in sessionStorage before form submission
- On page reload, check if flag exists (indicates failed login)
- Show error toast and clear password field for security
- Added TODO comment noting this should be replaced with proper SPA auth

This addresses reviewer concerns about missing login error messages while keeping
the solution simple since it will eventually be replaced with a JSON API approach.
Comment thread superset/views/base.py
self.pre_delete(item)
except Exception as ex: # pylint: disable=broad-except
flash(str(ex), "danger")
logger.error("Pre-delete error: %s", str(ex))
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

this probably never should have gone through to user

Comment thread superset/views/core.py
_("Error: permalink state not found"), status=404
)
except (ChartNotFoundError, ExplorePermalinkGetFailedError) as ex:
flash(__("Error: %(msg)s", msg=ex.message), "danger")
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

function flagged as @deprecated

Comment thread superset/views/core.py
initial_form_data["slice_id"] = slice_id
if form_data_key:
flash(
_("Form data not found in cache, reverting to chart metadata.")
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

part of a function flagged as @deprecated

Comment thread superset/views/core.py
if form_data_key:
flash(
_(
"Form data not found in cache, reverting to dataset metadata." # noqa: E501
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

part of a function flagged as @deprecated

Comment thread superset/views/core.py
)

flash(
_("Chart [{}] was added to dashboard [{}]").format(
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

tested that chart adding to dashboard is handled frontend now and flashed properly without these obsolete flash messages

Comment thread superset/views/core.py
)
flash(
_(
"Dashboard [{}] just got created and chart [{}] was added to it"
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

tested that chart adding to dashboard is handled frontend now and flashed properly without these obsolete flash messages

Comment thread superset/views/utils.py
return func(database, user, schema, sql)


def redirect_with_flash(url: str, message: str, category: str) -> Response:
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

not used anymore

Comment thread superset/views/core.py Outdated
)
except SupersetSecurityException:
# Return 404 to avoid revealing dashboard existence
abort(404)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

404 is most accurate when you don't have access

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.

Yep... it's even in the official spec for 403 (I got curious):

An origin server that wishes to "hide" the current existence of a forbidden target resource MAY instead respond with a status code of 404 (Not Found).

Copy link
Copy Markdown
Member

@rusackas rusackas left a comment

Choose a reason for hiding this comment

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

LGTM!

mistercrunch and others added 3 commits September 24, 2025 17:54
…rmation leakage

- Dashboard access denial now returns 404 instead of redirecting
- Prevents attackers from enumerating which dashboards exist
- Updated tests to expect 404 instead of 302 for access denial
- 18 of 20 RBAC tests now pass (2 have teardown issues)

Co-Authored-By: Claude <noreply@anthropic.com>
Using abort(404) inside an exception handler causes issues:
- Raises werkzeug.exceptions.NotFound which may not be caught properly
- Can leave database transactions in inconsistent state
- Causes test teardown failures

Solution: Return make_response('Not Found', 404) which returns a proper
Flask Response object with 404 status code.

All 20 RBAC security tests now pass.

Co-Authored-By: Claude <noreply@anthropic.com>
Cleaner solution for returning 404 in the dashboard access denial case:
- Response(status=404) returns an empty response with proper status
- Simpler than make_response('Not Found', 404)
- Lets browser/client handle 404 appropriately
- All tests still pass

Co-Authored-By: Claude <noreply@anthropic.com>
@mistercrunch mistercrunch merged commit abc2d46 into master Sep 25, 2025
73 of 74 checks passed
@mistercrunch mistercrunch deleted the test-env branch September 25, 2025 07:05
qfcwell pushed a commit to qfcwell/superset that referenced this pull request May 12, 2026
Co-authored-by: Claude <noreply@anthropic.com>
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 change:backend Requires changing the backend change:frontend Requires changing the frontend preset-io size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants