From: Armin Ronacher Date: Mon, 26 May 2008 11:35:58 +0000 (+0200) Subject: added attr filter X-Git-Tag: 2.0rc1~15 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=24b6558c3e8ea20d3f08481377970e53a80db33a;p=thirdparty%2Fjinja.git added attr filter --HG-- branch : trunk --- diff --git a/jinja2/filters.py b/jinja2/filters.py index de15b531..0a32f9ae 100644 --- a/jinja2/filters.py +++ b/jinja2/filters.py @@ -16,7 +16,7 @@ from operator import itemgetter from itertools import imap, groupby from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode from jinja2.runtime import Undefined -from jinja2.exceptions import FilterArgumentError +from jinja2.exceptions import FilterArgumentError, SecurityError _word_re = re.compile(r'\w+') @@ -620,7 +620,29 @@ def do_reverse(value): raise FilterArgumentError('argument must be iterable') +@environmentfilter +def do_attr(environment, obj, name): + """Get an attribute of an object. ``foo|attr("bar")`` works like + ``foo["bar"]`` just that always an attribute is returned. This is useful + if data structures are passed to the template that have an item that hides + an attribute with the same name. For example a dict ``{'items': []}`` + that obviously hides the item method of a dict. + """ + try: + value = getattr(obj, name) + except AttributeError: + return environment.undefined(obj=obj, name=name) + if environment.sandboxed and not \ + environment.is_safe_attribute(obj, name, value): + return environment.undefined('access to attribute %r of %r ' + 'object is unsafe.' % ( + name, obj.__class__.__name__ + ), name=name, obj=obj, exc=SecurityError) + return value + + FILTERS = { + 'attr': do_attr, 'replace': do_replace, 'upper': do_upper, 'lower': do_lower, diff --git a/jinja2/runtime.py b/jinja2/runtime.py index 7a1cf276..f5639f8d 100644 --- a/jinja2/runtime.py +++ b/jinja2/runtime.py @@ -9,6 +9,7 @@ :license: GNU GPL. """ import sys +from types import FunctionType, MethodType from itertools import chain, imap from jinja2.utils import Markup, partial, soft_unicode, escape, missing, concat from jinja2.exceptions import UndefinedError, TemplateRuntimeError @@ -19,6 +20,8 @@ __all__ = ['LoopContext', 'Context', 'TemplateReference', 'Macro', 'Markup', 'TemplateRuntimeError', 'missing', 'concat', 'escape', 'markup_join', 'unicode_join'] +_context_function_types = (FunctionType, MethodType) + def markup_join(seq): """Concatenation that escapes if necessary and converts to unicode.""" @@ -118,10 +121,13 @@ class Context(object): or environment as first arguments. Then forwards the call to the object with the arguments and keyword arguments. """ - if getattr(__obj, 'contextfunction', 0): - args = (__self,) + args - elif getattr(__obj, 'environmentfunction', 0): - args = (__self.environment,) + args + if __debug__: + __traceback_hide__ = True + if isinstance(__obj, _context_function_types): + if getattr(__obj, 'contextfunction', 0): + args = (__self,) + args + elif getattr(__obj, 'environmentfunction', 0): + args = (__self.environment,) + args return __obj(*args, **kwargs) def _all(meth): diff --git a/jinja2/sandbox.py b/jinja2/sandbox.py index ce3369b3..d44521ed 100644 --- a/jinja2/sandbox.py +++ b/jinja2/sandbox.py @@ -184,7 +184,7 @@ class SandboxedEnvironment(Environment): 'object is unsafe.' % ( argument, obj.__class__.__name__ - ), name=argument, exc=SecurityError) + ), name=argument, obj=obj, exc=SecurityError) return self.undefined(obj=obj, name=argument) def call(__self, __context, __obj, *args, **kwargs): diff --git a/tests/test_security.py b/tests/test_security.py index 68b15157..7c812c09 100644 --- a/tests/test_security.py +++ b/tests/test_security.py @@ -6,10 +6,12 @@ :copyright: 2007 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ +from py.test import raises from jinja2 import Environment from jinja2.sandbox import SandboxedEnvironment, \ ImmutableSandboxedEnvironment, unsafe from jinja2 import Markup, escape +from jinja2.exceptions import SecurityError class PrivateStuff(object): @@ -132,3 +134,9 @@ def test_template_data(): assert escape(t.module) == escaped_out assert t.module.say_hello('foo') == escaped_out assert escape(t.module.say_hello('foo')) == escaped_out + + +def test_attr_filter(): + env = SandboxedEnvironment() + tmpl = env.from_string('{{ 42|attr("__class__")|attr("__subclasses__")() }}') + raises(SecurityError, tmpl.render) diff --git a/tests/test_various.py b/tests/test_various.py index 7a3882e6..cbde4db4 100644 --- a/tests/test_various.py +++ b/tests/test_various.py @@ -68,3 +68,5 @@ def test_item_before_attribute(): tmpl = env.from_string('{{ foo.items() }}') assert tmpl.render(foo={'items': lambda: 42}) == '42' assert tmpl.render(foo={}) == '[]' + tmpl = env.from_string('{{ foo|attr("items")() }}') + assert tmpl.render(foo={'items': None}) == "[('items', None)]"