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
, \
23 class PrivateStuff(object):
36 class PublicStuff(object):
38 _foo
= lambda self
: 42
44 class SandboxTestCase(JinjaTestCase
):
46 def test_unsafe(self
):
47 env
= SandboxedEnvironment()
48 self
.assert_raises(SecurityError
, env
.from_string("{{ foo.foo() }}").render
,
50 self
.assert_equal(env
.from_string("{{ foo.bar() }}").render(foo
=PrivateStuff()), '23')
52 self
.assert_raises(SecurityError
, env
.from_string("{{ foo._foo() }}").render
,
54 self
.assert_equal(env
.from_string("{{ foo.bar() }}").render(foo
=PublicStuff()), '23')
55 self
.assert_equal(env
.from_string("{{ foo.__class__ }}").render(foo
=42), '')
56 self
.assert_equal(env
.from_string("{{ foo.func_code }}").render(foo
=lambda:None), '')
57 # security error comes from __class__ already.
58 self
.assert_raises(SecurityError
, env
.from_string(
59 "{{ foo.__class__.__subclasses__() }}").render
, foo
=42)
61 def test_immutable_environment(self
):
62 env
= ImmutableSandboxedEnvironment()
63 self
.assert_raises(SecurityError
, env
.from_string(
64 '{{ [].append(23) }}').render
)
65 self
.assert_raises(SecurityError
, env
.from_string(
66 '{{ {1:2}.clear() }}').render
)
68 def test_restricted(self
):
69 env
= SandboxedEnvironment()
70 self
.assert_raises(TemplateSyntaxError
, env
.from_string
,
71 "{% for item.attribute in seq %}...{% endfor %}")
72 self
.assert_raises(TemplateSyntaxError
, env
.from_string
,
73 "{% for foo, bar.baz in seq %}...{% endfor %}")
75 def test_markup_operations(self
):
76 # adding two strings should escape the unsafe one
77 unsafe
= '<script type="application/x-some-script">alert("foo");</script>'
78 safe
= Markup('<em>username</em>')
79 assert unsafe
+ safe
== unicode(escape(unsafe
)) + unicode(safe
)
81 # string interpolations are safe to use too
82 assert Markup('<em>%s</em>') % '<bad user>' == \
83 '<em><bad user></em>'
84 assert Markup('<em>%(username)s</em>') % {
85 'username': '<bad user>'
86 } == '<em><bad user></em>'
88 # an escaped object is markup too
89 assert type(Markup('foo') + 'bar') is Markup
91 # and it implements __html__ by returning itself
93 assert x
.__html
__() is x
95 # it also knows how to treat __html__ objects
98 return '<em>awesome</em>'
99 def __unicode__(self
):
101 assert Markup(Foo()) == '<em>awesome</em>'
102 assert Markup('<strong>%s</strong>') % Foo() == \
103 '<strong><em>awesome</em></strong>'
105 # escaping and unescaping
106 assert escape('"<>&\'') == '"<>&''
107 assert Markup("<em>Foo & Bar</em>").striptags() == "Foo & Bar"
108 assert Markup("<test>").unescape() == "<test>"
110 def test_template_data(self
):
111 env
= Environment(autoescape
=True)
112 t
= env
.from_string('{% macro say_hello(name) %}'
113 '<p>Hello {{ name }}!</p>{% endmacro %}'
114 '{{ say_hello("<blink>foo</blink>") }}')
115 escaped_out
= '<p>Hello <blink>foo</blink>!</p>'
116 assert t
.render() == escaped_out
117 assert unicode(t
.module
) == escaped_out
118 assert escape(t
.module
) == escaped_out
119 assert t
.module
.say_hello('<blink>foo</blink>') == escaped_out
120 assert escape(t
.module
.say_hello('<blink>foo</blink>')) == escaped_out
122 def test_attr_filter(self
):
123 env
= SandboxedEnvironment()
124 tmpl
= env
.from_string('{{ cls|attr("__subclasses__")() }}')
125 self
.assert_raises(SecurityError
, tmpl
.render
, cls
=int)
127 def test_binary_operator_intercepting(self
):
128 def disable_op(left
, right
):
129 raise TemplateRuntimeError('that operator so does not work')
130 for expr
, ctx
, rv
in ('1 + 2', {}, '3'), ('a + 2', {'a': 2}, '4'):
131 env
= SandboxedEnvironment()
132 env
.binop_table
['+'] = disable_op
133 t
= env
.from_string('{{ %s }}' % expr
)
134 assert t
.render(ctx
) == rv
135 env
.intercepted_binops
= frozenset(['+'])
136 t
= env
.from_string('{{ %s }}' % expr
)
139 except TemplateRuntimeError
, e
:
142 self
.fail('expected runtime error')
144 def test_unary_operator_intercepting(self
):
146 raise TemplateRuntimeError('that operator so does not work')
147 for expr
, ctx
, rv
in ('-1', {}, '-1'), ('-a', {'a': 2}, '-2'):
148 env
= SandboxedEnvironment()
149 env
.unop_table
['-'] = disable_op
150 t
= env
.from_string('{{ %s }}' % expr
)
151 assert t
.render(ctx
) == rv
152 env
.intercepted_unops
= frozenset(['-'])
153 t
= env
.from_string('{{ %s }}' % expr
)
156 except TemplateRuntimeError
, e
:
159 self
.fail('expected runtime error')
163 suite
= unittest
.TestSuite()
164 suite
.addTest(unittest
.makeSuite(SandboxTestCase
))