blob: c0442ce79bcb5c19ae4db835ffd330ae0e41b85c [file] [log] [blame]
Armin Ronacher644a2812010-02-09 18:06:32 +01001# -*- coding: utf-8 -*-
2"""
3 jinja2.testsuite.security
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
5
6 Checks the sandbox and other security features.
7
8 :copyright: (c) 2010 by the Jinja Team.
9 :license: BSD, see LICENSE for more details.
10"""
Kartheek Lenkala9d4afa12015-03-22 15:28:54 +053011import pytest
Armin Ronacher644a2812010-02-09 18:06:32 +010012
13from jinja2 import Environment
14from jinja2.sandbox import SandboxedEnvironment, \
Armin Ronacher9b530452016-12-29 14:13:38 +010015 ImmutableSandboxedEnvironment, unsafe, has_format
Armin Ronacher644a2812010-02-09 18:06:32 +010016from jinja2 import Markup, escape
Armin Ronachera9195382010-11-29 13:21:57 +010017from jinja2.exceptions import SecurityError, TemplateSyntaxError, \
18 TemplateRuntimeError
Armin Ronacher444fec32013-05-19 14:22:08 +010019from jinja2._compat import text_type
Armin Ronacher644a2812010-02-09 18:06:32 +010020
21
22class PrivateStuff(object):
23
24 def bar(self):
25 return 23
26
27 @unsafe
28 def foo(self):
29 return 42
30
31 def __repr__(self):
32 return 'PrivateStuff'
33
34
35class PublicStuff(object):
36 bar = lambda self: 23
37 _foo = lambda self: 42
38
39 def __repr__(self):
40 return 'PublicStuff'
41
42
Kartheek Lenkala9d4afa12015-03-22 15:28:54 +053043@pytest.mark.sandbox
44class TestSandbox():
Armin Ronacher644a2812010-02-09 18:06:32 +010045
Kartheek Lenkala9d4afa12015-03-22 15:28:54 +053046 def test_unsafe(self, env):
Armin Ronacher644a2812010-02-09 18:06:32 +010047 env = SandboxedEnvironment()
Kartheek Lenkala9d4afa12015-03-22 15:28:54 +053048 pytest.raises(SecurityError, env.from_string("{{ foo.foo() }}").render,
49 foo=PrivateStuff())
50 assert env.from_string("{{ foo.bar() }}").render(foo=PrivateStuff()) == '23'
Armin Ronacher644a2812010-02-09 18:06:32 +010051
Kartheek Lenkala9d4afa12015-03-22 15:28:54 +053052 pytest.raises(SecurityError,
53 env.from_string("{{ foo._foo() }}").render,
54 foo=PublicStuff())
55 assert env.from_string("{{ foo.bar() }}").render(foo=PublicStuff()) == '23'
56 assert env.from_string("{{ foo.__class__ }}").render(foo=42) == ''
57 assert env.from_string("{{ foo.func_code }}").render(foo=lambda:None) == ''
Armin Ronacher6a3e95d2010-11-19 13:51:38 +010058 # security error comes from __class__ already.
Kartheek Lenkala9d4afa12015-03-22 15:28:54 +053059 pytest.raises(SecurityError, env.from_string(
Armin Ronacher644a2812010-02-09 18:06:32 +010060 "{{ foo.__class__.__subclasses__() }}").render, foo=42)
61
Kartheek Lenkala9d4afa12015-03-22 15:28:54 +053062 def test_immutable_environment(self, env):
Armin Ronacher1fb42692010-02-09 21:14:16 +010063 env = ImmutableSandboxedEnvironment()
Kartheek Lenkala9d4afa12015-03-22 15:28:54 +053064 pytest.raises(SecurityError, env.from_string(
Armin Ronacher1fb42692010-02-09 21:14:16 +010065 '{{ [].append(23) }}').render)
Kartheek Lenkala9d4afa12015-03-22 15:28:54 +053066 pytest.raises(SecurityError, env.from_string(
Armin Ronacher1fb42692010-02-09 21:14:16 +010067 '{{ {1:2}.clear() }}').render)
68
Kartheek Lenkala9d4afa12015-03-22 15:28:54 +053069 def test_restricted(self, env):
Armin Ronacher644a2812010-02-09 18:06:32 +010070 env = SandboxedEnvironment()
Kartheek Lenkala9d4afa12015-03-22 15:28:54 +053071 pytest.raises(TemplateSyntaxError, env.from_string,
Armin Ronacher644a2812010-02-09 18:06:32 +010072 "{% for item.attribute in seq %}...{% endfor %}")
Kartheek Lenkala9d4afa12015-03-22 15:28:54 +053073 pytest.raises(TemplateSyntaxError, env.from_string,
Armin Ronacher644a2812010-02-09 18:06:32 +010074 "{% for foo, bar.baz in seq %}...{% endfor %}")
75
Kartheek Lenkala9d4afa12015-03-22 15:28:54 +053076 def test_markup_operations(self, env):
Armin Ronacher644a2812010-02-09 18:06:32 +010077 # adding two strings should escape the unsafe one
78 unsafe = '<script type="application/x-some-script">alert("foo");</script>'
79 safe = Markup('<em>username</em>')
Armin Ronacher444fec32013-05-19 14:22:08 +010080 assert unsafe + safe == text_type(escape(unsafe)) + text_type(safe)
Armin Ronacher644a2812010-02-09 18:06:32 +010081
82 # string interpolations are safe to use too
83 assert Markup('<em>%s</em>') % '<bad user>' == \
Kartheek Lenkala9d4afa12015-03-22 15:28:54 +053084 '<em>&lt;bad user&gt;</em>'
Armin Ronacher644a2812010-02-09 18:06:32 +010085 assert Markup('<em>%(username)s</em>') % {
86 'username': '<bad user>'
87 } == '<em>&lt;bad user&gt;</em>'
88
89 # an escaped object is markup too
90 assert type(Markup('foo') + 'bar') is Markup
91
92 # and it implements __html__ by returning itself
93 x = Markup("foo")
94 assert x.__html__() is x
95
96 # it also knows how to treat __html__ objects
97 class Foo(object):
98 def __html__(self):
99 return '<em>awesome</em>'
Kartheek Lenkala9d4afa12015-03-22 15:28:54 +0530100
Armin Ronacher644a2812010-02-09 18:06:32 +0100101 def __unicode__(self):
102 return 'awesome'
103 assert Markup(Foo()) == '<em>awesome</em>'
104 assert Markup('<strong>%s</strong>') % Foo() == \
Kartheek Lenkala9d4afa12015-03-22 15:28:54 +0530105 '<strong><em>awesome</em></strong>'
Armin Ronacher644a2812010-02-09 18:06:32 +0100106
107 # escaping and unescaping
108 assert escape('"<>&\'') == '&#34;&lt;&gt;&amp;&#39;'
109 assert Markup("<em>Foo &amp; Bar</em>").striptags() == "Foo & Bar"
110 assert Markup("&lt;test&gt;").unescape() == "<test>"
111
Kartheek Lenkala9d4afa12015-03-22 15:28:54 +0530112 def test_template_data(self, env):
Armin Ronacher644a2812010-02-09 18:06:32 +0100113 env = Environment(autoescape=True)
114 t = env.from_string('{% macro say_hello(name) %}'
115 '<p>Hello {{ name }}!</p>{% endmacro %}'
116 '{{ say_hello("<blink>foo</blink>") }}')
117 escaped_out = '<p>Hello &lt;blink&gt;foo&lt;/blink&gt;!</p>'
118 assert t.render() == escaped_out
Armin Ronacher444fec32013-05-19 14:22:08 +0100119 assert text_type(t.module) == escaped_out
Armin Ronacher644a2812010-02-09 18:06:32 +0100120 assert escape(t.module) == escaped_out
121 assert t.module.say_hello('<blink>foo</blink>') == escaped_out
122 assert escape(t.module.say_hello('<blink>foo</blink>')) == escaped_out
123
Kartheek Lenkala9d4afa12015-03-22 15:28:54 +0530124 def test_attr_filter(self, env):
Armin Ronacher644a2812010-02-09 18:06:32 +0100125 env = SandboxedEnvironment()
Armin Ronacher6a3e95d2010-11-19 13:51:38 +0100126 tmpl = env.from_string('{{ cls|attr("__subclasses__")() }}')
Kartheek Lenkala9d4afa12015-03-22 15:28:54 +0530127 pytest.raises(SecurityError, tmpl.render, cls=int)
Armin Ronacher644a2812010-02-09 18:06:32 +0100128
Kartheek Lenkala9d4afa12015-03-22 15:28:54 +0530129 def test_binary_operator_intercepting(self, env):
Armin Ronachera9195382010-11-29 13:21:57 +0100130 def disable_op(left, right):
131 raise TemplateRuntimeError('that operator so does not work')
132 for expr, ctx, rv in ('1 + 2', {}, '3'), ('a + 2', {'a': 2}, '4'):
133 env = SandboxedEnvironment()
134 env.binop_table['+'] = disable_op
135 t = env.from_string('{{ %s }}' % expr)
136 assert t.render(ctx) == rv
137 env.intercepted_binops = frozenset(['+'])
138 t = env.from_string('{{ %s }}' % expr)
139 try:
140 t.render(ctx)
Thomas Waldmanne0003552013-05-17 23:52:14 +0200141 except TemplateRuntimeError as e:
Armin Ronachera9195382010-11-29 13:21:57 +0100142 pass
143 else:
Kartheek Lenkala9d4afa12015-03-22 15:28:54 +0530144 assert False, 'expected runtime error'
Armin Ronachera9195382010-11-29 13:21:57 +0100145
Kartheek Lenkala9d4afa12015-03-22 15:28:54 +0530146 def test_unary_operator_intercepting(self, env):
Armin Ronachera9195382010-11-29 13:21:57 +0100147 def disable_op(arg):
148 raise TemplateRuntimeError('that operator so does not work')
149 for expr, ctx, rv in ('-1', {}, '-1'), ('-a', {'a': 2}, '-2'):
150 env = SandboxedEnvironment()
151 env.unop_table['-'] = disable_op
152 t = env.from_string('{{ %s }}' % expr)
153 assert t.render(ctx) == rv
154 env.intercepted_unops = frozenset(['-'])
155 t = env.from_string('{{ %s }}' % expr)
156 try:
157 t.render(ctx)
Thomas Waldmanne0003552013-05-17 23:52:14 +0200158 except TemplateRuntimeError as e:
Armin Ronachera9195382010-11-29 13:21:57 +0100159 pass
160 else:
Kartheek Lenkala9d4afa12015-03-22 15:28:54 +0530161 assert False, 'expected runtime error'
Armin Ronacher9b530452016-12-29 14:13:38 +0100162
163
164@pytest.mark.sandbox
165@pytest.mark.skipif(not has_format, reason='No format support')
166class TestStringFormat(object):
167
168 def test_basic_format_safety(self):
169 env = SandboxedEnvironment()
170 t = env.from_string('{{ "a{0.__class__}b".format(42) }}')
171 assert t.render() == 'ab'
172
173 def test_basic_format_all_okay(self):
174 env = SandboxedEnvironment()
175 t = env.from_string('{{ "a{0.foo}b".format({"foo": 42}) }}')
176 assert t.render() == 'a42b'
177
Miro Hrončokba7cba02016-12-29 14:28:38 +0100178 def test_safe_format_safety(self):
Armin Ronacher9b530452016-12-29 14:13:38 +0100179 env = SandboxedEnvironment()
180 t = env.from_string('{{ ("a{0.__class__}b{1}"|safe).format(42, "<foo>") }}')
181 assert t.render() == 'ab&lt;foo&gt;'
182
Miro Hrončokba7cba02016-12-29 14:28:38 +0100183 def test_safe_format_all_okay(self):
Armin Ronacher9b530452016-12-29 14:13:38 +0100184 env = SandboxedEnvironment()
185 t = env.from_string('{{ ("a{0.foo}b{1}"|safe).format({"foo": 42}, "<foo>") }}')
186 assert t.render() == 'a42b&lt;foo&gt;'