1
# -*- coding: utf-8 -*-
3
jinja2.testsuite.security
4
~~~~~~~~~~~~~~~~~~~~~~~~~
6
Checks the sandbox and other security features.
8
:copyright: (c) 2010 by the Jinja Team.
9
:license: BSD, see LICENSE for more details.
13
from jinja2.testsuite import JinjaTestCase
15
from jinja2 import Environment
16
from jinja2.sandbox import SandboxedEnvironment, \
17
ImmutableSandboxedEnvironment, unsafe
18
from jinja2 import Markup, escape
19
from jinja2.exceptions import SecurityError, TemplateSyntaxError, \
21
from jinja2._compat import text_type
24
class PrivateStuff(object):
37
class PublicStuff(object):
39
_foo = lambda self: 42
45
class SandboxTestCase(JinjaTestCase):
47
def test_unsafe(self):
48
env = SandboxedEnvironment()
49
self.assert_raises(SecurityError, env.from_string("{{ foo.foo() }}").render,
51
self.assert_equal(env.from_string("{{ foo.bar() }}").render(foo=PrivateStuff()), '23')
53
self.assert_raises(SecurityError, env.from_string("{{ foo._foo() }}").render,
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), '')
58
# security error comes from __class__ already.
59
self.assert_raises(SecurityError, env.from_string(
60
"{{ foo.__class__.__subclasses__() }}").render, foo=42)
62
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)
69
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 %}")
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>')
80
assert unsafe + safe == text_type(escape(unsafe)) + text_type(safe)
82
# string interpolations are safe to use too
83
assert Markup('<em>%s</em>') % '<bad user>' == \
84
'<em><bad user></em>'
85
assert Markup('<em>%(username)s</em>') % {
86
'username': '<bad user>'
87
} == '<em><bad user></em>'
89
# an escaped object is markup too
90
assert type(Markup('foo') + 'bar') is Markup
92
# and it implements __html__ by returning itself
94
assert x.__html__() is x
96
# it also knows how to treat __html__ objects
99
return '<em>awesome</em>'
100
def __unicode__(self):
102
assert Markup(Foo()) == '<em>awesome</em>'
103
assert Markup('<strong>%s</strong>') % Foo() == \
104
'<strong><em>awesome</em></strong>'
106
# escaping and unescaping
107
assert escape('"<>&\'') == '"<>&''
108
assert Markup("<em>Foo & Bar</em>").striptags() == "Foo & Bar"
109
assert Markup("<test>").unescape() == "<test>"
111
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 <blink>foo</blink>!</p>'
117
assert t.render() == escaped_out
118
assert text_type(t.module) == escaped_out
119
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
123
def test_attr_filter(self):
124
env = SandboxedEnvironment()
125
tmpl = env.from_string('{{ cls|attr("__subclasses__")() }}')
126
self.assert_raises(SecurityError, tmpl.render, cls=int)
128
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)
140
except TemplateRuntimeError as e:
143
self.fail('expected runtime error')
145
def test_unary_operator_intercepting(self):
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)
157
except TemplateRuntimeError as e:
160
self.fail('expected runtime error')
164
suite = unittest.TestSuite()
165
suite.addTest(unittest.makeSuite(SandboxTestCase))