Permalink
Comparing changes
Choose two branches to see what’s changed or to start a new pull request.
If you need to, you can also .
Open a pull request
Create a new pull request by comparing changes across two branches. If you need to, you can also .
- 10 commits
- 12 files changed
- 0 comments
- 4 contributors
Commits on Nov 22, 2020
Commits on Nov 23, 2020
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 2.8.2 to 2.9.0. - [Release notes](https://github.com/pre-commit/pre-commit/releases) - [Changelog](https://github.com/pre-commit/pre-commit/blob/master/CHANGELOG.md) - [Commits](pre-commit/pre-commit@v2.8.2...v2.9.0) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Commits on Nov 26, 2020
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 2.9.0 to 2.9.2. - [Release notes](https://github.com/pre-commit/pre-commit/releases) - [Changelog](https://github.com/pre-commit/pre-commit/blob/master/CHANGELOG.md) - [Commits](pre-commit/pre-commit@v2.9.0...v2.9.2) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Commits on Dec 04, 2020
Bumps [multidict](https://github.com/aio-libs/multidict) from 5.0.2 to 5.1.0. - [Release notes](https://github.com/aio-libs/multidict/releases) - [Changelog](https://github.com/aio-libs/multidict/blob/master/CHANGES.rst) - [Commits](aio-libs/multidict@v5.0.2...v5.1.0) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Commits on Dec 07, 2020
Bumps [pygments](https://github.com/pygments/pygments) from 2.7.2 to 2.7.3. - [Release notes](https://github.com/pygments/pygments/releases) - [Changelog](https://github.com/pygments/pygments/blob/master/CHANGES) - [Commits](pygments/pygments@2.7.2...2.7.3) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Commits on Dec 08, 2020
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 2.9.2 to 2.9.3. - [Release notes](https://github.com/pre-commit/pre-commit/releases) - [Changelog](https://github.com/pre-commit/pre-commit/blob/master/CHANGELOG.md) - [Commits](pre-commit/pre-commit@v2.9.2...v2.9.3) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Commits on Feb 25, 2021
(cherry picked from commit f2afa2f)
Bumps [chardet](https://github.com/chardet/chardet) from 3.0.4 to 4.0.0. - [Release notes](https://github.com/chardet/chardet/releases) - [Commits](chardet/chardet@3.0.4...4.0.0) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> (cherry picked from commit b0ed732)
This patch fixes an open redirect vulnerability bug in `aiohttp.web_middlewares.normalize_path_middleware` by making sure that there's at most one slash at the beginning of the `Location` header value. Refs: * https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html * GHSA-v6wp-4m6f-gcjg (cherry picked from commit 76c1fa1315faf48d44b061a1433d0d0c3e4dc12f)
Unified
Split
Showing
with
83 additions
and 10 deletions.
- +28 −0 CHANGES.rst
- +1 −0 CONTRIBUTORS.txt
- +1 −1 aiohttp/__init__.py
- +13 −1 aiohttp/http_parser.py
- +1 −0 aiohttp/web_middlewares.py
- +1 −4 docs/client_reference.rst
- +1 −1 requirements/base.txt
- +1 −1 requirements/doc.txt
- +1 −1 requirements/lint.txt
- +1 −1 requirements/multidict.txt
- +1 −0 tests/test_http_parser.py
- +33 −0 tests/test_web_middleware.py
| @@ -14,6 +14,34 @@ Changelog | ||
| .. towncrier release notes start | ||
| 3.7.3 (2021-02-25) | ||
| ================== | ||
|
|
||
| Bugfixes | ||
| -------- | ||
|
|
||
| - **(SECURITY BUG)** Started preventing open redirects in the | ||
| ``aiohttp.web.normalize_path_middleware`` middleware. For | ||
| more details, see | ||
| https://github.com/aio-libs/aiohttp/security/advisories/GHSA-v6wp-4m6f-gcjg. | ||
|
|
||
| Thanks to `Beast Glatisant <https://github.com/g147>`__ for | ||
| finding the first instance of this issue and `Jelmer Vernooij | ||
| <https://jelmer.uk/>`__ for reporting and tracking it down | ||
| in aiohttp. | ||
| `#5497 <https://github.com/aio-libs/aiohttp/issues/5497>`_ | ||
| - Fix interpretation difference of the pure-Python and the Cython-based | ||
| HTTP parsers construct a ``yarl.URL`` object for HTTP request-target. | ||
|
|
||
| Before this fix, the Python parser would turn the URI's absolute-path | ||
| for ``//some-path`` into ``/`` while the Cython code preserved it as | ||
| ``//some-path``. Now, both do the latter. | ||
| `#5498 <https://github.com/aio-libs/aiohttp/issues/5498>`_ | ||
|
|
||
|
|
||
| ---- | ||
|
|
||
|
|
||
| 3.7.3 (2020-11-18) | ||
| ================== | ||
|
|
||
| @@ -109,6 +109,7 @@ Florian Scheffler | ||
| Frederik Gladhorn | ||
| Frederik Peter Aalund | ||
| Gabriel Tremblay | ||
| Gary Wilson Jr. | ||
| Gennady Andreyev | ||
| Georges Dubus | ||
| Greg Holt | ||
| @@ -1,4 +1,4 @@ | ||
| __version__ = "3.7.3" | ||
| __version__ = "3.7.4" | ||
|
|
||
| from typing import Tuple | ||
|
|
||
| @@ -498,6 +498,9 @@ def parse_message(self, lines: List[bytes]) -> Any: | ||
| "Status line is too long", str(self.max_line_size), str(len(path)) | ||
| ) | ||
|
|
||
| path_part, _hash_separator, url_fragment = path.partition("#") | ||
| path_part, _question_mark_separator, qs_part = path_part.partition("?") | ||
|
|
||
| # method | ||
| if not METHRE.match(method): | ||
| raise BadStatusLine(method) | ||
| @@ -538,7 +541,16 @@ def parse_message(self, lines: List[bytes]) -> Any: | ||
| compression, | ||
| upgrade, | ||
| chunked, | ||
| URL(path), | ||
| # NOTE: `yarl.URL.build()` is used to mimic what the Cython-based | ||
| # NOTE: parser does, otherwise it results into the same | ||
| # NOTE: HTTP Request-Line input producing different | ||
| # NOTE: `yarl.URL()` objects | ||
| URL.build( | ||
| path=path_part, | ||
| query_string=qs_part, | ||
| fragment=url_fragment, | ||
| encoded=True, | ||
| ), | ||
| ) | ||
|
|
||
|
|
||
| @@ -102,6 +102,7 @@ async def impl(request: Request, handler: _Handler) -> StreamResponse: | ||
| paths_to_check.append(merged_slashes[:-1]) | ||
|
|
||
| for path in paths_to_check: | ||
| path = re.sub("^//+", "/", path) # SECURITY: GHSA-v6wp-4m6f-gcjg | ||
| resolves, request = await _check_request_resolves(request, path) | ||
| if resolves: | ||
| raise redirect_class(request.raw_path + query) | ||
| @@ -129,7 +129,7 @@ The client session supports the context manager protocol for self closing. | ||
| requests where you need to handle responses with status 400 or | ||
| higher. | ||
|
|
||
| :param timeout: a :class:`ClientTimeout` settings structure, 5min | ||
| :param timeout: a :class:`ClientTimeout` settings structure, 300 seconds (5min) | ||
| total timeout by default. | ||
|
|
||
| .. versionadded:: 3.3 | ||
| @@ -150,9 +150,6 @@ The client session supports the context manager protocol for self closing. | ||
|
|
||
| Use ``timeout`` parameter instead. | ||
|
|
||
| :param timeout: a :class:`ClientTimeout` settings structure, 300 seconds (5min) | ||
| total timeout by default. | ||
|
|
||
| :param bool connector_owner: | ||
|
|
||
| Close connector instance on session closing. | ||
| @@ -6,7 +6,7 @@ async-timeout==3.0.1 | ||
| attrs==20.3.0 | ||
| brotlipy==0.7.0 | ||
| cchardet==2.1.7 | ||
| chardet==3.0.4 | ||
| chardet==4.0.0 | ||
| gunicorn==20.0.4 | ||
| idna-ssl==1.1.0; python_version<"3.7" | ||
| typing_extensions==3.7.4.3 | ||
| @@ -1,5 +1,5 @@ | ||
| aiohttp-theme==0.1.6 | ||
| pygments==2.7.2 | ||
| pygments==2.7.3 | ||
| sphinx==3.3.1 | ||
| sphinxcontrib-asyncio==0.3.0 | ||
| sphinxcontrib-blockdiag==2.0.0 | ||
| @@ -3,4 +3,4 @@ flake8==3.8.4 | ||
| flake8-pyi==20.10.0 | ||
| isort==5.6.4 | ||
| mypy==0.790; implementation_name=="cpython" | ||
| pre-commit==2.8.2 | ||
| pre-commit==2.9.3 | ||
| @@ -1 +1 @@ | ||
| multidict==5.0.2 | ||
| multidict==5.1.0 |
| @@ -528,6 +528,7 @@ def test_http_request_parser_two_slashes(parser) -> None: | ||
|
|
||
| assert msg.method == "GET" | ||
| assert msg.path == "//path" | ||
| assert msg.url.path == "//path" | ||
| assert msg.version == (1, 1) | ||
| assert not msg.should_close | ||
| assert msg.compression is None | ||
| @@ -1,4 +1,5 @@ | ||
| import re | ||
| from typing import Any | ||
|
|
||
| import pytest | ||
| from yarl import URL | ||
| @@ -352,6 +353,38 @@ async def test_cannot_remove_and_add_slash(self) -> None: | ||
| with pytest.raises(AssertionError): | ||
| web.normalize_path_middleware(append_slash=True, remove_slash=True) | ||
|
|
||
| @pytest.mark.parametrize( | ||
| ["append_slash", "remove_slash"], | ||
| [ | ||
| (True, False), | ||
| (False, True), | ||
| (False, False), | ||
| ], | ||
| ) | ||
| async def test_open_redirects( | ||
| self, append_slash: bool, remove_slash: bool, aiohttp_client: Any | ||
| ) -> None: | ||
| async def handle(request: web.Request) -> web.StreamResponse: | ||
| pytest.fail( | ||
| msg="Security advisory 'GHSA-v6wp-4m6f-gcjg' test handler " | ||
| "matched unexpectedly", | ||
| pytrace=False, | ||
| ) | ||
|
|
||
| app = web.Application( | ||
| middlewares=[ | ||
| web.normalize_path_middleware( | ||
| append_slash=append_slash, remove_slash=remove_slash | ||
| ) | ||
| ] | ||
| ) | ||
| app.add_routes([web.get("/", handle), web.get("/google.com", handle)]) | ||
| client = await aiohttp_client(app, server_kwargs={"skip_url_asserts": True}) | ||
| resp = await client.get("//google.com", allow_redirects=False) | ||
| assert resp.status == 308 | ||
| assert resp.headers["Location"] == "/google.com" | ||
| assert resp.url.query == URL("//google.com").query | ||
|
|
||
|
|
||
| async def test_old_style_middleware(loop, aiohttp_client) -> None: | ||
| async def handler(request): | ||