blob: c892fed2d424a5fc5ddd1247883835fe8d5663b0 [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"""
Armin Ronacher644a2812010-02-09 18:06:32 +010011import unittest
12
13from jinja2.testsuite import JinjaTestCase
14
15from jinja2 import Environment
16from jinja2.sandbox import SandboxedEnvironment, \
17 ImmutableSandboxedEnvironment, unsafe
18from jinja2 import Markup, escape
Armin Ronachera9195382010-11-29 13:21:57 +010019from jinja2.exceptions import SecurityError, TemplateSyntaxError, \
20 TemplateRuntimeError
Thomas Waldmanne0003552013-05-17 23:52:14 +020021import six
Armin Ronacher644a2812010-02-09 18:06:32 +010022
23
24class PrivateStuff(object):
25
26 def bar(self):
27 return 23
28
29 @unsafe
30 def foo(self):
31 return 42
32
33 def __repr__(self):
34 return 'PrivateStuff'
35
36
37class PublicStuff(object):
38 bar = lambda self: 23
39 _foo = lambda self: 42
40
41 def __repr__(self):
42 return 'PublicStuff'
43
44
45class SandboxTestCase(JinjaTestCase):
46
47 def test_unsafe(self):
48 env = SandboxedEnvironment()
49 self.assert_raises(SecurityError, env.from_string("{{ foo.foo() }}").render,
50 foo=PrivateStuff())
51 self.assert_equal(env.from_string("{{ foo.bar() }}").render(foo=PrivateStuff()), '23')
52
53 self.assert_raises(SecurityError, env.from_string("{{ foo._foo() }}").render,
54 foo=PublicStuff())
55 self.assert_equal(env.from_string("{{ foo.bar() }}").render(foo=PublicStuff()), '23')
56 self.assert_equal(env.from_string("{{ foo.__class__ }}").render(foo=42), '')
57 self.assert_equal(env.from_string("{{ foo.func_code }}").render(foo=lambda:None), '')
Armin Ronacher6a3e95d2010-11-19 13:51:38 +010058 # security error comes from __class__ already.
Armin Ronacher644a2812010-02-09 18:06:32 +010059 self.assert_raises(SecurityError, env.from_string(
60 "{{ foo.__class__.__subclasses__() }}").render, foo=42)
61
Armin Ronacher1fb42692010-02-09 21:14:16 +010062 def test_immutable_environment(self):
63 env = ImmutableSandboxedEnvironment()
64 self.assert_raises(SecurityError, env.from_string(
65 '{{ [].append(23) }}').render)
66 self.assert_raises(SecurityError, env.from_string(
67 '{{ {1:2}.clear() }}').render)
68
Armin Ronacher644a2812010-02-09 18:06:32 +010069 def test_restricted(self):
70 env = SandboxedEnvironment()
71 self.assert_raises(TemplateSyntaxError, env.from_string,
72 "{% for item.attribute in seq %}...{% endfor %}")
73 self.assert_raises(TemplateSyntaxError, env.from_string,
74 "{% for foo, bar.baz in seq %}...{% endfor %}")
75
76 def test_markup_operations(self):
77 # 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>')
Thomas Waldmanne0003552013-05-17 23:52:14 +020080 assert unsafe + safe == six.text_type(escape(unsafe)) + six.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>' == \
84 '<em>&lt;bad user&gt;</em>'
85 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>'
100 def __unicode__(self):
101 return 'awesome'
102 assert Markup(Foo()) == '<em>awesome</em>'
103 assert Markup('<strong>%s</strong>') % Foo() == \
104 '<strong><em>awesome</em></strong>'
105
106 # escaping and unescaping
107 assert escape('"<>&\'') == '&#34;&lt;&gt;&amp;&#39;'
108 assert Markup("<em>Foo &amp; Bar</em>").striptags() == "Foo & Bar"
109 assert Markup("&lt;test&gt;").unescape() == "<test>"
110
Armin Ronacher644a2812010-02-09 18:06:32 +0100111 def test_template_data(self):
112 env = Environment(autoescape=True)
113 t = env.from_string('{% macro say_hello(name) %}'
114 '<p>Hello {{ name }}!</p>{% endmacro %}'
115 '{{ say_hello("<blink>foo</blink>") }}')
116 escaped_out = '<p>Hello &lt;blink&gt;foo&lt;/blink&gt;!</p>'
117 assert t.render() == escaped_out
Thomas Waldmanne0003552013-05-17 23:52:14 +0200118 assert six.text_type(t.module) == escaped_out
Armin Ronacher644a2812010-02-09 18:06:32 +0100119 assert escape(t.module) == escaped_out
120 assert t.module.say_hello('<blink>foo</blink>') == escaped_out
121 assert escape(t.module.say_hello('<blink>foo</blink>')) == escaped_out
122
Armin Ronacher644a2812010-02-09 18:06:32 +0100123 def test_attr_filter(self):
124 env = SandboxedEnvironment()
Armin Ronacher6a3e95d2010-11-19 13:51:38 +0100125 tmpl = env.from_string('{{ cls|attr("__subclasses__")() }}')
126 self.assert_raises(SecurityError, tmpl.render, cls=int)
Armin Ronacher644a2812010-02-09 18:06:32 +0100127
Armin Ronachera9195382010-11-29 13:21:57 +0100128 def test_binary_operator_intercepting(self):
129 def disable_op(left, right):
130 raise TemplateRuntimeError('that operator so does not work')
131 for expr, ctx, rv in ('1 + 2', {}, '3'), ('a + 2', {'a': 2}, '4'):
132 env = SandboxedEnvironment()
133 env.binop_table['+'] = disable_op
134 t = env.from_string('{{ %s }}' % expr)
135 assert t.render(ctx) == rv
136 env.intercepted_binops = frozenset(['+'])
137 t = env.from_string('{{ %s }}' % expr)
138 try:
139 t.render(ctx)
Thomas Waldmanne0003552013-05-17 23:52:14 +0200140 except TemplateRuntimeError as e:
Armin Ronachera9195382010-11-29 13:21:57 +0100141 pass
142 else:
143 self.fail('expected runtime error')
144
145 def test_unary_operator_intercepting(self):
146 def disable_op(arg):
147 raise TemplateRuntimeError('that operator so does not work')
148 for expr, ctx, rv in ('-1', {}, '-1'), ('-a', {'a': 2}, '-2'):
149 env = SandboxedEnvironment()
150 env.unop_table['-'] = disable_op
151 t = env.from_string('{{ %s }}' % expr)
152 assert t.render(ctx) == rv
153 env.intercepted_unops = frozenset(['-'])
154 t = env.from_string('{{ %s }}' % expr)
155 try:
156 t.render(ctx)
Thomas Waldmanne0003552013-05-17 23:52:14 +0200157 except TemplateRuntimeError as e:
Armin Ronachera9195382010-11-29 13:21:57 +0100158 pass
159 else:
160 self.fail('expected runtime error')
161
Armin Ronacher644a2812010-02-09 18:06:32 +0100162
163def suite():
164 suite = unittest.TestSuite()
165 suite.addTest(unittest.makeSuite(SandboxTestCase))
166 return suite