Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Bypass proxy authentication with HTTP/1.0 requests #1267 #1342

Merged
merged 4 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion proxy/http/proxy/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ class AuthPlugin(HttpProxyBasePlugin):
def before_upstream_connection(
self, request: HttpParser,
) -> Optional[HttpParser]:
if self.flags.auth_code and request.headers:
if self.flags.auth_code:
request.headers = request.headers or {}
if httpHeaders.PROXY_AUTHORIZATION not in request.headers:
raise ProxyAuthenticationFailed()
parts = request.headers[httpHeaders.PROXY_AUTHORIZATION][1].split()
Expand Down
74 changes: 73 additions & 1 deletion tests/plugin/test_http_proxy_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"""
import gzip
import json
import base64
import selectors
from typing import Any, cast
from urllib import parse as urlparse
Expand All @@ -29,7 +30,8 @@
from proxy.http.parser import HttpParser, httpParserTypes
from proxy.common.utils import bytes_, build_http_request, build_http_response
from proxy.http.responses import (
NOT_FOUND_RESPONSE_PKT, PROXY_TUNNEL_ESTABLISHED_RESPONSE_PKT,
NOT_FOUND_RESPONSE_PKT, PROXY_AUTH_FAILED_RESPONSE_PKT,
PROXY_TUNNEL_ESTABLISHED_RESPONSE_PKT,
)
from proxy.common.constants import DEFAULT_HTTP_PORT, PROXY_AGENT_HEADER_VALUE
from .utils import get_plugin_by_test_name
Expand Down Expand Up @@ -555,3 +557,73 @@ async def test_shortlink_plugin_external(self) -> None:
),
)
self.assertFalse(self.protocol_handler.work.has_buffer())

@pytest.mark.asyncio # type: ignore[misc]
@pytest.mark.parametrize(
"_setUp",
(
('test_auth_plugin'),
),
indirect=True,
) # type: ignore[misc]
async def test_auth_plugin(self) -> None:
self.flags.auth_code = base64.b64encode(bytes_("admin:123456"))

request = b'\r\n'.join([
b'GET http://www.facebook.com/tr/ HTTP/1.1',
b'Host: www.facebook.com',
b'User-Agent: proxy.py v2.4.4rc5.dev3+g95b646a.d20230811',
b'',
b'',
])

self._conn.recv.return_value = request
self.mock_selector.return_value.select.side_effect = [
[(
selectors.SelectorKey(
fileobj=self._conn.fileno(),
fd=self._conn.fileno(),
events=selectors.EVENT_READ,
data=None,
),
selectors.EVENT_READ,
)],
]
await self.protocol_handler._run_once()
self.assertEqual(
self.protocol_handler.work.buffer[0],
PROXY_AUTH_FAILED_RESPONSE_PKT,
)

@pytest.mark.asyncio # type: ignore[misc]
@pytest.mark.parametrize(
"_setUp",
(
('test_auth_plugin'),
),
indirect=True,
) # type: ignore[misc]
async def test_auth_plugin_bypass(self) -> None:
self.flags.auth_code = base64.b64encode(bytes_("admin:123456"))

# miss requests header when https and HTTP 1.0
request = b'CONNECT www.facebook.com:443 HTTP/1.0\r\n\r\n'

self._conn.recv.return_value = request
self.mock_selector.return_value.select.side_effect = [
[(
selectors.SelectorKey(
fileobj=self._conn.fileno(),
fd=self._conn.fileno(),
events=selectors.EVENT_READ,
data=None,
),
selectors.EVENT_READ,
)],
]
await self.protocol_handler._run_once()

self.assertEqual(
self.protocol_handler.work.buffer[0],
PROXY_AUTH_FAILED_RESPONSE_PKT,
)
3 changes: 3 additions & 0 deletions tests/plugin/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
FilterByUpstreamHostPlugin, RedirectToCustomServerPlugin,
)
from proxy.http.proxy import HttpProxyBasePlugin
from proxy.http.proxy.auth import AuthPlugin


def get_plugin_by_test_name(test_name: str) -> Type[HttpProxyBasePlugin]:
Expand All @@ -36,4 +37,6 @@ def get_plugin_by_test_name(test_name: str) -> Type[HttpProxyBasePlugin]:
plugin = FilterByURLRegexPlugin
elif test_name == 'test_shortlink_plugin':
plugin = ShortLinkPlugin
elif test_name == 'test_auth_plugin':
plugin = AuthPlugin
return plugin