feat: upgrade SQLAlchemy 1.3→1.4 and Flask-SQLAlchemy 2.5→3.0#7723
Open
wtfiwtz wants to merge 6 commits into
Open
feat: upgrade SQLAlchemy 1.3→1.4 and Flask-SQLAlchemy 2.5→3.0#7723wtfiwtz wants to merge 6 commits into
wtfiwtz wants to merge 6 commits into
Conversation
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>
This was referenced Jun 1, 2026
1. P1: Restore get_mapper() ambiguity check - collect all matching mappers and raise ValueError if multiple found (regression from original code) 2. P2: Log exceptions in ChangeTrackingMixin instead of silent pass - provides observability when attribute access fails during change tracking Addresses cubic-dev-ai issues on original getredash#7722. 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>
Author
CVE / advisory coverage for PR #7723 (
|
| Package | Version change | CVEs / advisories |
|---|---|---|
| werkzeug | 2.3.8 → 3.1.6 | CVE-2026-27199, CVE-2024-34069, CVE-2024-49766, CVE-2024-49767 |
| jinja2 | 3.1.5 → 3.1.6 | CVE-2025-27516 |
| authlib | 0.15.5 → 1.7.2 | OAuth/OIDC advisory class (e.g. CVE-2024-37568) |
| cryptography | 43.0.1 → 48.0.0 | Multiple OpenSSL-binding advisories |
| pyjwt | 2.4.0 → 2.12.0 | CVE-2022-29217 and related algorithm-confusion fixes |
| urllib3 | 1.26.19 → 1.26.20 | CVE-2025-50181, CVE-2025-50182 |
| flask | 2.3.2 → 2.3.3 | Flask 2.3 security patch level |
| Plus: requests, sqlparse, itsdangerous, pyopenssl, boto3/botocore, snowflake-connector-python, etc. |
Incremental fixes added by this PR (SQLAlchemy / ORM migration)
| Package | Version change | CVEs / advisories addressed |
|---|---|---|
| jwcrypto | 1.5.6 → 1.5.7 | CVE-2026-39373 (GHSA-fjrm-76x2-c4q4) — JWE ZIP decompression bomb / memory exhaustion; completes mitigation beyond CVE-2024-28102 input-size limit |
| sqlalchemy | 1.3.24 → 1.4.53 | No new CVE IDs specific to 1.4.53; moves to the maintained 1.4 line with Python 3.13 fixes and backported hardening (1.4.53 release). Redash already on 1.3.x (past CVE-2019-7164 / GHSA-887w-45rq-vxgf order_by injection). |
| flask-sqlalchemy | 2.5.1 → 3.0.5 | Compatibility / maintainability (required for SQLAlchemy 1.4); not a direct CVE remediation |
| flask-migrate | 2.5.2 → 4.0.7 | Compatibility with Flask-SQLAlchemy 3.x |
| nzalchemy | ^11.0.2 → ^11.1.2 | SQLAlchemy 1.4 compatibility for Netezza data source |
Flask remains at 2.3.3 in this PR — Flask 3 security fixes are in #7724.
Code changes (security-relevant context, not CVE IDs)
| Change | Notes |
|---|---|
| SQLAlchemy 1.4 / FSA 3.0 API compatibility | ORM session identity-map is stricter; fixes prevent detached-object errors and audit-log corruption under 1.4 |
get_mapper() ambiguity check restored |
Prevents silent wrong-mapper selection (review bot P1) |
ChangeTrackingMixin logging |
Replaces silent except pass on change tracking failures |
| Test isolation fixes | JWT key cache teardown, destination test session fix, MonkeyPatch.undo() in script runner tests |
Out of scope (other split PRs)
| Area | PR |
|---|---|
| Flask 3.x security fixes | #7724 |
| urllib3 2.x + champion SSRF | #7721 |
| Frontend / npm CVEs | #7720 |
| Debian OS / Docker build-time CVEs | #7718 |
Known remaining gaps (not in this PR)
| Package | CVE / advisory | Notes |
|---|---|---|
| paramiko 3.4.1 | CVE-2026-44405 | Blocked by sshtunnel compatibility |
| pysaml2 7.3.1 | GMS-2016-67 | pyOpenSSL constraint |
| bootstrap@3.4.1 | CVE-2019-8331, CVE-2025-1647 | Frontend; CSS-only use |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
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 (which will follow in a separate PR once this merges).
This PR is one of two PRs that split the original #7722 into smaller, reviewable pieces.
Dependency upgrades
Core ORM stack:
sqlalchemy: 1.3.24 → 1.4.53flask-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 compat)Test tooling:
pytest: 7.4.0 → 9.0.3coverage: 7.2.7 → 7.14.0pytest-cov: 4.1.0 → 6.0.0jwcrypto: 1.5.6 → 1.5.7Code changes (SQLAlchemy 1.4 / Flask-SQLAlchemy 3.0 API compatibility)
redash/models/base.py: ReplaceBaseQuery(removed in FSA 3.0) withflask_sqlalchemy.query.Query; addSearchBaseQuery._entitiesshim for sqlalchemy-searchable 1.2 which reads the removed_entitiesattributeredash/handlers/base.py: FSA 3.0 changedpaginate(page, page_size)→paginate(page=…, per_page=…, error_out=False)redash/metrics/database.py: SQLAlchemy 1.4 removed.froms; use.get_final_froms()redash/models/__init__.py: Replace.options(load_only("id"))with.with_entities(Model.id)in.in_()subqueriesredash/utils/query_order.py:_ColumnEntity,_MapperEntity,_mapper_registryare internal APIs removed in 1.4; replace withcolumn_descriptionsand_mapper_registriesredash/models/changes.py: GuardChangeTrackingMixin.__setattr__against detached/deleted SQLAlchemy 1.4 identity-map stateredash/tasks/queries/execution.py: SQLAlchemy 1.4 identity-map is stricter; skipsession.close()inTESTINGmode to prevent detached-object errorsredash/authentication/__init__.py: Usesession.flush()instead ofcommit()inTESTINGmode for SQLAlchemy 1.4 session scopingredash/utils/configuration.py: Compact JSON separators fortest_cli.pyassertionsTest infrastructure
tests/__init__.py:db.get_engine(app).dispose()→db.engine.dispose()(FSA 3.0 removed theget_engine(app)signature)tests/destinations/__init__.py: Add empty__init__.pyfor pytest collectiontests/tasks/test_worker.py: Addsuper().tearDown()callstests/test_cli.py: Update compact-JSON expectationspytest.ini: Addpythonpath = ., logging config,addoptsTest plan
poetry install --only main,devmake test— expect ~900+ pytest passesNotes
Related PRs
Made with Cursor