Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-starlette for openSUSE:Factory checked in at 2023-05-18 15:18:15 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-starlette (Old) and /work/SRC/openSUSE:Factory/.python-starlette.new.1533 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-starlette" Thu May 18 15:18:15 2023 rev:19 rq:1087526 version:0.27.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-starlette/python-starlette.changes 2023-03-15 18:53:47.100152746 +0100 +++ /work/SRC/openSUSE:Factory/.python-starlette.new.1533/python-starlette.changes 2023-05-18 15:18:19.413548122 +0200 @@ -1,0 +2,12 @@ +Wed May 17 07:42:13 UTC 2023 - David Anes <david.a...@suse.com> + +- Update to 0.27.0: + * Added + - Minify JSON websocket data via send_json #2128 + + * Fixed + - Replace commonprefix by commonpath on StaticFiles 1797de4. + - Convert ImportErrors into ModuleNotFoundError #2135. + - Correct the RuntimeError message content in websockets #2141. + +------------------------------------------------------------------- Old: ---- starlette-0.26.1.tar.gz New: ---- starlette-0.27.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-starlette.spec ++++++ --- /var/tmp/diff_new_pack.KvTBez/_old 2023-05-18 15:18:20.197552108 +0200 +++ /var/tmp/diff_new_pack.KvTBez/_new 2023-05-18 15:18:20.205552150 +0200 @@ -27,7 +27,7 @@ %define skip_python2 1 Name: python-starlette%{psuffix} -Version: 0.26.1 +Version: 0.27.0 Release: 0 Summary: Lightweight ASGI framework/toolkit License: BSD-3-Clause ++++++ starlette-0.26.1.tar.gz -> starlette-0.27.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.26.1/.github/pull_request_template.md new/starlette-0.27.0/.github/pull_request_template.md --- old/starlette-0.26.1/.github/pull_request_template.md 2023-03-13 19:08:31.000000000 +0100 +++ new/starlette-0.27.0/.github/pull_request_template.md 2023-05-16 12:56:45.000000000 +0200 @@ -1,7 +1,12 @@ -The starting point for contributions should usually be [a discussion](https://github.com/encode/starlette/discussions) +<!-- Thanks for contributing to Starlette! ð +Given this is a project maintained by volunteers, please read this template to not waste your time, or ours! ð --> -Simple documentation typos may be raised as stand-alone pull requests, but otherwise please ensure you've discussed your proposal prior to issuing a pull request. +# Summary -This will help us direct work appropriately, and ensure that any suggested changes have been okayed by the maintainers. +<!-- Write a small summary about what is happening here. --> -- [ ] Initially raised as discussion #... +# Checklist + +- [ ] I understand that this PR may be closed in case there was no previous discussion. (This doesn't apply to typos!) +- [ ] I've added a test for each change that was introduced, and I tried as much as possible to make a single atomic change. +- [ ] I've updated the documentation accordingly. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.26.1/docs/lifespan.md new/starlette-0.27.0/docs/lifespan.md --- old/starlette-0.26.1/docs/lifespan.md 2023-03-13 19:08:31.000000000 +0100 +++ new/starlette-0.27.0/docs/lifespan.md 2023-05-16 12:56:45.000000000 +0200 @@ -43,6 +43,7 @@ import httpx from starlette.applications import Starlette +from starlette.requests import Request from starlette.responses import PlainTextResponse from starlette.routing import Route @@ -52,12 +53,12 @@ @contextlib.asynccontextmanager -async def lifespan(app: Starlette) -> State: +async def lifespan(app: Starlette) -> typing.AsyncIterator[State]: async with httpx.AsyncClient() as client: yield {"http_client": client} -async def homepage(request): +async def homepage(request: Request) -> PlainTextResponse: client = request.state.http_client response = await client.get("https://www.example.com") return PlainTextResponse(response.text) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.26.1/docs/middleware.md new/starlette-0.27.0/docs/middleware.md --- old/starlette-0.26.1/docs/middleware.md 2023-03-13 19:08:31.000000000 +0100 +++ new/starlette-0.27.0/docs/middleware.md 2023-05-16 12:56:45.000000000 +0200 @@ -778,6 +778,11 @@ A middleware class that emits tracing info to [OpenTracing.io](https://opentracing.io/) compatible tracers and can be used to profile and monitor distributed applications. +#### [SecureCookiesMiddleware](https://github.com/thearchitector/starlette-securecookies) + +Customizable middleware for adding automatic cookie encryption and decryption to Starlette applications, with +extra support for existing cookie-based middleware. + #### [TimingMiddleware](https://github.com/steinnes/timing-asgi) A middleware class to emit timing information (cpu and wall time) for each request which diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.26.1/docs/release-notes.md new/starlette-0.27.0/docs/release-notes.md --- old/starlette-0.26.1/docs/release-notes.md 2023-03-13 19:08:31.000000000 +0100 +++ new/starlette-0.27.0/docs/release-notes.md 2023-05-16 12:56:45.000000000 +0200 @@ -1,3 +1,18 @@ +## 0.27.0 + +May 16, 2023 + +This release fixes a path traversal vulnerability in `StaticFiles`. You can view the full security advisory: +https://github.com/encode/starlette/security/advisories/GHSA-v5gw-mw7f-84px + +### Added +* Minify JSON websocket data via `send_json` https://github.com/encode/starlette/pull/2128 + +### Fixed +* Replace `commonprefix` by `commonpath` on `StaticFiles` [1797de4](https://github.com/encode/starlette/commit/1797de464124b090f10cf570441e8292936d63e3). +* Convert ImportErrors into ModuleNotFoundError [#2135](https://github.com/encode/starlette/pull/2135). +* Correct the RuntimeError message content in websockets [#2141](https://github.com/encode/starlette/pull/2141). + ## 0.26.1 March 13, 2023 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.26.1/docs/staticfiles.md new/starlette-0.27.0/docs/staticfiles.md --- old/starlette-0.26.1/docs/staticfiles.md 2023-03-13 19:08:31.000000000 +0100 +++ new/starlette-0.27.0/docs/staticfiles.md 2023-05-16 12:56:45.000000000 +0200 @@ -3,7 +3,7 @@ ### StaticFiles -Signature: `StaticFiles(directory=None, packages=None, check_dir=True, follow_symlink=False)` +Signature: `StaticFiles(directory=None, packages=None, html=False, check_dir=True, follow_symlink=False)` * `directory` - A string or [os.Pathlike][pathlike] denoting a directory path. * `packages` - A list of strings or list of tuples of strings of python packages. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.26.1/docs/third-party-packages.md new/starlette-0.27.0/docs/third-party-packages.md --- old/starlette-0.26.1/docs/third-party-packages.md 2023-03-13 19:08:31.000000000 +0100 +++ new/starlette-0.27.0/docs/third-party-packages.md 2023-05-16 12:56:45.000000000 +0200 @@ -147,6 +147,16 @@ to quickly generate fully customizable admin interface for your models. You can export your data to many formats (*CSV*, *PDF*, *Excel*, etc), filter your data with complex query including `AND` and `OR` conditions, upload files, ... +## Starlette Bridge + +<a href="https://github.com/tarsil/starlette-bridge" target="_blank">GitHub</a> | +<a href="https://starlette-bridge.tarsild.io/" target="_blank">Documentation</a> + +With the deprecation of `on_startup` and `on_shutdown`, Starlette Bridge makes sure you can still +use the old ways of declaring events with a particularity that internally, in fact, creates the +`lifespan` for you. This way backwards compatibility is assured for the existing packages out there +while maintaining the integrity of the newly `lifespan` events of `Starlette`. + ## Frameworks ### FastAPI @@ -160,7 +170,7 @@ ### Esmerald <a href="https://github.com/dymmond/esmerald" target="_blank">GitHub</a> | -<a href="https://esmerald.dymmond.com/" target="_blank">Documentation</a> +<a href="https://www.esmerald.dev" target="_blank">Documentation</a> Highly scalable, performant, easy to learn, easy to code and for every application web framework. Inspired by a lot of frameworks out there, Esmerald provides what every application needs, from the diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.26.1/pyproject.toml new/starlette-0.27.0/pyproject.toml --- old/starlette-0.26.1/pyproject.toml 2023-03-13 19:08:31.000000000 +0100 +++ new/starlette-0.27.0/pyproject.toml 2023-05-16 12:56:45.000000000 +0200 @@ -55,3 +55,9 @@ include = [ "/starlette", ] + +[tool.ruff] +select = ["E", "F", "I"] + +[tool.ruff.isort] +combine-as-imports = true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.26.1/requirements.txt new/starlette-0.27.0/requirements.txt --- old/starlette-0.26.1/requirements.txt 2023-03-13 19:08:31.000000000 +0100 +++ new/starlette-0.27.0/requirements.txt 2023-05-16 12:56:45.000000000 +0200 @@ -2,23 +2,21 @@ -e .[full] # Testing -autoflake==1.5.3 -black==22.12.0 -coverage==7.1.0 -flake8==3.9.2 +black==23.3.0 +coverage==7.2.5 importlib-metadata==4.13.0 -isort==5.10.1 -mypy==1.0.1 -typing_extensions==4.4.0 -types-contextvars==2.4.7.1 -types-PyYAML==6.0.12.3 +mypy==1.2.0 +ruff==0.0.263 +typing_extensions==4.5.0 +types-contextvars==2.4.7.2 +types-PyYAML==6.0.12.9 types-dataclasses==0.6.6 -pytest==7.2.1 +pytest==7.3.1 trio==0.21.0 # Documentation mkdocs==1.4.2 -mkdocs-material==9.0.15 +mkdocs-material==9.1.8 mkautodoc==0.2.0 # Packaging diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.26.1/scripts/check new/starlette-0.27.0/scripts/check --- old/starlette-0.26.1/scripts/check 2023-03-13 19:08:31.000000000 +0100 +++ new/starlette-0.27.0/scripts/check 2023-05-16 12:56:45.000000000 +0200 @@ -9,7 +9,6 @@ set -x ./scripts/sync-version -${PREFIX}isort --check --diff --project=starlette $SOURCE_FILES ${PREFIX}black --check --diff $SOURCE_FILES -${PREFIX}flake8 $SOURCE_FILES ${PREFIX}mypy $SOURCE_FILES +${PREFIX}ruff check $SOURCE_FILES diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.26.1/scripts/lint new/starlette-0.27.0/scripts/lint --- old/starlette-0.26.1/scripts/lint 2023-03-13 19:08:31.000000000 +0100 +++ new/starlette-0.27.0/scripts/lint 2023-05-16 12:56:45.000000000 +0200 @@ -8,6 +8,5 @@ set -x -${PREFIX}autoflake --in-place --recursive $SOURCE_FILES -${PREFIX}isort --project=starlette $SOURCE_FILES ${PREFIX}black $SOURCE_FILES +${PREFIX}ruff --fix $SOURCE_FILES diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.26.1/setup.cfg new/starlette-0.27.0/setup.cfg --- old/starlette-0.26.1/setup.cfg 2023-03-13 19:08:31.000000000 +0100 +++ new/starlette-0.27.0/setup.cfg 2023-05-16 12:56:45.000000000 +0200 @@ -1,7 +1,3 @@ -[flake8] -ignore = W503, E203, B305 -max-line-length = 88 - [mypy] disallow_untyped_defs = True ignore_missing_imports = True @@ -16,10 +12,6 @@ disallow_untyped_defs = False check_untyped_defs = True -[tool:isort] -profile = black -combine_as_imports = True - [tool:pytest] addopts = -rxXs diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.26.1/starlette/__init__.py new/starlette-0.27.0/starlette/__init__.py --- old/starlette-0.26.1/starlette/__init__.py 2023-03-13 19:08:31.000000000 +0100 +++ new/starlette-0.27.0/starlette/__init__.py 2023-05-16 12:56:45.000000000 +0200 @@ -1 +1 @@ -__version__ = "0.26.1" +__version__ = "0.27.0" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.26.1/starlette/_compat.py new/starlette-0.27.0/starlette/_compat.py --- old/starlette-0.26.1/starlette/_compat.py 2023-03-13 19:08:31.000000000 +0100 +++ new/starlette-0.27.0/starlette/_compat.py 2023-05-16 12:56:45.000000000 +0200 @@ -10,7 +10,6 @@ # # See issue: https://github.com/encode/starlette/issues/1365 try: - # check if the Python version supports the parameter # using usedforsecurity=False to avoid an exception on FIPS systems # that reject usedforsecurity=True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.26.1/starlette/endpoints.py new/starlette-0.27.0/starlette/endpoints.py --- old/starlette-0.26.1/starlette/endpoints.py 2023-03-13 19:08:31.000000000 +0100 +++ new/starlette-0.27.0/starlette/endpoints.py 2023-05-16 12:56:45.000000000 +0200 @@ -55,7 +55,6 @@ class WebSocketEndpoint: - encoding: typing.Optional[str] = None # May be "text", "bytes", or "json". def __init__(self, scope: Scope, receive: Receive, send: Send) -> None: @@ -91,7 +90,6 @@ await self.on_disconnect(websocket, close_code) async def decode(self, websocket: WebSocket, message: Message) -> typing.Any: - if self.encoding == "text": if "text" not in message: await websocket.close(code=status.WS_1003_UNSUPPORTED_DATA) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.26.1/starlette/formparsers.py new/starlette-0.27.0/starlette/formparsers.py --- old/starlette-0.26.1/starlette/formparsers.py 2023-03-13 19:08:31.000000000 +0100 +++ new/starlette-0.27.0/starlette/formparsers.py 2023-05-16 12:56:45.000000000 +0200 @@ -9,7 +9,7 @@ try: import multipart from multipart.multipart import parse_options_header -except ImportError: # pragma: nocover +except ModuleNotFoundError: # pragma: nocover parse_options_header = None multipart = None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.26.1/starlette/middleware/cors.py new/starlette-0.27.0/starlette/middleware/cors.py --- old/starlette-0.26.1/starlette/middleware/cors.py 2023-03-13 19:08:31.000000000 +0100 +++ new/starlette-0.27.0/starlette/middleware/cors.py 2023-05-16 12:56:45.000000000 +0200 @@ -22,7 +22,6 @@ expose_headers: typing.Sequence[str] = (), max_age: int = 600, ) -> None: - if "*" in allow_methods: allow_methods = ALL_METHODS diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.26.1/starlette/requests.py new/starlette-0.27.0/starlette/requests.py --- old/starlette-0.26.1/starlette/requests.py 2023-03-13 19:08:31.000000000 +0100 +++ new/starlette-0.27.0/starlette/requests.py 2023-05-16 12:56:45.000000000 +0200 @@ -12,7 +12,7 @@ try: from multipart.multipart import parse_options_header -except ImportError: # pragma: nocover +except ModuleNotFoundError: # pragma: nocover parse_options_header = None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.26.1/starlette/schemas.py new/starlette-0.27.0/starlette/schemas.py --- old/starlette-0.26.1/starlette/schemas.py 2023-03-13 19:08:31.000000000 +0100 +++ new/starlette-0.27.0/starlette/schemas.py 2023-05-16 12:56:45.000000000 +0200 @@ -8,7 +8,7 @@ try: import yaml -except ImportError: # pragma: nocover +except ModuleNotFoundError: # pragma: nocover yaml = None # type: ignore[assignment] @@ -132,7 +132,6 @@ endpoints_info = self.get_endpoints(routes) for endpoint in endpoints_info: - parsed = self.parse_docstring(endpoint.func) if not parsed: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.26.1/starlette/staticfiles.py new/starlette-0.27.0/starlette/staticfiles.py --- old/starlette-0.26.1/starlette/staticfiles.py 2023-03-13 19:08:31.000000000 +0100 +++ new/starlette-0.27.0/starlette/staticfiles.py 2023-05-16 12:56:45.000000000 +0200 @@ -169,7 +169,7 @@ else: full_path = os.path.realpath(joined_path) directory = os.path.realpath(directory) - if os.path.commonprefix([full_path, directory]) != directory: + if os.path.commonpath([full_path, directory]) != directory: # Don't allow misbehaving clients to break out of the static files # directory. continue diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.26.1/starlette/templating.py new/starlette-0.27.0/starlette/templating.py --- old/starlette-0.26.1/starlette/templating.py 2023-03-13 19:08:31.000000000 +0100 +++ new/starlette-0.27.0/starlette/templating.py 2023-05-16 12:56:45.000000000 +0200 @@ -18,7 +18,7 @@ pass_context = jinja2.pass_context else: # pragma: nocover pass_context = jinja2.contextfunction # type: ignore[attr-defined] -except ImportError: # pragma: nocover +except ModuleNotFoundError: # pragma: nocover jinja2 = None # type: ignore[assignment] @@ -68,7 +68,7 @@ context_processors: typing.Optional[ typing.List[typing.Callable[[Request], typing.Dict[str, typing.Any]]] ] = None, - **env_options: typing.Any + **env_options: typing.Any, ) -> None: assert jinja2 is not None, "jinja2 must be installed to use Jinja2Templates" self.env = self._create_env(directory, **env_options) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.26.1/starlette/testclient.py new/starlette-0.27.0/starlette/testclient.py --- old/starlette-0.26.1/starlette/testclient.py 2023-03-13 19:08:31.000000000 +0100 +++ new/starlette-0.27.0/starlette/testclient.py 2023-05-16 12:56:45.000000000 +0200 @@ -145,7 +145,7 @@ def send_json(self, data: typing.Any, mode: str = "text") -> None: assert mode in ["text", "binary"] - text = json.dumps(data) + text = json.dumps(data, separators=(",", ":")) if mode == "text": self.send({"type": "websocket.receive", "text": text}) else: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.26.1/starlette/websockets.py new/starlette-0.27.0/starlette/websockets.py --- old/starlette-0.26.1/starlette/websockets.py 2023-03-13 19:08:31.000000000 +0100 +++ new/starlette-0.27.0/starlette/websockets.py 2023-05-16 12:56:45.000000000 +0200 @@ -65,8 +65,8 @@ message_type = message["type"] if message_type not in {"websocket.accept", "websocket.close"}: raise RuntimeError( - 'Expected ASGI message "websocket.connect", ' - f"but got {message_type!r}" + 'Expected ASGI message "websocket.accept" or ' + f'"websocket.close", but got {message_type!r}' ) if message_type == "websocket.close": self.application_state = WebSocketState.DISCONNECTED @@ -168,7 +168,7 @@ async def send_json(self, data: typing.Any, mode: str = "text") -> None: if mode not in {"text", "binary"}: raise RuntimeError('The "mode" argument should be "text" or "binary".') - text = json.dumps(data) + text = json.dumps(data, separators=(",", ":")) if mode == "text": await self.send({"type": "websocket.send", "text": text}) else: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/starlette-0.26.1/tests/test_staticfiles.py new/starlette-0.27.0/tests/test_staticfiles.py --- old/starlette-0.26.1/tests/test_staticfiles.py 2023-03-13 19:08:31.000000000 +0100 +++ new/starlette-0.27.0/tests/test_staticfiles.py 2023-05-16 12:56:45.000000000 +0200 @@ -1,8 +1,8 @@ import os -import pathlib import stat import tempfile import time +from pathlib import Path import anyio import pytest @@ -28,13 +28,12 @@ assert response.text == "<file content>" -def test_staticfiles_with_pathlib(tmpdir, test_client_factory): - base_dir = pathlib.Path(tmpdir) - path = base_dir / "example.txt" +def test_staticfiles_with_pathlib(tmp_path: Path, test_client_factory): + path = tmp_path / "example.txt" with open(path, "w") as file: file.write("<file content>") - app = StaticFiles(directory=base_dir) + app = StaticFiles(directory=tmp_path) client = test_client_factory(app) response = client.get("/example.txt") assert response.status_code == 200 @@ -516,3 +515,36 @@ assert exc_info.value.status_code == 404 assert exc_info.value.detail == "Not Found" + + +def test_staticfiles_avoids_path_traversal(tmp_path: Path): + statics_path = tmp_path / "static" + statics_disallow_path = tmp_path / "static_disallow" + + statics_path.mkdir() + statics_disallow_path.mkdir() + + static_index_file = statics_path / "index.html" + statics_disallow_path_index_file = statics_disallow_path / "index.html" + static_file = tmp_path / "static1.txt" + + static_index_file.write_text("<h1>Hello</h1>") + statics_disallow_path_index_file.write_text("<h1>Private</h1>") + static_file.write_text("Private") + + app = StaticFiles(directory=statics_path) + + # We can't test this with 'httpx', so we test the app directly here. + path = app.get_path({"path": "/../static1.txt"}) + with pytest.raises(HTTPException) as exc_info: + anyio.run(app.get_response, path, {"method": "GET"}) + + assert exc_info.value.status_code == 404 + assert exc_info.value.detail == "Not Found" + + path = app.get_path({"path": "/../static_disallow/index.html"}) + with pytest.raises(HTTPException) as exc_info: + anyio.run(app.get_response, path, {"method": "GET"}) + + assert exc_info.value.status_code == 404 + assert exc_info.value.detail == "Not Found"