diff --git a/CHANGES.rst b/CHANGES.rst index 5019208e4..fd6c1a929 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -11,6 +11,15 @@ Unreleased - Use ``flit_core`` instead of ``setuptools`` as build backend. +Version 3.1.6 +------------- + +Released 2025-03-05 + +- The ``|attr`` filter does not bypass the environment's attribute lookup, + allowing the sandbox to apply its checks. :ghsa:`cpwx-vrp4-4pq7` + + Version 3.1.5 ------------- diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst deleted file mode 100644 index ec2420a3f..000000000 --- a/CONTRIBUTING.rst +++ /dev/null @@ -1,216 +0,0 @@ -How to contribute to Jinja -========================== - -Thank you for considering contributing to Jinja! - - -Support questions ------------------ - -Please don't use the issue tracker for this. The issue tracker is a -tool to address bugs and feature requests in Jinja itself. Use one of -the following resources for questions about using Jinja or issues with -your own code: - -- The ``#get-help`` channel on our Discord chat: - https://discord.gg/pallets -- The mailing list flask@python.org for long term discussion or larger - issues. -- Ask on `Stack Overflow`_. Search with Google first using: - ``site:stackoverflow.com jinja {search term, exception message, etc.}`` - -.. _Stack Overflow: https://stackoverflow.com/questions/tagged/jinja?tab=Frequent - - -Reporting issues ----------------- - -Include the following information in your post: - -- Describe what you expected to happen. -- If possible, include a `minimal reproducible example`_ to help us - identify the issue. This also helps check that the issue is not with - your own code. -- Describe what actually happened. Include the full traceback if there - was an exception. -- List your Python and Jinja versions. If possible, check if this - issue is already fixed in the latest releases or the latest code in - the repository. - -.. _minimal reproducible example: https://stackoverflow.com/help/minimal-reproducible-example - - -Submitting patches ------------------- - -If there is not an open issue for what you want to submit, prefer -opening one for discussion before working on a PR. You can work on any -issue that doesn't have an open PR linked to it or a maintainer assigned -to it. These show up in the sidebar. No need to ask if you can work on -an issue that interests you. - -Include the following in your patch: - -- Use `Black`_ to format your code. This and other tools will run - automatically if you install `pre-commit`_ using the instructions - below. -- Include tests if your patch adds or changes code. Make sure the test - fails without your patch. -- Update any relevant docs pages and docstrings. Docs pages and - docstrings should be wrapped at 72 characters. -- Add an entry in ``CHANGES.rst``. Use the same style as other - entries. Also include ``.. versionchanged::`` inline changelogs in - relevant docstrings. - -.. _Black: https://black.readthedocs.io -.. _pre-commit: https://pre-commit.com - - -First time setup -~~~~~~~~~~~~~~~~ - -- Download and install the `latest version of git`_. -- Configure git with your `username`_ and `email`_. - - .. code-block:: text - - $ git config --global user.name 'your name' - $ git config --global user.email 'your email' - -- Make sure you have a `GitHub account`_. -- Fork Jinja to your GitHub account by clicking the `Fork`_ button. -- `Clone`_ the main repository locally. - - .. code-block:: text - - $ git clone https://github.com/pallets/jinja - $ cd jinja - -- Add your fork as a remote to push your work to. Replace - ``{username}`` with your username. This names the remote "fork", the - default Pallets remote is "origin". - - .. code-block:: text - - $ git remote add fork https://github.com/{username}/jinja - -- Create a virtualenv. - - .. code-block:: text - - $ python3 -m venv env - $ . env/bin/activate - - On Windows, activating is different. - - .. code-block:: text - - > env\Scripts\activate - -- Install the development dependencies, then install Jinja in editable - mode. - - .. code-block:: text - - $ pip install -r requirements/dev.txt && pip install -e . - -- Install the pre-commit hooks. - - .. code-block:: text - - $ pre-commit install - -.. _latest version of git: https://git-scm.com/downloads -.. _username: https://docs.github.com/en/github/using-git/setting-your-username-in-git -.. _email: https://docs.github.com/en/github/setting-up-and-managing-your-github-user-account/setting-your-commit-email-address -.. _GitHub account: https://github.com/join -.. _Fork: https://github.com/pallets/jinja/fork -.. _Clone: https://docs.github.com/en/github/getting-started-with-github/fork-a-repo#step-2-create-a-local-clone-of-your-fork - - -Start coding -~~~~~~~~~~~~ - -- Create a branch to identify the issue you would like to work on. If - you're submitting a bug or documentation fix, branch off of the - latest ".x" branch. - - .. code-block:: text - - $ git fetch origin - $ git checkout -b your-branch-name origin/3.0.x - - If you're submitting a feature addition or change, branch off of the - "main" branch. - - .. code-block:: text - - $ git fetch origin - $ git checkout -b your-branch-name origin/main - -- Using your favorite editor, make your changes, - `committing as you go`_. -- Include tests that cover any code changes you make. Make sure the - test fails without your patch. Run the tests as described below. -- Push your commits to your fork on GitHub and - `create a pull request`_. Link to the issue being addressed with - ``fixes #123`` in the pull request. - - .. code-block:: text - - $ git push --set-upstream fork your-branch-name - -.. _committing as you go: https://dont-be-afraid-to-commit.readthedocs.io/en/latest/git/commandlinegit.html#commit-your-changes -.. _create a pull request: https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request - - -Running the tests -~~~~~~~~~~~~~~~~~ - -Run the basic test suite with pytest. - -.. code-block:: text - - $ pytest - -This runs the tests for the current environment, which is usually -sufficient. CI will run the full suite when you submit your pull -request. You can run the full test suite with tox if you don't want to -wait. - -.. code-block:: text - - $ tox - - -Running test coverage -~~~~~~~~~~~~~~~~~~~~~ - -Generating a report of lines that do not have test coverage can indicate -where to start contributing. Run ``pytest`` using ``coverage`` and -generate a report. - -.. code-block:: text - - $ pip install coverage - $ coverage run -m pytest - $ coverage html - -Open ``htmlcov/index.html`` in your browser to explore the report. - -Read more about `coverage `__. - - -Building the docs -~~~~~~~~~~~~~~~~~ - -Build the docs in the ``docs`` directory using Sphinx. - -.. code-block:: text - - $ cd docs - $ make html - -Open ``_build/html/index.html`` in your browser to view the docs. - -Read more about `Sphinx `__. diff --git a/README.md b/README.md index f4aa7cbea..d1a6870d0 100644 --- a/README.md +++ b/README.md @@ -47,3 +47,11 @@ allow the maintainers to devote more time to the projects, [please donate today][]. [please donate today]: https://palletsprojects.com/donate + +## Contributing + +See our [detailed contributing documentation][contrib] for many ways to +contribute, including reporting issues, requesting features, asking or answering +questions, and making PRs. + +[contrib]: https://palletsprojects.com/contributing/ diff --git a/src/jinja2/filters.py b/src/jinja2/filters.py index e5b5a00c5..2bcba4fbd 100644 --- a/src/jinja2/filters.py +++ b/src/jinja2/filters.py @@ -6,6 +6,7 @@ import typing import typing as t from collections import abc +from inspect import getattr_static from itertools import chain from itertools import groupby @@ -1411,31 +1412,25 @@ def do_reverse(value: t.Union[str, t.Iterable[V]]) -> t.Union[str, t.Iterable[V] def do_attr( environment: "Environment", obj: t.Any, name: str ) -> t.Union[Undefined, t.Any]: - """Get an attribute of an object. ``foo|attr("bar")`` works like - ``foo.bar`` just that always an attribute is returned and items are not - looked up. + """Get an attribute of an object. ``foo|attr("bar")`` works like + ``foo.bar``, but returns undefined instead of falling back to ``foo["bar"]`` + if the attribute doesn't exist. See :ref:`Notes on subscriptions ` for more details. """ + # Environment.getattr will fall back to obj[name] if obj.name doesn't exist. + # But we want to call env.getattr to get behavior such as sandboxing. + # Determine if the attr exists first, so we know the fallback won't trigger. try: - name = str(name) - except UnicodeError: - pass - else: - try: - value = getattr(obj, name) - except AttributeError: - pass - else: - if environment.sandboxed: - environment = t.cast("SandboxedEnvironment", environment) - - if not environment.is_safe_attribute(obj, name, value): - return environment.unsafe_undefined(obj, name) - - return value - - return environment.undefined(obj=obj, name=name) + # This avoids executing properties/descriptors, but misses __getattr__ + # and __getattribute__ dynamic attrs. + getattr_static(obj, name) + except AttributeError: + # This finds dynamic attrs, and we know it's not a descriptor at this point. + if not hasattr(obj, name): + return environment.undefined(obj=obj, name=name) + + return environment.getattr(obj, name) @typing.overload diff --git a/tests/test_security.py b/tests/test_security.py index 864d5f7f9..3a1378192 100644 --- a/tests/test_security.py +++ b/tests/test_security.py @@ -190,3 +190,13 @@ def run(value, arg): with pytest.raises(SecurityError): t.render() + + def test_attr_filter(self) -> None: + env = SandboxedEnvironment() + t = env.from_string( + """{{ "{0.__call__.__builtins__[__import__]}" + | attr("format")(not_here) }}""" + ) + + with pytest.raises(SecurityError): + t.render()