Skip to content

feat: upgrade Flask 2.3.3 → 3.1.3#7724

Open
wtfiwtz wants to merge 7 commits into
getredash:masterfrom
orchestrated-io:feat/flask3
Open

feat: upgrade Flask 2.3.3 → 3.1.3#7724
wtfiwtz wants to merge 7 commits into
getredash:masterfrom
orchestrated-io:feat/flask3

Conversation

@wtfiwtz
Copy link
Copy Markdown

@wtfiwtz wtfiwtz commented Jun 1, 2026

Summary

Upgrade Flask to 3.1.3, building on the SQLAlchemy 1.4 + Flask-SQLAlchemy 3.0 migration.

⚠️ This PR depends on #7723 (SQLAlchemy 1.4 + Flask-SQLAlchemy 3.0) merging first. Do not merge this PR before #7723.

This PR is one of two PRs that split the original #7722 into smaller, reviewable pieces.

Dependency upgrades

  • flask: 2.3.3 → 3.1.3
  • blinker: 1.6.2 → 1.9.0 (Flask 3 requires blinker ≥ 1.6.2)
  • pyjwt: 2.12.0 → 2.13.0
  • supervisor: 4.1.0 → 4.3.0

Code changes (Flask 3 compatibility)

redash/security.py — CSP dict rewrite for flask-talisman

Flask-talisman with Flask 3 requires CSP to be passed as a dict instead of a string. The csp_allows_embeding decorator now:

  1. Parses the semicolon-separated CONTENT_SECURITY_POLICY setting into directives
  2. Overrides frame-ancestors to * to allow embedding
  3. Passes the dict to talisman instead of string concatenation

This avoids brittle text replacement that depended on exact frame-ancestors 'none' substring formatting.

redash/cli/rq.py — Soft-import supervisor_checks

supervisor_checks has no Python 3.13 wheel available. The import is now wrapped in try/except and WorkerHealthcheck class definition is gated on SUPERVISOR_CHECKS_AVAILABLE flag. The healthcheck command prints an error and returns exit code 1 when the library is unavailable.

Test changes

tests/test_authentication.py — JWT cache teardown

Clear JWT public-key cache in tearDown() to prevent cross-test pollution. This issue was exposed by Flask 3 app-context scoping changes where key cache persisted between tests.

Test plan

  1. Depends on feat: upgrade SQLAlchemy 1.3→1.4 and Flask-SQLAlchemy 2.5→3.0 #7723 (SQLAlchemy 1.4 + Flask-SQLAlchemy 3.0) and security: upgrade Python dependencies to Flask 2.3.3 and Werkzeug 3.1.6 #7719 (Werkzeug 3) merging first
  2. poetry install --only main,dev
  3. make test — expect ~900+ pytest passes
  4. Smoke-test login, query execution, dashboard embed views (CSP changes)

Notes

Related PRs

Made with Cursor

wtfiwtz and others added 4 commits June 1, 2026 15:30
Upgrade security-critical Python dependencies while staying on Flask 2.x.
Werkzeug 3.x is compatible with Flask 2.3.x and provides important security
fixes without requiring a Flask 3 migration.

Core dependency upgrades:
- authlib: 0.15.5 → 1.7.2 (fixes CVEs in OAuth/OIDC flows)
- cryptography: 43.0.1 → 48.0.0
- flask: 2.3.2 → 2.3.3
- flask-login: 0.6.0 → 0.6.3
- flask-wtf: 1.1.1 → 1.3.0
- itsdangerous: 2.1.2 → 2.2.0
- jinja2: 3.1.5 → 3.1.6
- pyjwt: 2.4.0 → 2.12.0
- pyopenssl: 24.2.1 → 26.2.0
- python-dotenv: 0.19.2 → 1.2.2
- requests: 2.32.3 → 2.33.0
- sqlparse: 0.5.0 → 0.5.4
- urllib3: 1.26.19 → 1.26.20
- werkzeug: 2.3.8 → 3.1.6

Added transitive dependency pins: pyasn1, mako, pynacl

Data source dependency upgrades:
- boto3/botocore: 1.28.8 → 1.43.7
- snowflake-connector-python: 3.12.3 → 4.5.0
- New additions: azure-core, grpcio, h11, httpcore, marshmallow

Dev dependency upgrades:
- pre-commit: 3.3.3 → 4.3.0
- Add filelock, pygments, virtualenv

