Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f23c78533e | ||
|
|
d29ed6aaa1 | ||
|
|
8e5fb92ff1 | ||
|
|
06015934b8 | ||
|
|
568cd51fd2 |
@@ -1,5 +1,11 @@
|
|||||||
# Microdot change log
|
# Microdot change log
|
||||||
|
|
||||||
|
**Release 0.7.2** - 2021-09-28
|
||||||
|
|
||||||
|
- Document a security risk in the send_file function ([commit](https://github.com/miguelgrinberg/microdot/commit/d29ed6aaa1f2080fcf471bf6ae0f480f95ff1716)) (thanks **Ky Tran**!)
|
||||||
|
- Validate redirect URLs ([commit](https://github.com/miguelgrinberg/microdot/commit/8e5fb92ff1ccd50972b0c1cb5a6c3bd5eb54d86b)) (thanks **Ky Tran**!)
|
||||||
|
- Return a 400 error when request object could not be created ([commit](https://github.com/miguelgrinberg/microdot/commit/06015934b834622d39f52b3e13d16bfee9dc8e5a))
|
||||||
|
|
||||||
**Release 0.7.1** - 2021-09-27
|
**Release 0.7.1** - 2021-09-27
|
||||||
|
|
||||||
- Breaking change: Limit the size of each request line to 2KB. A different maximum can be set in `Request.max_readline`. ([commit](https://github.com/miguelgrinberg/microdot/commit/de9c991a9ab836d57d5c08bf4282f99f073b502a)) (thanks **Ky Tran**!)
|
- Breaking change: Limit the size of each request line to 2KB. A different maximum can be set in `Request.max_readline`. ([commit](https://github.com/miguelgrinberg/microdot/commit/de9c991a9ab836d57d5c08bf4282f99f073b502a)) (thanks **Ky Tran**!)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
name = microdot
|
name = microdot
|
||||||
version = 0.7.1
|
version = 0.7.2
|
||||||
author = Miguel Grinberg
|
author = Miguel Grinberg
|
||||||
author_email = miguel.grinberg@gmail.com
|
author_email = miguel.grinberg@gmail.com
|
||||||
description = The impossibly small web framework for MicroPython
|
description = The impossibly small web framework for MicroPython
|
||||||
|
|||||||
@@ -310,7 +310,6 @@ class Request():
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _safe_readline(stream):
|
def _safe_readline(stream):
|
||||||
line = stream.readline(Request.max_readline + 1)
|
line = stream.readline(Request.max_readline + 1)
|
||||||
print(line, Request.max_readline)
|
|
||||||
if len(line) > Request.max_readline:
|
if len(line) > Request.max_readline:
|
||||||
raise ValueError('line too long')
|
raise ValueError('line too long')
|
||||||
return line
|
return line
|
||||||
@@ -431,6 +430,8 @@ class Response():
|
|||||||
:param status_code: The 3xx status code to use for the redirect. The
|
:param status_code: The 3xx status code to use for the redirect. The
|
||||||
default is 302.
|
default is 302.
|
||||||
"""
|
"""
|
||||||
|
if '\x0d' in location or '\x0a' in location:
|
||||||
|
raise ValueError('invalid redirect URL')
|
||||||
return cls(status_code=status_code, headers={'Location': location})
|
return cls(status_code=status_code, headers={'Location': location})
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -443,6 +444,10 @@ class Response():
|
|||||||
:param content_type: The ``Content-Type`` header to use in the
|
:param content_type: The ``Content-Type`` header to use in the
|
||||||
response. If omitted, it is generated
|
response. If omitted, it is generated
|
||||||
automatically from the file extension.
|
automatically from the file extension.
|
||||||
|
|
||||||
|
Security note: The filename is assumed to be trusted. Never pass
|
||||||
|
filenames provided by the user before validating and sanitizing them
|
||||||
|
first.
|
||||||
"""
|
"""
|
||||||
if content_type is None:
|
if content_type is None:
|
||||||
ext = filename.split('.')[-1]
|
ext = filename.split('.')[-1]
|
||||||
@@ -795,7 +800,11 @@ class Microdot():
|
|||||||
else:
|
else:
|
||||||
stream = sock
|
stream = sock
|
||||||
|
|
||||||
|
req = None
|
||||||
|
try:
|
||||||
req = Request.create(self, stream, addr)
|
req = Request.create(self, stream, addr)
|
||||||
|
except Exception as exc: # pragma: no cover
|
||||||
|
print_exception(exc)
|
||||||
if req:
|
if req:
|
||||||
if req.content_length > req.max_content_length:
|
if req.content_length > req.max_content_length:
|
||||||
if 413 in self.error_handlers:
|
if 413 in self.error_handlers:
|
||||||
@@ -836,6 +845,8 @@ class Microdot():
|
|||||||
res = self.error_handlers[500](req)
|
res = self.error_handlers[500](req)
|
||||||
else:
|
else:
|
||||||
res = 'Internal server error', 500
|
res = 'Internal server error', 500
|
||||||
|
else:
|
||||||
|
res = 'Bad request', 400
|
||||||
if isinstance(res, tuple):
|
if isinstance(res, tuple):
|
||||||
res = Response(*res)
|
res = Response(*res)
|
||||||
elif not isinstance(res, Response):
|
elif not isinstance(res, Response):
|
||||||
|
|||||||
@@ -218,8 +218,12 @@ class Microdot(BaseMicrodot):
|
|||||||
self.server.close()
|
self.server.close()
|
||||||
|
|
||||||
async def dispatch_request(self, reader, writer):
|
async def dispatch_request(self, reader, writer):
|
||||||
|
req = None
|
||||||
|
try:
|
||||||
req = await Request.create(self, reader,
|
req = await Request.create(self, reader,
|
||||||
writer.get_extra_info('peername'))
|
writer.get_extra_info('peername'))
|
||||||
|
except Exception as exc: # pragma: no cover
|
||||||
|
print_exception(exc)
|
||||||
if req:
|
if req:
|
||||||
if req.content_length > req.max_content_length:
|
if req.content_length > req.max_content_length:
|
||||||
if 413 in self.error_handlers:
|
if 413 in self.error_handlers:
|
||||||
@@ -266,6 +270,8 @@ class Microdot(BaseMicrodot):
|
|||||||
self.error_handlers[500], req)
|
self.error_handlers[500], req)
|
||||||
else:
|
else:
|
||||||
res = 'Internal server error', 500
|
res = 'Internal server error', 500
|
||||||
|
else:
|
||||||
|
res = 'Bad request', 400
|
||||||
if isinstance(res, tuple):
|
if isinstance(res, tuple):
|
||||||
res = Response(*res)
|
res = Response(*res)
|
||||||
elif not isinstance(res, Response):
|
elif not isinstance(res, Response):
|
||||||
|
|||||||
@@ -73,7 +73,10 @@ class TestMicrodot(unittest.TestCase):
|
|||||||
mock_socket._requests.append(fd)
|
mock_socket._requests.append(fd)
|
||||||
self._add_shutdown(app)
|
self._add_shutdown(app)
|
||||||
app.run()
|
app.run()
|
||||||
assert fd.response == b''
|
self.assertTrue(fd.response.startswith(b'HTTP/1.0 400 N/A\r\n'))
|
||||||
|
self.assertIn(b'Content-Length: 11\r\n', fd.response)
|
||||||
|
self.assertIn(b'Content-Type: text/plain\r\n', fd.response)
|
||||||
|
self.assertTrue(fd.response.endswith(b'\r\n\r\nBad request'))
|
||||||
|
|
||||||
def test_method_decorators(self):
|
def test_method_decorators(self):
|
||||||
app = Microdot()
|
app = Microdot()
|
||||||
|
|||||||
@@ -84,7 +84,10 @@ class TestMicrodotAsync(unittest.TestCase):
|
|||||||
mock_socket._requests.append(fd)
|
mock_socket._requests.append(fd)
|
||||||
self._add_shutdown(app)
|
self._add_shutdown(app)
|
||||||
app.run()
|
app.run()
|
||||||
assert fd.response == b''
|
self.assertTrue(fd.response.startswith(b'HTTP/1.0 400 N/A\r\n'))
|
||||||
|
self.assertIn(b'Content-Length: 11\r\n', fd.response)
|
||||||
|
self.assertIn(b'Content-Type: text/plain\r\n', fd.response)
|
||||||
|
self.assertTrue(fd.response.endswith(b'\r\n\r\nBad request'))
|
||||||
|
|
||||||
def test_before_after_request(self):
|
def test_before_after_request(self):
|
||||||
app = Microdot()
|
app = Microdot()
|
||||||
|
|||||||
Reference in New Issue
Block a user