Skip to content

chore(sec): bump deps + drop Python 3.9 to clear all OSV findings (5.0.0)#818

Open
vikrantpuppala wants to merge 4 commits into
mainfrom
vp/security-bump-py5
Open

chore(sec): bump deps + drop Python 3.9 to clear all OSV findings (5.0.0)#818
vikrantpuppala wants to merge 4 commits into
mainfrom
vp/security-bump-py5

Conversation

@vikrantpuppala
Copy link
Copy Markdown
Contributor

Summary

  • Clears 25 OSV-Scanner findings (1 CRITICAL, 13 HIGH, 10 MED, 1 UNKNOWN) on poetry.lock.
  • Bumps the Python floor ^3.8.0^3.10 and the version to 5.0.0 (major, breaking — see below).
  • Mirrors the equivalent work already merged/in-flight on the Go and Node.js drivers.

This is the dep-bump PR the description of #798 (OSV-Scanner workflow) flagged as a follow-up. Once this merges and #798 wires the gate into branch protection, the gate becomes enforced.

What changed

Direct runtime dep bumps (in pyproject.toml)

Package From To Why
urllib3 >=1.26 >=2.7.0,<3.0.0 6 advisories incl HIGH PYSEC-2026-141; SemVer-major 1.x → 2.x
requests ^2.18.1 ^2.33.0 2 MED advisories
pyjwt ^2.0.0 ^2.12.0 HIGH PYSEC-2026-120 (+ unfixed PYSEC-2025-183 remains; no fixed release published)
pyarrow dual 14/18/22 blocks >=23.0.1 (all three Python-version blocks) HIGH PYSEC-2026-113

Direct dev dep bumps

Package From To Why
pytest ^7.1.2 ^9.0.3 MED GHSA-6w46-j5rx-g56g
black ^22.3.0 ^26.3.1 HIGH GHSA-3936-cmfr-pm3m + MED PYSEC-2024-48

Transitive cleanups (lockfile-only, no pyproject change)

Package From To Why
cryptography 43.0.3 / 45.0.6 48.0.0 CRITICAL PYSEC-2026-36 (9.8) + 4 HIGH/MED
idna 3.10 3.16 MED GHSA-65pc-fj4g-8rjx
python-dotenv (dev) 1.0.1 1.2.2 MED GHSA-mf9w-mj56-hr94

Python support

  • python = "^3.8.0"python = "^3.10" (drops 3.8, 3.9)
  • CI matrix: ["3.9","3.10","3.11","3.12","3.13","3.14"]["3.10","3.11","3.12","3.13","3.14"] (all four jobs)

Why drop 3.9? The patched versions of urllib3, pyarrow, requests, pytest, and black all declare requires_python>=3.10. Keeping 3.9 in the matrix would cause the resolver to silently install CVE-vulnerable older versions for 3.9 customers — defeating the purpose of the PR. Python 3.9 EOL was 2025-10-31, so 3.9 is also 7 months past upstream support.

Source changes

  • 13 files reformatted by black 26.5.1 (isolated in a separate commit for clean blame). No logic changes.

Why a major version bump (5.0.0)?

  • Python floor bump (3.8/3.9 dropped) — pip install fails for affected users
  • urllib3 1.x → 2.x — customers with hard pins like urllib3<2 cannot install
  • pyarrow 14/18/22 → 23 — same
  • pytest 7 → 9, black 22 → 26 — same (dev-only but tooling-breaking for contributors)

The Python ecosystem strongly conventions transitive pins; signalling these breaks via SemVer-major is the only way customers know to re-evaluate their dep pins. A minor bump would let them silently stay on 4.x with CVEs unresolved.

Verification

  • ✅ OSV-Scanner v2.3.8 against new poetry.lock: 25 findings → 0
  • ✅ Unit tests (Python 3.10.18, default deps): 765 passed, 4 skipped
  • ✅ Unit tests (Python 3.10.18, min deps): 765 passed, 4 skipped
  • black --check src: clean
  • ⚠️ mypy: same 11 pre-existing errors as main. None introduced by this PR.

Test plan

  • CI passes on all 5 Python versions × 2 dep configurations
  • black --check and mypy pass on all Python versions (same baseline as main)
  • E2E tests pass on Python 3.10 + 3.13 (worth running on at least these two)
  • OSV-Scanner workflow (Add OSV-Scanner-based security workflow #798) runs against this PR and reports 0 findings
  • Verify pip install databricks-sql-connector==5.0.0 on Python 3.9 fails with a clear error message

Migration notes for the CHANGELOG / users

For most users, pip install -U databricks-sql-connector is the only required change.

  • Users on Python 3.8 or 3.9 must upgrade Python first.
  • Users with hard pins on urllib3<2, pyarrow<23, pyjwt<2.12, or requests<2.33 must lift the pin first.
  • Downstream forks with local changes against any of the 13 black-reformatted files will see a one-time merge conflict against the style commit.

This pull request and its description were written by Isaac.

…0.0)

