added `ImmutableSandboxedEnvironment`.
authorArmin Ronacher <armin.ronacher@active-4.com>
Sat, 17 May 2008 11:55:37 +0000 (13:55 +0200)
committerArmin Ronacher <armin.ronacher@active-4.com>
Sat, 17 May 2008 11:55:37 +0000 (13:55 +0200)
--HG--
branch : trunk

docs/sandbox.rst
jinja2/sandbox.py

index a919df8e51b907b4ed6b4dba3a125adf9d20c900..2b65f9c49b5ecfcc3c7d6b7f73c1fc3af6bd03bb 100644 (file)
@@ -20,8 +20,12 @@ SecurityError: access to attribute 'func_code' of 'function' object is unsafe.
 .. autoclass:: SandboxedEnvironment([options])
     :members: is_safe_attribute, is_safe_callable
 
+.. autoclass:: ImmutableSandboxedEnvironment([options])
+
 .. autoexception:: SecurityError
 
 .. autofunction:: unsafe
 
 .. autofunction:: is_internal_attribute
+
+.. autofunction:: modifies_builtin_mutable
index 714a0e1a11364f6230f3badcc4b548ad6604c956..c558c739d387ecaf179c9efc5c9dfde1b42889e8 100644 (file)
     :copyright: Copyright 2008 by Armin Ronacher.
     :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
@@ -29,6 +33,20 @@ 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'])
+
 
 def safe_range(*args):
     """A range that can't generate ranges with a length of more than
@@ -81,6 +99,35 @@ def is_internal_attribute(obj, attr):
     return attr.startswith('__')
 
 
+def modifies_builtin_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.)
+
+    >>> modifies_builtin_mutable({}, "clear")
+    True
+    >>> modifies_builtin_mutable({}, "keys")
+    False
+    >>> modifies_builtin_mutable([], "append")
+    True
+    >>> modifies_builtin_mutable([], "index")
+    False
+
+    If called with an unsupported object (such as unicode) `False` is
+    returned.
+
+    >>> modifies_builtin_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
+    return False
+
+
 class SandboxedEnvironment(Environment):
     """The sandboxed environment.  It works like the regular environment but
     tells the compiler to generate sandboxed code.  Additionally subclasses of
@@ -150,3 +197,15 @@ class SandboxedEnvironment(Environment):
         if not __self.is_safe_callable(__obj):
             raise SecurityError('%r is not safely callable' % (__obj,))
         return __obj(*args, **kwargs)
+
+
+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.
+    """
+
+    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)