| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166 |
- # -*- coding: utf-8 -*-
- """
- jinja2.testsuite.security
- ~~~~~~~~~~~~~~~~~~~~~~~~~
- Checks the sandbox and other security features.
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
- """
- import unittest
- from jinja2.testsuite import JinjaTestCase
- from jinja2 import Environment
- from jinja2.sandbox import SandboxedEnvironment, \
- ImmutableSandboxedEnvironment, unsafe
- from jinja2 import Markup, escape
- from jinja2.exceptions import SecurityError, TemplateSyntaxError, \
- TemplateRuntimeError
- from jinja2._compat import text_type
- class PrivateStuff(object):
- def bar(self):
- return 23
- @unsafe
- def foo(self):
- return 42
- def __repr__(self):
- return 'PrivateStuff'
- class PublicStuff(object):
- bar = lambda self: 23
- _foo = lambda self: 42
- def __repr__(self):
- return 'PublicStuff'
- class SandboxTestCase(JinjaTestCase):
- def test_unsafe(self):
- env = SandboxedEnvironment()
- self.assert_raises(SecurityError, env.from_string("{{ foo.foo() }}").render,
- foo=PrivateStuff())
- self.assert_equal(env.from_string("{{ foo.bar() }}").render(foo=PrivateStuff()), '23')
- self.assert_raises(SecurityError, env.from_string("{{ foo._foo() }}").render,
- foo=PublicStuff())
- self.assert_equal(env.from_string("{{ foo.bar() }}").render(foo=PublicStuff()), '23')
- self.assert_equal(env.from_string("{{ foo.__class__ }}").render(foo=42), '')
- self.assert_equal(env.from_string("{{ foo.func_code }}").render(foo=lambda:None), '')
- # security error comes from __class__ already.
- self.assert_raises(SecurityError, env.from_string(
- "{{ foo.__class__.__subclasses__() }}").render, foo=42)
- def test_immutable_environment(self):
- env = ImmutableSandboxedEnvironment()
- self.assert_raises(SecurityError, env.from_string(
- '{{ [].append(23) }}').render)
- self.assert_raises(SecurityError, env.from_string(
- '{{ {1:2}.clear() }}').render)
- def test_restricted(self):
- env = SandboxedEnvironment()
- self.assert_raises(TemplateSyntaxError, env.from_string,
- "{% for item.attribute in seq %}...{% endfor %}")
- self.assert_raises(TemplateSyntaxError, env.from_string,
- "{% for foo, bar.baz in seq %}...{% endfor %}")
- def test_markup_operations(self):
- # adding two strings should escape the unsafe one
- unsafe = '<script type="application/x-some-script">alert("foo");</script>'
- safe = Markup('<em>username</em>')
- assert unsafe + safe == text_type(escape(unsafe)) + text_type(safe)
- # string interpolations are safe to use too
- assert Markup('<em>%s</em>') % '<bad user>' == \
- '<em><bad user></em>'
- assert Markup('<em>%(username)s</em>') % {
- 'username': '<bad user>'
- } == '<em><bad user></em>'
- # an escaped object is markup too
- assert type(Markup('foo') + 'bar') is Markup
- # and it implements __html__ by returning itself
- x = Markup("foo")
- assert x.__html__() is x
- # it also knows how to treat __html__ objects
- class Foo(object):
- def __html__(self):
- return '<em>awesome</em>'
- def __unicode__(self):
- return 'awesome'
- assert Markup(Foo()) == '<em>awesome</em>'
- assert Markup('<strong>%s</strong>') % Foo() == \
- '<strong><em>awesome</em></strong>'
- # escaping and unescaping
- assert escape('"<>&\'') == '"<>&''
- assert Markup("<em>Foo & Bar</em>").striptags() == "Foo & Bar"
- assert Markup("<test>").unescape() == "<test>"
- def test_template_data(self):
- env = Environment(autoescape=True)
- t = env.from_string('{% macro say_hello(name) %}'
- '<p>Hello {{ name }}!</p>{% endmacro %}'
- '{{ say_hello("<blink>foo</blink>") }}')
- escaped_out = '<p>Hello <blink>foo</blink>!</p>'
- assert t.render() == escaped_out
- assert text_type(t.module) == escaped_out
- assert escape(t.module) == escaped_out
- assert t.module.say_hello('<blink>foo</blink>') == escaped_out
- assert escape(t.module.say_hello('<blink>foo</blink>')) == escaped_out
- def test_attr_filter(self):
- env = SandboxedEnvironment()
- tmpl = env.from_string('{{ cls|attr("__subclasses__")() }}')
- self.assert_raises(SecurityError, tmpl.render, cls=int)
- def test_binary_operator_intercepting(self):
- def disable_op(left, right):
- raise TemplateRuntimeError('that operator so does not work')
- for expr, ctx, rv in ('1 + 2', {}, '3'), ('a + 2', {'a': 2}, '4'):
- env = SandboxedEnvironment()
- env.binop_table['+'] = disable_op
- t = env.from_string('{{ %s }}' % expr)
- assert t.render(ctx) == rv
- env.intercepted_binops = frozenset(['+'])
- t = env.from_string('{{ %s }}' % expr)
- try:
- t.render(ctx)
- except TemplateRuntimeError as e:
- pass
- else:
- self.fail('expected runtime error')
- def test_unary_operator_intercepting(self):
- def disable_op(arg):
- raise TemplateRuntimeError('that operator so does not work')
- for expr, ctx, rv in ('-1', {}, '-1'), ('-a', {'a': 2}, '-2'):
- env = SandboxedEnvironment()
- env.unop_table['-'] = disable_op
- t = env.from_string('{{ %s }}' % expr)
- assert t.render(ctx) == rv
- env.intercepted_unops = frozenset(['-'])
- t = env.from_string('{{ %s }}' % expr)
- try:
- t.render(ctx)
- except TemplateRuntimeError as e:
- pass
- else:
- self.fail('expected runtime error')
- def suite():
- suite = unittest.TestSuite()
- suite.addTest(unittest.makeSuite(SandboxTestCase))
- return suite
|