From 04dc54ccb93df773e2affa6c1f3db32ca24e8d9c Mon Sep 17 00:00:00 2001 From: Akshaay Date: Thu, 15 Oct 2026 09:01:00 +0530 Subject: [PATCH] Raise ApiValueError for malformed Basic proxy credentials Follow-up to #2738. A proxy-authorization header whose decoded Basic credentials contain no colon was silently skipped, so the CONNECT went out without Proxy-Authorization and failed with a confusing 407. A bare "Basic" header with no credentials still raised IndexError. Both cases now raise ApiValueError with a clear message, and the "useronly" test case expects it. --- kubernetes/stream/ws_client.py | 14 ++++++++++---- kubernetes/stream/ws_client_test.py | 12 +++++++++++- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/kubernetes/stream/ws_client.py b/kubernetes/stream/ws_client.py index 0692ff180d..f4c94809e7 100644 --- a/kubernetes/stream/ws_client.py +++ b/kubernetes/stream/ws_client.py @@ -598,10 +598,16 @@ def websocket_proxycare(connect_opt, configuration, url, headers): if configuration.proxy_headers: for key,value in configuration.proxy_headers.items(): if key == 'proxy-authorization' and value.startswith('Basic'): - b64value = value.split()[1] - auth = urlsafe_b64decode(b64value).decode().split(':', 1) - if len(auth) == 2: - connect_opt.update({'http_proxy_auth': (auth[0], auth[1]) }) + parts = value.split() + if len(parts) < 2: + raise ApiValueError( + "Invalid proxy-authorization header: missing Basic credentials") + auth = urlsafe_b64decode(parts[1]).decode().split(':', 1) + if len(auth) != 2: + raise ApiValueError( + "Invalid proxy-authorization header: Basic credentials " + "must be in the form 'user:password'") + connect_opt.update({'http_proxy_auth': (auth[0], auth[1]) }) return(connect_opt) diff --git a/kubernetes/stream/ws_client_test.py b/kubernetes/stream/ws_client_test.py index 7a31dea872..45402d7149 100644 --- a/kubernetes/stream/ws_client_test.py +++ b/kubernetes/stream/ws_client_test.py @@ -19,6 +19,7 @@ from .ws_client import get_websocket_url, WSClient, V5_CHANNEL_PROTOCOL, V4_CHANNEL_PROTOCOL, CLOSE_CHANNEL, STDIN_CHANNEL, STDOUT_CHANNEL from .ws_client import websocket_proxycare from kubernetes.client.configuration import Configuration +from kubernetes.client.rest import ApiValueError import os import select import socket @@ -112,7 +113,6 @@ def test_websocket_proxycare(self): ( 'http://proxy.example.com:8080/', 'user:pass', '.example.com', 'proxy.example.com', 8080, ('user','pass'), ['.example.com']), ( 'http://proxy.example.com:8080/', 'user:pass', 'localhost,.local,.example.com', 'proxy.example.com', 8080, ('user','pass'), ['localhost','.local','.example.com']), ( 'http://proxy.example.com:8080/', 'user:pa:ss:word', None, 'proxy.example.com', 8080, ('user','pa:ss:word'), None), - ( 'http://proxy.example.com:8080/', 'useronly', None, 'proxy.example.com', 8080, None, None), ]: # input setup cfg = Configuration(proxy='', no_proxy='') @@ -130,6 +130,16 @@ def test_websocket_proxycare(self): assert dictval(connect_opts, 'http_proxy_auth') == expect_auth assert dictval(connect_opts, 'http_no_proxy') == expect_noproxy + def test_websocket_proxycare_invalid_basic_credentials(self): + for proxy_auth in [ + urllib3.util.make_headers(proxy_basic_auth='useronly')['proxy-authorization'], + 'Basic', + ]: + cfg = Configuration(proxy='http://proxy.example.com:8080/', no_proxy='') + cfg.proxy_headers = {'proxy-authorization': proxy_auth} + with self.assertRaises(ApiValueError): + websocket_proxycare({}, cfg, None, None) + class WSClientMultiFrameReadTest(unittest.TestCase): """Tests that reads spanning multiple websocket frames are not