Skip to content

fix(security): pin a TLS 1.2 floor for the Twitch connection - #29

Merged
Avicennasis merged 3 commits into
mainfrom
fix/tls-minimum-version
Aug 22, 2026
Merged

fix(security): pin a TLS 1.2 floor for the Twitch connection#29
Avicennasis merged 3 commits into
mainfrom
fix/tls-minimum-version

Conversation

@Avicennasis

Copy link
Copy Markdown
Owner

Closes CodeQL alert #13 (py/insecure-protocol, high).

ssl.create_default_context() leaves minimum_version at the MINIMUM_SUPPORTED sentinel (-2), so TLS 1.0 and 1.1 remain permitted wherever the platform still enables them.

That matters here specifically: the bot authenticates by sending its OAuth token in the IRC PASS command. The comment two lines above already says the TLS wrap exists so the token "travels encrypted rather than in plaintext" — which is only as true as the weakest version allowed.

Change

Extracted _make_tls_context() so the floor is testable without opening a socket, and pinned minimum_version = ssl.TLSVersion.TLSv1_2.

Verification

Tests written first and confirmed red.

  • The floor test carries a negative control asserting create_default_context() really does leave the floor below TLS 1.2 on this platform (verified: -2 < 771), so it cannot pass vacuously.
  • A second test asserts hardening the floor did not weaken verification — verify_mode stays CERT_REQUIRED, check_hostname stays True. That is the thing most likely to break when fiddling with an SSL context.

Verified with the exact CI pin (ruff 0.15.22): check and format --check clean, 40 tests pass.

Closes CodeQL alert #13 (py/insecure-protocol, high).

ssl.create_default_context() leaves minimum_version at the
MINIMUM_SUPPORTED sentinel (-2), so TLS 1.0 and 1.1 stay permitted
wherever the platform still enables them. This bot authenticates by
sending its OAuth token in the IRC PASS command, so the negotiated floor
is exactly what protects that token in transit — the comment two lines
above already says the TLS wrap exists so the token 'travels encrypted
rather than in plaintext', which is only as true as the weakest version
allowed.

Extracted _make_tls_context() so the floor is testable without opening a
socket, and pinned minimum_version to TLSv1_2.

Tests written first and confirmed red. The floor test carries a negative
control asserting create_default_context() really does leave the floor
below TLS 1.2 on this platform (verified: -2 < 771), so it cannot pass
vacuously. A second test asserts hardening the floor did not weaken
verification — verify_mode stays CERT_REQUIRED and check_hostname stays
True, which is the thing most likely to be broken by fiddling with a
context.

Verified with the exact CI pin (ruff 0.15.22): check and format clean,
40 tests pass.
CI (Python 3.14) failed the negative control that passed locally on 3.12:
ssl.create_default_context().minimum_version is the MINIMUM_SUPPORTED
sentinel (-2) on 3.12 but already TLSv1_2 on 3.14, so asserting the
platform default sits BELOW the floor is interpreter-dependent.

The control now exhibits a context that fails the assertion outright
rather than asserting anything about the platform's default, so it
discriminates on every interpreter. Mutation-checked: reverting the
helper to TLSv1 turns the test red.

Worth recording rather than just fixing: the fix itself is a no-op on
3.14, where the default is already TLS 1.2 — but this package targets
3.10+, and on 3.10-3.13 the floor really is unpinned. The pin is what
makes the guarantee independent of which interpreter a user runs.
The control used ssl.TLSVersion.TLSv1, which emits a DeprecationWarning.
MINIMUM_SUPPORTED is the actual 'no floor' sentinel, expresses the intent
more directly, and is not deprecated. Mutation-rechecked: removing the
pin from the helper still turns the test red.
@Avicennasis
Avicennasis merged commit 8d68fe7 into main Aug 22, 2026
6 checks passed
@Avicennasis
Avicennasis deleted the fix/tls-minimum-version branch August 22, 2026 14:46
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