Skip to content

Commit

Permalink
Reject incorrect Engine.IO protocol versions
Browse files Browse the repository at this point in the history
  • Loading branch information
miguelgrinberg committed Nov 27, 2020
1 parent 232f165 commit 00330bb
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 78 deletions.
47 changes: 28 additions & 19 deletions engineio/asyncio_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,15 +200,10 @@ async def handle_request(self, *args, **kwargs):
if allowed_origins is not None and origin not in \
allowed_origins:
self.logger.info(origin + ' is not an accepted origin.')
r = self._bad_request()
make_response = self._async['make_response']
if asyncio.iscoroutinefunction(make_response):
response = await make_response(
r['status'], r['headers'], r['response'], environ)
else:
response = make_response(r['status'], r['headers'],
r['response'], environ)
return response
return await self._make_response(
self._bad_request(
origin + ' is not an accepted origin.'),
environ)

method = environ['REQUEST_METHOD']
query = urllib.parse.parse_qs(environ.get('QUERY_STRING', ''))
Expand All @@ -218,6 +213,14 @@ async def handle_request(self, *args, **kwargs):
jsonp = False
jsonp_index = None

# make sure the client speaks a compatible Engine.IO version
sid = query['sid'][0] if 'sid' in query else None
if sid is None and query.get('EIO') not in [['2'], ['3']]:
return await self._make_response(self._bad_request(
'The client is using an unsupported version of the Socket.IO '
'or Engine.IO protocols'
), environ)

if 'b64' in query:
if query['b64'][0] == "1" or query['b64'][0].lower() == "true":
b64 = True
Expand Down Expand Up @@ -297,16 +300,7 @@ async def handle_request(self, *args, **kwargs):
getattr(self, '_' + encoding)(r['response'])
r['headers'] += [('Content-Encoding', encoding)]
break
cors_headers = self._cors_headers(environ)
make_response = self._async['make_response']
if asyncio.iscoroutinefunction(make_response):
response = await make_response(r['status'],
r['headers'] + cors_headers,
r['response'], environ)
else:
response = make_response(r['status'], r['headers'] + cors_headers,
r['response'], environ)
return response
return await self._make_response(r, environ)

def start_background_task(self, target, *args, **kwargs):
"""Start a background task using the appropriate async model.
Expand Down Expand Up @@ -365,6 +359,21 @@ def create_event(self, *args, **kwargs):
"""
return asyncio.Event(*args, **kwargs)

async def _make_response(self, response_dict, environ):
cors_headers = self._cors_headers(environ)
make_response = self._async['make_response']
if asyncio.iscoroutinefunction(make_response):
response = await make_response(
response_dict['status'],
response_dict['headers'] + cors_headers,
response_dict['response'], environ)
else:
response = make_response(
response_dict['status'],
response_dict['headers'] + cors_headers,
response_dict['response'], environ)
return response

async def _handle_connect(self, environ, transport, b64=False,
jsonp_index=None):
"""Handle a client connection request."""
Expand Down
21 changes: 16 additions & 5 deletions engineio/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,18 +336,26 @@ def handle_request(self, environ, start_response):
if allowed_origins is not None and origin not in \
allowed_origins:
self.logger.info(origin + ' is not an accepted origin.')
r = self._bad_request()
r = self._bad_request(
origin + ' is not an accepted origin.')
start_response(r['status'], r['headers'])
return [r['response']]

method = environ['REQUEST_METHOD']
query = urllib.parse.parse_qs(environ.get('QUERY_STRING', ''))

sid = query['sid'][0] if 'sid' in query else None
b64 = False
jsonp = False
jsonp_index = None

# make sure the client speaks a compatible Engine.IO version
sid = query['sid'][0] if 'sid' in query else None
if sid is None and query.get('EIO') not in [['2'], ['3']]:
r = self._bad_request(
'The client is using an unsupported version of the Socket.IO '
'or Engine.IO protocols')
start_response(r['status'], r['headers'])
return [r['response']]

if 'b64' in query:
if query['b64'][0] == "1" or query['b64'][0].lower() == "true":
b64 = True
Expand Down Expand Up @@ -612,11 +620,14 @@ def _ok(self, packets=None, headers=None, b64=False, jsonp_index=None):
'headers': [('Content-Type', 'text/plain')],
'response': b'OK'}

def _bad_request(self):
def _bad_request(self, message=None):
"""Generate a bad request HTTP error response."""
if message is None:
message = 'Bad Request'
message = packet.Packet.json.dumps(message)
return {'status': '400 BAD REQUEST',
'headers': [('Content-Type', 'text/plain')],
'response': b'Bad Request'}
'response': message.encode('utf-8')}

def _method_not_found(self):
"""Generate a method not found HTTP error response."""
Expand Down
18 changes: 18 additions & 0 deletions tests/asyncio/test_asyncio_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ def _run(coro):
class TestAsyncServer(unittest.TestCase):
@staticmethod
def get_async_mock(environ={'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}):
if environ.get('QUERY_STRING'):
if 'EIO=' not in environ['QUERY_STRING']:
environ['QUERY_STRING'] = 'EIO=3&' + environ['QUERY_STRING']
else:
environ['QUERY_STRING'] = 'EIO=3'
a = mock.MagicMock()
a._async = {
'asyncio': True,
Expand Down Expand Up @@ -252,6 +257,19 @@ def test_connect_no_upgrades(self, import_module):
).packets
assert packets[0].data['upgrades'] == []

@mock.patch('importlib.import_module')
def test_connect_bad_eio_version(self, import_module):
a = self.get_async_mock(
{'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'EIO=5'}
)
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
_run(s.handle_request('request'))
assert a._async['make_response'].call_count == 1
assert a._async['make_response'].call_args[0][0] == '400 BAD REQUEST'
assert b'unsupported version' in \
a._async['make_response'].call_args[0][2]

@mock.patch('importlib.import_module')
def test_connect_b64_with_1(self, import_module):
a = self.get_async_mock(
Expand Down
Loading

0 comments on commit 00330bb

Please sign in to comment.