allow history to work in webkit browsers
[gae-samples.git] / python27 / guestbook / jinja2 / testsuite / security.py
blob4518eac659fc33b2284f16e76c4a67b47572fcae
1 # -*- coding: utf-8 -*-
2 """
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.
10 """
11 import unittest
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, \
20 TemplateRuntimeError
23 class PrivateStuff(object):
25 def bar(self):
26 return 23
28 @unsafe
29 def foo(self):
30 return 42
32 def __repr__(self):
33 return 'PrivateStuff'
36 class PublicStuff(object):
37 bar = lambda self: 23
38 _foo = lambda self: 42
40 def __repr__(self):
41 return 'PublicStuff'
44 class SandboxTestCase(JinjaTestCase):
46 def test_unsafe(self):
47 env = SandboxedEnvironment()
48 self.assert_raises(SecurityError, env.from_string("{{ foo.foo() }}").render,
49 foo=PrivateStuff())
50 self.assert_equal(env.from_string("{{ foo.bar() }}").render(foo=PrivateStuff()), '23')
52 self.assert_raises(SecurityError, env.from_string("{{ foo._foo() }}").render,
53 foo=PublicStuff())
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>&lt;bad user&gt;</em>'
84 assert Markup('<em>%(username)s</em>') % {
85 'username': '<bad user>'
86 } == '<em>&lt;bad user&gt;</em>'
88 # an escaped object is markup too
89 assert type(Markup('foo') + 'bar') is Markup
91 # and it implements __html__ by returning itself
92 x = Markup("foo")
93 assert x.__html__() is x
95 # it also knows how to treat __html__ objects
96 class Foo(object):
97 def __html__(self):
98 return '<em>awesome</em>'
99 def __unicode__(self):
100 return 'awesome'
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('"<>&\'') == '&#34;&lt;&gt;&amp;&#39;'
107 assert Markup("<em>Foo &amp; Bar</em>").striptags() == "Foo & Bar"
108 assert Markup("&lt;test&gt;").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 &lt;blink&gt;foo&lt;/blink&gt;!</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)
137 try:
138 t.render(ctx)
139 except TemplateRuntimeError, e:
140 pass
141 else:
142 self.fail('expected runtime error')
144 def test_unary_operator_intercepting(self):
145 def disable_op(arg):
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)
154 try:
155 t.render(ctx)
156 except TemplateRuntimeError, e:
157 pass
158 else:
159 self.fail('expected runtime error')
162 def suite():
163 suite = unittest.TestSuite()
164 suite.addTest(unittest.makeSuite(SandboxTestCase))
165 return suite