VYPR
High severityOSV Advisory· Published Apr 8, 2019· Updated Aug 6, 2024

CVE-2016-10745

CVE-2016-10745

Description

In Pallets Jinja before 2.8.1, str.format allows a sandbox escape.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Jinja2 before 2.8.1 allows sandbox escape via Python str.format, leading to potential information disclosure.

Root

Cause

Jinja2, a popular templating engine for Python, prior to version 2.8.1, contains a sandbox escape vulnerability related to the use of Python's str.format method. The sandbox is intended to prevent template code from accessing unsafe functions or attributes. However, the str.format method, when used in a Jinja2 template, can be manipulated to leak internal objects and bypass these restrictions [4]. This is because str.format does not respect the sandboxed attribute access controls, allowing crafted template strings to access attributes and items on format arguments.

Exploitation

To exploit this vulnerability, an attacker needs the ability to inject or control the template content processed by an application that uses a vulnerable version of Jinja2 (before 2.8.1). The attacker crafts a template that uses str.format with specially crafted format strings to access and potentially leak sensitive information from the Python environment. The vulnerability is present when untrusted users are allowed to create or modify Jinja2 templates, such as in web applications that allow user-defined templates or in collaborative platforms where templates are shared [1][2][3].

Impact

Successful exploitation can lead to information disclosure. An attacker may be able to read environment variables, application secrets, or other sensitive data accessible from the Python runtime. The Red Hat security advisory rates this as an Important severity issue, with a CVSS base score that reflects the potential for significant confidentiality impact [1][2][3]. The NVD entry confirms the sandbox escape vector [4].

Mitigation

The vulnerability is fixed in Jinja2 version 2.8.1 and later. Users are advised to upgrade to the latest release. Red Hat has released updates for Red Hat Software Collections (python27-python and python27-python-jinja2) and Red Hat Enterprise Linux to address this CVE [1][2][3]. No workaround is available; upgrading is the only remediation.

AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
Jinja2PyPI
< 2.8.12.8.1

Affected products

77

Patches

1
9b53045c34e6

SECURITY: support sandboxing in format expressions