Code changes for authlib 1.x and Werkzeug 3.x compatibility:
- Remove api_key_load_user_from_request from user_loader (belongs in request_loader)
- Add TESTING-mode reset_request_g_cache hook to prevent g leakage across test requests
- Pass explicit client_id/client_secret to oauth.register() (authlib 1.x API)
- Replace flask.globals._app_ctx_stack with current_app (Werkzeug 3 removal)

Co-authored-by: Cursor <cursoragent@cursor.com>
Upgrade SQLAlchemy to 1.4.53 and Flask-SQLAlchemy to 3.0.5 while keeping
Flask at 2.3.3. This allows the ORM migration to land independently of the
Flask 3 upgrade.

Dependency upgrades:
- sqlalchemy: 1.3.24 → 1.4.53
- flask-sqlalchemy: 2.5.1 → 3.0.5 (requires Flask ≥ 2.2.5, SQLAlchemy ≥ 1.4.18)
- flask-migrate: 2.5.2 → 4.0.7 (requires Flask-SQLAlchemy 3.x)
- nzalchemy: ^11.0.2 → ^11.1.2 (SQLAlchemy 1.4 compatibility)
- pytest: 7.4.0 → 9.0.3
- coverage: 7.2.7 → 7.14.0
- pytest-cov: 4.1.0 → 6.0.0
- jwcrypto: 1.5.6 → 1.5.7

Code changes for SQLAlchemy 1.4 / Flask-SQLAlchemy 3.0 API compatibility:
- models/base.py: Replace BaseQuery with flask_sqlalchemy.query.Query; add
  SearchBaseQuery._entities shim for sqlalchemy-searchable 1.2
- handlers/base.py: Update paginate() for FSA 3.0 signature change
- metrics/database.py: Replace .froms with .get_final_froms()
- models/__init__.py: Replace .options(load_only("id")) with .with_entities()
- utils/query_order.py: Replace removed internal APIs (_ColumnEntity,
  _mapper_registry) with column_descriptions and _mapper_registries
- models/changes.py: Guard ChangeTrackingMixin against detached/deleted state
- tasks/queries/execution.py: Skip session.close() in TESTING mode
- authentication/__init__.py: Use flush() instead of commit() in TESTING mode
- utils/configuration.py: Compact JSON separators for test assertions

Test infrastructure:
- tests/__init__.py: Replace db.get_engine(app) with db.engine
- tests/destinations/__init__.py: Add package marker for pytest collection
- tests/tasks/test_worker.py: Add super().tearDown() calls
- tests/test_cli.py: Update compact-JSON expectations
- pytest.ini: Add pythonpath, logging config, addopts

Depends on getredash#7719 (Werkzeug 3 + authlib 1.7) merging first.

Co-authored-by: Cursor <cursoragent@cursor.com>
Upgrade Flask to 3.1.3, building on the SQLAlchemy 1.4 + Flask-SQLAlchemy 3.0
migration. This PR must merge after the SQLAlchemy upgrade PR.

Dependency upgrades:
- flask: 2.3.3 → 3.1.3
- blinker: 1.6.2 → 1.9.0 (Flask 3 requires blinker ≥ 1.6.2)
- pyjwt: 2.12.0 → 2.13.0
- supervisor: 4.1.0 → 4.3.0

Code changes for Flask 3 compatibility:
- security.py: Rewrite csp_allows_embeding for flask-talisman Flask 3 compatibility.
  Flask-talisman with Flask 3 requires CSP to be passed as a dict instead of a
  string. New _embedding_content_security_policy() parses the semicolon-separated
  CONTENT_SECURITY_POLICY setting into directives and overrides frame-ancestors.
- cli/rq.py: Soft-import supervisor_checks (no Python 3.13 wheel available). Wrap
  import in try/except and gate WorkerHealthcheck class definition on
  SUPERVISOR_CHECKS_AVAILABLE flag.

Test changes:
- tests/test_authentication.py: Clear JWT public-key cache in tearDown() to
  prevent cross-test pollution (exposed by Flask 3 app-context scoping changes)

Depends on SQLAlchemy 1.4 + Flask-SQLAlchemy 3.0 PR merging first.

Co-authored-by: Cursor <cursoragent@cursor.com>
wtfiwtz and others added 3 commits June 1, 2026 18:07
Click commands don't use return values as exit codes. Use sys.exit(1)
explicitly when supervisor_checks is unavailable so monitoring scripts
can detect healthcheck failures.