Clears 25 OSV-Scanner findings (1 CRITICAL cryptography, 13 HIGH across
cryptography/urllib3/pyjwt/pyarrow/black, 10 MED, 1 UNKNOWN) by bumping
direct dependency floors and regenerating the lockfile. Forces a major
version bump because the new dependency constraints will conflict with
customers' transitive-dep pins on the older majors of urllib3 (1.x → 2.x),
pyarrow (14/18/22 → 23), pytest (7 → 9), and black (22 → 26).

Drops Python 3.8/3.9 from supported floors. Python 3.8 is 20 months past
upstream EOL (2024-10), Python 3.9 is 7 months past (2025-10). The patched
versions of urllib3 / pyarrow / requests / pytest / black all declare
requires_python>=3.10, so keeping 3.9 in the matrix would silently
downgrade these to CVE-vulnerable versions for 3.9 customers — defeating
the entire purpose of the PR.

Direct runtime bumps:
- urllib3:  >=1.26      -> >=2.7.0,<3.0.0   (urllib3 1.x -> 2.x major)
- requests: ^2.18.1     -> ^2.33.0
- pyjwt:    ^2.0.0      -> ^2.12.0
- pyarrow:  14/18/22+   -> >=23.0.1 (all three Python-version-gated blocks)

Direct dev bumps:
- pytest:   ^7.1.2      -> ^9.0.3
- black:    ^22.3.0     -> ^26.3.1  (reformats 13 src files in a follow-up commit)

Transitive cleanups via lockfile (no pyproject change):
- cryptography  43.0.3/45.0.6 -> 48.0.0   (clears CRITICAL PYSEC-2026-36 9.8)
- idna          3.10          -> 3.16
- python-dotenv 1.0.1         -> 1.2.2

Verification:
- OSV-Scanner v2.3.8 against new poetry.lock: 25 findings -> 0
- Unit tests (Python 3.10.18, default deps):     765 passed, 4 skipped
- Unit tests (Python 3.10.18, min deps):         765 passed, 4 skipped
- mypy:   same 11 pre-existing errors on main; unchanged
- black:  applied separately in next commit

Co-authored-by: Isaac
Signed-off-by: Vikrant Puppala <vikrant.puppala@databricks.com>
Mechanical commit, no semantic changes. black 26.x changed its line-wrapping
rules for several common patterns (trailing-comma handling in long function
calls, kwarg layout in multi-line calls). Applied across 13 src files. Run
locally with `poetry run black src` against black 26.5.1.

Isolated from the dep-bump commit so future bisect/blame can skip past
the reformat.

Co-authored-by: Isaac
Signed-off-by: Vikrant Puppala <vikrant.puppala@databricks.com>
Three independent fixes surfaced by the dep bump's pytest 7 → 9 jump:

1) pytest 9 + xdist 3.8 serializes subTest parametrization across worker
   process boundaries via execnet. Class objects (thrift response types)
   and thrift instances are not pickleable through execnet's restricted
   serializer. Replace them with string labels:

   - tests/unit/test_thrift_backend.py:
     * resp_type / exec_resp_type → resp_type.__name__
     * op_state_resp thrift instance → ("error_resp"/"closed_resp", inst) tuple
     * error_resp thrift instance → f"resp_{i}" index label
   - tests/unit/test_endpoint.py:
     * CloudType enum → .value

   Net effect: every subTest now passes only str/int/None to the subTest
   keyword args. Test coverage is unchanged — the labels still uniquely
   identify which subtest failed.

2) urllib3 2.x adds a `version_string` attribute that retry handlers
   read off the response object. tests/e2e/common/retry_test_mixins.py's
   SimpleHttpResponse mock missed this attribute; add it.

3) pyjwt 2.13.0's stricter type signatures expose a pre-existing mypy
   `Incompatible return value type` warning in oauth.py — `exp_time and
   (exp_time - buffer_time) <= current_time` returns Any|None, not bool.
   Wrap exp_time in bool() to satisfy mypy without changing semantics
   (None and False are both falsy; result is identical for all inputs).

Also updates poetry.lock for the pytest line (`^8.4.0` -> `^9.0.3`
restoration; intermediate downgrade was an investigation step, now
reverted).

Local verification on Python 3.10.18:
  poetry run pytest tests/unit/ -n auto   765 passed, 4 skipped
  ~/osv-scanner scan source --lockfile poetry.lock   No issues found

Co-authored-by: Isaac
Signed-off-by: Vikrant Puppala <vikrant.puppala@databricks.com>
Previous fix wrapped exp_time in bool() to satisfy mypy's [return-value]
rule. That didn't narrow the type, so mypy then flagged the
`(exp_time - buffer_time)` expression with `[operator]` "Unsupported
operand types for - ('None' and 'int')" — the short-circuit at runtime
is correct, but mypy can't track narrowing through a bool() call.

Replace with explicit `if exp_time is None: return False`. Same runtime
semantics (None and falsy exp_time both produce False), cleaner reading,
and mypy narrows the rest of the function to int after the guard.

Co-authored-by: Isaac
Signed-off-by: Vikrant Puppala <vikrant.puppala@databricks.com>
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