Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/pymax/transport/websocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
from pymax.logging import get_logger

from .base import Transport
import ssl
ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE
Comment on lines +7 to +10

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.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Tracked websocket.py files:\n'
git ls-files | grep -E '(^|/)websocket\.py$' || true

printf '\nRelevant file excerpt:\n'
if [ -f src/pymax/transport/websocket.py ]; then
  nl -ba src/pymax/transport/websocket.py | sed -n '1,80p'
fi

printf '\nSearch for ssl_context use and transport connect definitions:\n'
rg -n "ssl_context|check_hostname|verify_mode|def connect|client\.connect|async def connect" src/pymax/transport/websocket.py src/pymax/transport || true

printf '\nPython ssl semantics probe for create_default_context mutations:\n'
python3 - <<'PY'
import ssl
ctx = ssl.create_default_context()
before = (ctx.check_hostname, ctx.verify_mode == ssl.CERT_NONE)
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
after = (ctx.check_hostname, ctx.verify_mode == ssl.CERT_NONE)
print("before_hostname_true:", before[0])
print("before_cert_verified:", not before[1])
print("after_hostname_false:", after[0])
print("after_cert_none:", after[1])
PY

printf '\nPackage version constraint context:\n'
if [ -f pyproject.toml ]; then
  sed -n '1,80p' pyproject.toml | nl -ba
fi

Repository: MaxApiTeam/PyMax

Length of output: 279


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Relevant file excerpt:\n'
awk '{printf "%6d\t%s\n", NR, $0}' src/pymax/transport/websocket.py | sed -n '1,80p'

printf '\nSearch for ssl_context use and transport connect definitions:\n'
rg -n "ssl_context|check_hostname|verify_mode|def connect|client\.connect|async def connect" src/pymax/transport src/pymax || true

printf '\nPython ssl semantics probe for create_default_context mutations:\n'
python3 - <<'PY'
import ssl
ctx = ssl.create_default_context()
before = (ctx.check_hostname, ctx.verify_mode == ssl.CERT_NONE)
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
after = (ctx.check_hostname, ctx.verify_mode == ssl.CERT_NONE)
print("before_hostname_true:", before[0])
print("before_cert_verified:", not before[1])
print("after_hostname_false:", after[0])
print("after_cert_none:", after[1])
PY

printf '\nPackage version constraint context:\n'
awk '{printf "%6d\t%s\n", NR, $0}' pyproject.toml | sed -n '1,80p'

printf '\nCall sites for build_connection / WebSocketTransport:\n'
rg -n "WebSocketTransport|_build_connection|WebSocketTransport\(" src/py* tests || true

Repository: MaxApiTeam/PyMax

Length of output: 8191


Security Misconfiguration (CWE-295): Improper Certificate Validation

Reachability: External

Do not ship an unverified WebSocket context.

Lines 8-10 set verify_mode to ssl.CERT_NONE and disable check_hostname, then both connection paths use the same context. This removes server certificate and hostname validation, so an attacker who can intercept the connection or control the proxy can impersonate the endpoint and read or modify WebSocket traffic.

Keep the default verified context. For private CAs, fix the server chain or load the CA bundle with load_verify_locations() while retaining hostname checking. Make any insecure local-dev mode explicit and opt-in.

Proposed fix
 ssl_context = ssl.create_default_context()
-ssl_context.check_hostname = False
-ssl_context.verify_mode = ssl.CERT_NONE
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import ssl
ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE
import ssl
ssl_context = ssl.create_default_context()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pymax/transport/websocket.py` around lines 7 - 10, Remove the assignments
disabling validation from the module-level ssl_context in websocket.py,
preserving the verified defaults created by ssl.create_default_context(). If
private CA support is required, load it via load_verify_locations() without
changing check_hostname or verify_mode; any insecure local-development mode must
be explicit and opt-in rather than shared by both connection paths.


logger = get_logger(__name__)

Expand All @@ -17,11 +21,11 @@ def __init__(self, url: str, proxy: str | None) -> None:
async def connect(self) -> None:
if self.proxy:
self.ws = await client.connect(
self.url, origin=Origin("https://web.max.ru"), proxy=self.proxy
self.url, origin=Origin("https://web.max.ru"), proxy=self.proxy, ssl=ssl_context,
)
else:
self.ws = await client.connect(
self.url, origin=Origin("https://web.max.ru")
self.url, origin=Origin("https://web.max.ru"), ssl=ssl_context,
) # TODO: origin should be configurable

async def close(self) -> None:
Expand Down