https://github.com/pallets/jinjaArmin RonacherDec 29, 2016via ghsa
3 files changed · +143 5
  • jinja2/nodes.py+1 1 modified
    @@ -604,7 +604,7 @@ class Call(Expr):
     
         def as_const(self, eval_ctx=None):
             eval_ctx = get_eval_context(self, eval_ctx)
    -        if eval_ctx.volatile:
    +        if eval_ctx.volatile or eval_ctx.environment.sandboxed:
                 raise Impossible()
             obj = self.node.as_const(eval_ctx)
     
    
  • jinja2/sandbox.py+116 3 modified
    @@ -14,9 +14,17 @@
     """
     import types
     import operator
    +from collections import Mapping
     from jinja2.environment import Environment
     from jinja2.exceptions import SecurityError
    -from jinja2._compat import string_types, PY2
    +from jinja2._compat import string_types, text_type, PY2
    +from jinja2.utils import Markup
    +
    +has_format = False
    +if hasattr(text_type, 'format'):
    +    from markupsafe import EscapeFormatter
    +    from string import Formatter
    +    has_format = True
     
     
     #: maximum number of items a range may produce
    @@ -38,6 +46,12 @@
     #: unsafe generator attirbutes.
     UNSAFE_GENERATOR_ATTRIBUTES = set(['gi_frame', 'gi_code'])
     
    +#: unsafe attributes on coroutines
    +UNSAFE_COROUTINE_ATTRIBUTES = set(['cr_frame', 'cr_code'])
    +
    +#: unsafe attributes on async generators
    +UNSAFE_ASYNC_GENERATOR_ATTRIBUTES = set(['ag_code', 'ag_frame'])
    +
     import warnings
     
     # make sure we don't warn in python 2.6 about stuff we don't care about
    @@ -94,6 +108,49 @@
     )
     
     
    +class _MagicFormatMapping(Mapping):
    +    """This class implements a dummy wrapper to fix a bug in the Python
    +    standard library for string formatting.
    +
    +    See http://bugs.python.org/issue13598 for information about why
    +    this is necessary.
    +    """
    +
    +    def __init__(self, args, kwargs):
    +        self._args = args
    +        self._kwargs = kwargs
    +        self._last_index = 0
    +
    +    def __getitem__(self, key):
    +        if key == '':
    +            idx = self._last_index
    +            self._last_index += 1
    +            try:
    +                return self._args[idx]
    +            except LookupError:
    +                pass
    +            key = str(idx)
    +        return self._kwargs[key]
    +
    +    def __iter__(self):
    +        return iter(self._kwargs)
    +
    +    def __len__(self):
    +        return len(self._kwargs)
    +
    +
    +def inspect_format_method(callable):
    +    if not has_format:
    +        return None
    +    if not isinstance(callable, (types.MethodType,
    +                                 types.BuiltinMethodType)) or \
    +       callable.__name__ != 'format':
    +        return None
    +    obj = callable.__self__
    +    if isinstance(obj, string_types):
    +        return obj
    +
    +
     def safe_range(*args):
         """A range that can't generate ranges with a length of more than
         MAX_RANGE items.
    @@ -145,6 +202,12 @@ def is_internal_attribute(obj, attr):
         elif isinstance(obj, types.GeneratorType):
             if attr in UNSAFE_GENERATOR_ATTRIBUTES:
                 return True
    +    elif hasattr(types, 'CoroutineType') and isinstance(obj, types.CoroutineType):
    +        if attr in UNSAFE_COROUTINE_ATTRIBUTES:
    +            return True
    +    elif hasattr(types, 'AsyncGeneratorType') and isinstance(obj, types.AsyncGeneratorType):
    +        if attri in UNSAFE_ASYNC_GENERATOR_ATTRIBUTES:
    +            return True
         return attr.startswith('__')
     
     
    @@ -183,8 +246,8 @@ class SandboxedEnvironment(Environment):
         attributes or functions are safe to access.
     
         If the template tries to access insecure code a :exc:`SecurityError` is
    -    raised.  However also other exceptions may occour during the rendering so
    -    the caller has to ensure that all exceptions are catched.
    +    raised.  However also other exceptions may occur during the rendering so
    +    the caller has to ensure that all exceptions are caught.
         """
         sandboxed = True
     
    @@ -346,8 +409,24 @@ def unsafe_undefined(self, obj, attribute):
                 obj.__class__.__name__
             ), name=attribute, obj=obj, exc=SecurityError)
     
    +    def format_string(self, s, args, kwargs):
    +        """If a format call is detected, then this is routed through this
    +        method so that our safety sandbox can be used for it.
    +        """
    +        if isinstance(s, Markup):
    +            formatter = SandboxedEscapeFormatter(self, s.escape)
    +        else:
    +            formatter = SandboxedFormatter(self)
    +        kwargs = _MagicFormatMapping(args, kwargs)
    +        rv = formatter.vformat(s, args, kwargs)
    +        return type(s)(rv)
    +
         def call(__self, __context, __obj, *args, **kwargs):
             """Call an object from sandboxed code."""
    +        fmt = inspect_format_method(__obj)
    +        if fmt is not None:
    +            return __self.format_string(fmt, args, kwargs)
    +
             # the double prefixes are to avoid double keyword argument
             # errors when proxying the call.
             if not __self.is_safe_callable(__obj):
    @@ -365,3 +444,37 @@ def is_safe_attribute(self, obj, attr, value):
             if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value):
                 return False
             return not modifies_known_mutable(obj, attr)
    +
    +
    +if has_format:
    +    # This really is not a public API apparenlty.
    +    try:
    +        from _string import formatter_field_name_split
    +    except ImportError:
    +        def formatter_field_name_split(field_name):
    +            return field_name._formatter_field_name_split()
    +
    +    class SandboxedFormatterMixin(object):
    +
    +        def __init__(self, env):
    +            self._env = env
    +
    +        def get_field(self, field_name, args, kwargs):
    +            first, rest = formatter_field_name_split(field_name)
    +            obj = self.get_value(first, args, kwargs)
    +            for is_attr, i in rest:
    +                if is_attr:
    +                    obj = self._env.getattr(obj, i)
    +                else:
    +                    obj = self._env.getitem(obj, i)
    +            return obj, first
    +
    +    class SandboxedFormatter(SandboxedFormatterMixin, Formatter):
    +        def __init__(self, env):
    +            SandboxedFormatterMixin.__init__(self, env)
    +            Formatter.__init__(self)
    +
    +    class SandboxedEscapeFormatter(SandboxedFormatterMixin, EscapeFormatter):
    +        def __init__(self, env, escape):
    +            SandboxedFormatterMixin.__init__(self, env)
    +            EscapeFormatter.__init__(self, escape)
    
  • tests/test_security.py+26 1 modified
    @@ -12,7 +12,7 @@
     
     from jinja2 import Environment
     from jinja2.sandbox import SandboxedEnvironment, \
    -     ImmutableSandboxedEnvironment, unsafe
    +     ImmutableSandboxedEnvironment, unsafe, has_format
     from jinja2 import Markup, escape
     from jinja2.exceptions import SecurityError, TemplateSyntaxError, \
          TemplateRuntimeError
    @@ -159,3 +159,28 @@ def disable_op(arg):
                     pass
                 else:
                     assert False, 'expected runtime error'
    +
    +
    +@pytest.mark.sandbox
    +@pytest.mark.skipif(not has_format, reason='No format support')
    +class TestStringFormat(object):
    +
    +    def test_basic_format_safety(self):
    +        env = SandboxedEnvironment()
    +        t = env.from_string('{{ "a{0.__class__}b".format(42) }}')
    +        assert t.render() == 'ab'
    +
    +    def test_basic_format_all_okay(self):
    +        env = SandboxedEnvironment()
    +        t = env.from_string('{{ "a{0.foo}b".format({"foo": 42}) }}')
    +        assert t.render() == 'a42b'
    +
    +    def test_basic_format_safety(self):
    +        env = SandboxedEnvironment()
    +        t = env.from_string('{{ ("a{0.__class__}b{1}"|safe).format(42, "<foo>") }}')
    +        assert t.render() == 'ab&lt;foo&gt;'
    +
    +    def test_basic_format_all_okay(self):
    +        env = SandboxedEnvironment()
    +        t = env.from_string('{{ ("a{0.foo}b{1}"|safe).format({"foo": 42}, "<foo>") }}')
    +        assert t.render() == 'a42b&lt;foo&gt;'
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

17

News mentions

0

No linked articles in our index yet.