From d71fff001dcf0d2a689c7db6003fc0e1cc0846c6 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 26 May 2008 23:57:07 +0200 Subject: [PATCH] improved sandbox and updated setup.py --HG-- branch : trunk --- docs/sandbox.rst | 2 +- jinja2/_speedups.c | 2 +- jinja2/runtime.py | 4 -- jinja2/sandbox.py | 101 +++++++++++++++++++++++++++------------------ jinja2/utils.py | 35 +++++++++------- setup.py | 2 +- 6 files changed, 85 insertions(+), 61 deletions(-) diff --git a/docs/sandbox.rst b/docs/sandbox.rst index 2b65f9c..f6ec78c 100644 --- a/docs/sandbox.rst +++ b/docs/sandbox.rst @@ -28,4 +28,4 @@ SecurityError: access to attribute 'func_code' of 'function' object is unsafe. .. autofunction:: is_internal_attribute -.. autofunction:: modifies_builtin_mutable +.. autofunction:: modifies_known_mutable diff --git a/jinja2/_speedups.c b/jinja2/_speedups.c index 112e600..f691c78 100644 --- a/jinja2/_speedups.c +++ b/jinja2/_speedups.c @@ -157,7 +157,7 @@ soft_unicode(PyObject *self, PyObject *s) } -static PyObject * +static PyObject* tb_set_next(PyObject *self, PyObject *args) { PyTracebackObject *tb, *old; diff --git a/jinja2/runtime.py b/jinja2/runtime.py index f5639f8..babc3c9 100644 --- a/jinja2/runtime.py +++ b/jinja2/runtime.py @@ -117,10 +117,6 @@ class Context(object): return dict(self.parent, **self.vars) def call(__self, __obj, *args, **kwargs): - """Called by the template code to inject the current context - or environment as first arguments. Then forwards the call to - the object with the arguments and keyword arguments. - """ if __debug__: __traceback_hide__ = True if isinstance(__obj, _context_function_types): diff --git a/jinja2/sandbox.py b/jinja2/sandbox.py index d44521e..7135ff0 100644 --- a/jinja2/sandbox.py +++ b/jinja2/sandbox.py @@ -13,9 +13,6 @@ :license: BSD. """ import operator -from UserDict import UserDict, DictMixin -from UserList import UserList -from sets import Set, ImmutableSet from types import FunctionType, MethodType, TracebackType, CodeType, \ FrameType, GeneratorType from jinja2.runtime import Undefined @@ -33,19 +30,40 @@ UNSAFE_FUNCTION_ATTRIBUTES = set(['func_closure', 'func_code', 'func_dict', #: unsafe method attributes. function attributes are unsafe for methods too UNSAFE_METHOD_ATTRIBUTES = set(['im_class', 'im_func', 'im_self']) -SET_TYPES = (ImmutableSet, Set, set) -MODIFYING_SET_ATTRIBUTES = set([ - 'add', 'clear', 'difference_update', 'discard', 'pop', 'remove', - 'symmetric_difference_update', 'update' -]) - -DICT_TYPES = (UserDict, DictMixin, dict) -MODIFYING_DICT_ATTRIBUTES = set(['clear', 'pop', 'popitem', 'setdefault', - 'update']) -LIST_TYPES = (UserList, list) -MODIFYING_LIST_ATTRIBUTES = set(['append', 'reverse', 'insert', 'sort', - 'extend', 'remove']) +from collections import deque +from sets import Set, ImmutableSet +from UserDict import UserDict, DictMixin +from UserList import UserList +_mutable_set_types = (ImmutableSet, Set, set) +_mutable_mapping_types = (UserDict, DictMixin, dict) +_mutable_sequence_types = (UserList, list) + +#: register Python 2.6 abstract base classes +try: + from collections import MutableSet, MutableMapping, MutableSequence + _mutable_set_types += (MutableSet,) + _mutable_mapping_types += (MutableMapping,) + _mutable_sequence_types += (MutableSequence,) +except ImportError: + pass + +_mutable_spec = ( + (_mutable_set_types, frozenset([ + 'add', 'clear', 'difference_update', 'discard', 'pop', 'remove', + 'symmetric_difference_update', 'update' + ])), + (_mutable_mapping_types, frozenset([ + 'clear', 'pop', 'popitem', 'setdefault', 'update' + ])), + (_mutable_sequence_types, frozenset([ + 'append', 'reverse', 'insert', 'sort', 'extend', 'remove' + ])), + (deque, frozenset([ + 'append', 'appendleft', 'clear', 'extend', 'extendleft', 'pop', + 'popleft', 'remove', 'rotate' + ])) +) def safe_range(*args): @@ -86,45 +104,48 @@ def is_internal_attribute(obj, attr): False """ if isinstance(obj, FunctionType): - return attr in UNSAFE_FUNCTION_ATTRIBUTES - if isinstance(obj, MethodType): - return attr in UNSAFE_FUNCTION_ATTRIBUTES or \ - attr in UNSAFE_METHOD_ATTRIBUTES - if isinstance(obj, type): - return attr == 'mro' - if isinstance(obj, (CodeType, TracebackType, FrameType)): + if attr in UNSAFE_FUNCTION_ATTRIBUTES: + return True + elif isinstance(obj, MethodType): + if attr in UNSAFE_FUNCTION_ATTRIBUTES or \ + attr in UNSAFE_METHOD_ATTRIBUTES: + return True + elif isinstance(obj, type): + if attr == 'mro': + return True + elif isinstance(obj, (CodeType, TracebackType, FrameType)): return True - if isinstance(obj, GeneratorType): - return attr == 'gi_frame' + elif isinstance(obj, GeneratorType): + if attr == 'gi_frame': + return True return attr.startswith('__') -def modifies_builtin_mutable(obj, attr): +def modifies_known_mutable(obj, attr): """This function checks if an attribute on a builtin mutable object - (list, dict or set) would modify it if called. It also supports - the "user"-versions of the objects (`sets.Set`, `UserDict.*` etc.) + (list, dict, set or deque) would modify it if called. It also supports + the "user"-versions of the objects (`sets.Set`, `UserDict.*` etc.) and + with Python 2.6 onwards the abstract base classes `MutableSet`, + `MutableMapping`, and `MutableSequence`. - >>> modifies_builtin_mutable({}, "clear") + >>> modifies_known_mutable({}, "clear") True - >>> modifies_builtin_mutable({}, "keys") + >>> modifies_known_mutable({}, "keys") False - >>> modifies_builtin_mutable([], "append") + >>> modifies_known_mutable([], "append") True - >>> modifies_builtin_mutable([], "index") + >>> modifies_known_mutable([], "index") False If called with an unsupported object (such as unicode) `False` is returned. - >>> modifies_builtin_mutable("foo", "upper") + >>> modifies_known_mutable("foo", "upper") False """ - if isinstance(obj, LIST_TYPES): - return attr in MODIFYING_LIST_ATTRIBUTES - elif isinstance(obj, DICT_TYPES): - return attr in MODIFYING_DICT_ATTRIBUTES - elif isinstance(obj, SET_TYPES): - return attr in MODIFYING_SET_ATTRIBUTES + for typespec, unsafe in _mutable_spec: + if isinstance(obj, typespec): + return attr in unsafe return False @@ -199,10 +220,10 @@ class SandboxedEnvironment(Environment): class ImmutableSandboxedEnvironment(SandboxedEnvironment): """Works exactly like the regular `SandboxedEnvironment` but does not permit modifications on the builtin mutable objects `list`, `set`, and - `dict` by using the :func:`modifies_builtin_mutable` function. + `dict` by using the :func:`modifies_known_mutable` function. """ def is_safe_attribute(self, obj, attr, value): if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value): return False - return not modifies_builtin_mutable(obj, attr) + return not modifies_known_mutable(obj, attr) diff --git a/jinja2/utils.py b/jinja2/utils.py index 258961f..e064a25 100644 --- a/jinja2/utils.py +++ b/jinja2/utils.py @@ -253,9 +253,9 @@ class Markup(unicode): return NotImplemented def __mul__(self, num): - if not isinstance(num, (int, long)): - return NotImplemented - return self.__class__(unicode.__mul__(self, num)) + if isinstance(num, (int, long)): + return self.__class__(unicode.__mul__(self, num)) + return NotImplemented __rmul__ = __mul__ def __mod__(self, arg): @@ -319,17 +319,13 @@ class Markup(unicode): def make_wrapper(name): orig = getattr(unicode, name) def func(self, *args, **kwargs): - args = list(args) - for idx, arg in enumerate(args): - if hasattr(arg, '__html__') or isinstance(arg, basestring): - args[idx] = escape(arg) - for name, arg in kwargs.iteritems(): - if hasattr(arg, '__html__') or isinstance(arg, basestring): - kwargs[name] = escape(arg) + args = _escape_argspec(list(args), enumerate(args)) + _escape_argspec(kwargs, kwargs.iteritems()) return self.__class__(orig(self, *args, **kwargs)) func.__name__ = orig.__name__ func.__doc__ = orig.__doc__ return func + for method in '__getitem__', '__getslice__', 'capitalize', \ 'title', 'lower', 'upper', 'replace', 'ljust', \ 'rjust', 'lstrip', 'rstrip', 'center', 'strip', \ @@ -338,13 +334,24 @@ class Markup(unicode): # new in python 2.5 if hasattr(unicode, 'partition'): - locals().update( - partition=make_wrapper('partition'), - rpartition=make_wrapper('rpartition') - ) + partition = make_wrapper('partition'), + rpartition = make_wrapper('rpartition') + + # new in python 2.6 + if hasattr(unicode, 'format'): + format = make_wrapper('format') + del method, make_wrapper +def _escape_argspec(obj, iterable): + """Helper for various string-wrapped functions.""" + for key, value in iterable: + if hasattr(value, '__html__') or isinstance(value, basestring): + obj[key] = escape(value) + return obj + + class _MarkupEscapeHelper(object): """Helper for Markup.__mod__""" diff --git a/setup.py b/setup.py index fd7d3f5..8cc724c 100644 --- a/setup.py +++ b/setup.py @@ -120,7 +120,7 @@ setup( ], packages=['jinja2'], data_files=[ - ##('docs', list(list_files('docs/_build/html'))) + ('docs', list(list_files('docs/_build/html'))) ], features={ 'speedups': Feature("optional C speed-enhancements", -- 2.26.2