Package: jinja2 / 2.10-2

CVE-2019-10906_sandbox_str.format_map.patch Patch series | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
Description: CVE-2019-10906: sandbox str.format_map
Author: Armin Ronacher <armin.ronacher@active-4.com>
Date: Sat, 6 Apr 2019 10:50:47 -0700
Bug-Debian: https://bugs.debian.org/926602
Origin: upstream, https://github.com/pallets/jinja/commit/a2a6c930bcca591a25d2b316fcfd2d6793897b26.patch

diff --git a/jinja2/sandbox.py b/jinja2/sandbox.py
index 93fb9d45..752e8128 100644
--- a/jinja2/sandbox.py
+++ b/jinja2/sandbox.py
@@ -137,7 +137,7 @@ def __len__(self):
 def inspect_format_method(callable):
     if not isinstance(callable, (types.MethodType,
                                  types.BuiltinMethodType)) or \
-       callable.__name__ != 'format':
+       callable.__name__ not in ('format', 'format_map'):
         return None
     obj = callable.__self__
     if isinstance(obj, string_types):
@@ -402,7 +402,7 @@ def unsafe_undefined(self, obj, attribute):
             obj.__class__.__name__
         ), name=attribute, obj=obj, exc=SecurityError)
 
-    def format_string(self, s, args, kwargs):
+    def format_string(self, s, args, kwargs, format_func=None):
         """If a format call is detected, then this is routed through this
         method so that our safety sandbox can be used for it.
         """
@@ -410,6 +410,17 @@ def format_string(self, s, args, kwargs):
             formatter = SandboxedEscapeFormatter(self, s.escape)
         else:
             formatter = SandboxedFormatter(self)
+
+        if format_func is not None and format_func.__name__ == 'format_map':
+            if len(args) != 1 or kwargs:
+                raise TypeError(
+                    'format_map() takes exactly one argument %d given'
+                    % (len(args) + (kwargs is not None))
+                )
+
+            kwargs = args[0]
+            args = None
+
         kwargs = _MagicFormatMapping(args, kwargs)
         rv = formatter.vformat(s, args, kwargs)
         return type(s)(rv)
@@ -418,7 +429,7 @@ 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)
+            return __self.format_string(fmt, args, kwargs, __obj)
 
         # the double prefixes are to avoid double keyword argument
         # errors when proxying the call.
diff --git a/tests/test_security.py b/tests/test_security.py
index 8e4222e5..5c8639c4 100644
--- a/tests/test_security.py
+++ b/tests/test_security.py
@@ -187,3 +187,22 @@ def test_safe_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;'
+
+
+@pytest.mark.sandbox
+@pytest.mark.skipif(not hasattr(str, 'format_map'), reason='requires str.format_map method')
+class TestStringFormatMap(object):
+    def test_basic_format_safety(self):
+        env = SandboxedEnvironment()
+        t = env.from_string('{{ "a{x.__class__}b".format_map({"x":42}) }}')
+        assert t.render() == 'ab'
+
+    def test_basic_format_all_okay(self):
+        env = SandboxedEnvironment()
+        t = env.from_string('{{ "a{x.foo}b".format_map({"x":{"foo": 42}}) }}')
+        assert t.render() == 'a42b'
+
+    def test_safe_format_all_okay(self):
+        env = SandboxedEnvironment()
+        t = env.from_string('{{ ("a{x.foo}b{y}"|safe).format_map({"x":{"foo": 42}, "y":"<foo>"}) }}')
+        assert t.render() == 'a42b&lt;foo&gt;'