Addresses cubic-dev-ai P1 issue.

Co-authored-by: Cursor <cursoragent@cursor.com>
- Remove nested app_context in destination test_post that caused
  "Object is already attached to session" under SQLAlchemy 1.4
- Clear JWT public-key cache in TestJWTAuthentication tearDown and
  require both RSA key files before regenerating
- Update database metrics listeners to SQLAlchemy 1.4 event signatures

Co-authored-by: Cursor <cursoragent@cursor.com>
Class-level MonkeyPatch instances in test_script.py were never undone,
leaving a subprocess.check_output mock that broke TestJWTAuthentication
when the full suite ran test_script before test_authentication.

Co-authored-by: Cursor <cursoragent@cursor.com>
@wtfiwtz wtfiwtz marked this pull request as ready for review June 1, 2026 10:50
@wtfiwtz
Copy link
Copy Markdown
Author

wtfiwtz commented Jun 1, 2026

Disclaimer: This comment was AI-generated to summarize the CVE and advisory coverage for this PR. Please verify against your scanner output and release notes before treating it as authoritative.

CVE / advisory coverage for PR #7724 (feat/flask3)

This PR is stacked on #7723 and #7719 — the branch contains all commits from those PRs plus the Flask 2→3 upgrade. If merged to master as-is, it delivers the full Python security + ORM + Flask 3 stack.

⚠️ Merge order: #7719#7723this PR.


Inherited from #7719 and #7723


Incremental fixes added by this PR (Flask 3 migration)

Package Version change CVEs / advisories addressed
flask 2.3.3 → 3.1.3 CVE-2026-27205 (GHSA-68rp-wp8r-4726) — missing Vary: Cookie when session accessed via in/len; CVE-2025-47278 (GHSA-4grg-w6v8-c28g) — incorrect SECRET_KEY_FALLBACKS signing order (patched in 3.1.3)
pyjwt 2.12.0 → 2.13.0 PyJWT 2.13.0 security release — multiple advisories including: GHSA-xgmm-8j9v-c9wx (JWK-as-HMAC algorithm confusion), GHSA-jq35-7prp-9v3f (PyJWK allow-list bypass), GHSA-993g-76c3-p5m4 / CVE-2026-48522 (non-HTTP(S) URI schemes in PyJWKClient), GHSA-w7vc-732c-9m39 / CVE-2026-48525 (DoS via b64=false detached JWS), GHSA-fhv5-28vv-h8m8 (JWK cache cleared on transient fetch errors)
blinker 1.6.2 → 1.9.0 Flask 3 dependency refresh; no named CVE mapped
supervisor 4.1.0 → 4.3.0 Process manager maintenance bump; no named CVE mapped

Code changes (Flask 3 compatibility; security-relevant context)

Change Notes
redash/security.py — CSP dict for flask-talisman Fixes brittle CSP string concatenation for dashboard embed views under Flask 3; reduces risk of misconfigured frame-ancestors silently failing
redash/cli/rq.py — soft-import supervisor_checks Python 3.13 wheel absence; healthcheck uses sys.exit(1) when unavailable (monitoring can detect failure)
tests/test_authentication.py — JWT cache teardown Prevents cross-test JWKS cache pollution

Out of scope (other split PRs)

Area PR
urllib3 2.x + champion SSRF #7721
Frontend / npm CVEs #7720
Debian OS / Docker build-time CVEs #7718

Supersedes

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-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.

1 issue found across 26 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="redash/cli/rq.py">

<violation number="1" location="redash/cli/rq.py:21">
P2: Import-time print() in except ImportError emits to stdout for all CLI commands when supervisor_checks is absent</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread redash/cli/rq.py

SUPERVISOR_CHECKS_AVAILABLE = True
except ImportError as e:
print(f"Warning: supervisor_checks not available: {e}")
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.

P2: Import-time print() in except ImportError emits to stdout for all CLI commands when supervisor_checks is absent

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At redash/cli/rq.py, line 21:

<comment>Import-time print() in except ImportError emits to stdout for all CLI commands when supervisor_checks is absent</comment>

<file context>
@@ -7,8 +7,21 @@
+
+    SUPERVISOR_CHECKS_AVAILABLE = True
+except ImportError as e:
+    print(f"Warning: supervisor_checks not available: {e}")
+    check_runner = None
+    base = None
</file context>

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant