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+')
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,
: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
'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."""
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):
'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):
: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):
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():
+ env = SandboxedEnvironment()
+ tmpl = env.from_string('{{ 42|attr("__class__")|attr("__subclasses__")() }}')
+ raises(SecurityError, tmpl.render)
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)]"