diff options
Diffstat (limited to 'module/lib/jinja2/testsuite/security.py')
-rw-r--r-- | module/lib/jinja2/testsuite/security.py | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/module/lib/jinja2/testsuite/security.py b/module/lib/jinja2/testsuite/security.py new file mode 100644 index 000000000..246d0f073 --- /dev/null +++ b/module/lib/jinja2/testsuite/security.py @@ -0,0 +1,166 @@ +# -*- 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 |