1# -*- coding: utf-8 -*-
2"""
3    jinja2.sandbox
4    ~~~~~~~~~~~~~~
5
6    Adds a sandbox layer to Jinja as it was the default behavior in the old
7    Jinja 1 releases.  This sandbox is slightly different from Jinja 1 as the
8    default behavior is easier to use.
9
10    The behavior can be changed by subclassing the environment.
11
12    :copyright: (c) 2010 by the Jinja Team.
13    :license: BSD.
14"""
15import operator
16from jinja2.runtime import Undefined
17from jinja2.environment import Environment
18from jinja2.exceptions import SecurityError
19from jinja2.utils import FunctionType, MethodType, TracebackType, CodeType, \
20     FrameType, GeneratorType
21
22
23#: maximum number of items a range may produce
24MAX_RANGE = 100000
25
26#: attributes of function objects that are considered unsafe.
27UNSAFE_FUNCTION_ATTRIBUTES = set(['func_closure', 'func_code', 'func_dict',
28                                  'func_defaults', 'func_globals'])
29
30#: unsafe method attributes.  function attributes are unsafe for methods too
31UNSAFE_METHOD_ATTRIBUTES = set(['im_class', 'im_func', 'im_self'])
32
33
34import warnings
35
36# make sure we don't warn in python 2.6 about stuff we don't care about
37warnings.filterwarnings('ignore', 'the sets module', DeprecationWarning,
38                        module='jinja2.sandbox')
39
40from collections import deque
41
42_mutable_set_types = (set,)
43_mutable_mapping_types = (dict,)
44_mutable_sequence_types = (list,)
45
46
47# on python 2.x we can register the user collection types
48try:
49    from UserDict import UserDict, DictMixin
50    from UserList import UserList
51    _mutable_mapping_types += (UserDict, DictMixin)
52    _mutable_set_types += (UserList,)
53except ImportError:
54    pass
55
56# if sets is still available, register the mutable set from there as well
57try:
58    from sets import Set
59    _mutable_set_types += (Set,)
60except ImportError:
61    pass
62
63#: register Python 2.6 abstract base classes
64try:
65    from collections import MutableSet, MutableMapping, MutableSequence
66    _mutable_set_types += (MutableSet,)
67    _mutable_mapping_types += (MutableMapping,)
68    _mutable_sequence_types += (MutableSequence,)
69except ImportError:
70    pass
71
72_mutable_spec = (
73    (_mutable_set_types, frozenset([
74        'add', 'clear', 'difference_update', 'discard', 'pop', 'remove',
75        'symmetric_difference_update', 'update'
76    ])),
77    (_mutable_mapping_types, frozenset([
78        'clear', 'pop', 'popitem', 'setdefault', 'update'
79    ])),
80    (_mutable_sequence_types, frozenset([
81        'append', 'reverse', 'insert', 'sort', 'extend', 'remove'
82    ])),
83    (deque, frozenset([
84        'append', 'appendleft', 'clear', 'extend', 'extendleft', 'pop',
85        'popleft', 'remove', 'rotate'
86    ]))
87)
88
89
90def safe_range(*args):
91    """A range that can't generate ranges with a length of more than
92    MAX_RANGE items.
93    """
94    rng = xrange(*args)
95    if len(rng) > MAX_RANGE:
96        raise OverflowError('range too big, maximum size for range is %d' %
97                            MAX_RANGE)
98    return rng
99
100
101def unsafe(f):
102    """
103    Mark a function or method as unsafe::
104
105        @unsafe
106        def delete(self):
107            pass
108    """
109    f.unsafe_callable = True
110    return f
111
112
113def is_internal_attribute(obj, attr):
114    """Test if the attribute given is an internal python attribute.  For
115    example this function returns `True` for the `func_code` attribute of
116    python objects.  This is useful if the environment method
117    :meth:`~SandboxedEnvironment.is_safe_attribute` is overriden.
118
119    >>> from jinja2.sandbox import is_internal_attribute
120    >>> is_internal_attribute(lambda: None, "func_code")
121    True
122    >>> is_internal_attribute((lambda x:x).func_code, 'co_code')
123    True
124    >>> is_internal_attribute(str, "upper")
125    False
126    """
127    if isinstance(obj, FunctionType):
128        if attr in UNSAFE_FUNCTION_ATTRIBUTES:
129            return True
130    elif isinstance(obj, MethodType):
131        if attr in UNSAFE_FUNCTION_ATTRIBUTES or \
132           attr in UNSAFE_METHOD_ATTRIBUTES:
133            return True
134    elif isinstance(obj, type):
135        if attr == 'mro':
136            return True
137    elif isinstance(obj, (CodeType, TracebackType, FrameType)):
138        return True
139    elif isinstance(obj, GeneratorType):
140        if attr == 'gi_frame':
141            return True
142    return attr.startswith('__')
143
144
145def modifies_known_mutable(obj, attr):
146    """This function checks if an attribute on a builtin mutable object
147    (list, dict, set or deque) would modify it if called.  It also supports
148    the "user"-versions of the objects (`sets.Set`, `UserDict.*` etc.) and
149    with Python 2.6 onwards the abstract base classes `MutableSet`,
150    `MutableMapping`, and `MutableSequence`.
151
152    >>> modifies_known_mutable({}, "clear")
153    True
154    >>> modifies_known_mutable({}, "keys")
155    False
156    >>> modifies_known_mutable([], "append")
157    True
158    >>> modifies_known_mutable([], "index")
159    False
160
161    If called with an unsupported object (such as unicode) `False` is
162    returned.
163
164    >>> modifies_known_mutable("foo", "upper")
165    False
166    """
167    for typespec, unsafe in _mutable_spec:
168        if isinstance(obj, typespec):
169            return attr in unsafe
170    return False
171
172
173class SandboxedEnvironment(Environment):
174    """The sandboxed environment.  It works like the regular environment but
175    tells the compiler to generate sandboxed code.  Additionally subclasses of
176    this environment may override the methods that tell the runtime what
177    attributes or functions are safe to access.
178
179    If the template tries to access insecure code a :exc:`SecurityError` is
180    raised.  However also other exceptions may occour during the rendering so
181    the caller has to ensure that all exceptions are catched.
182    """
183    sandboxed = True
184
185    def __init__(self, *args, **kwargs):
186        Environment.__init__(self, *args, **kwargs)
187        self.globals['range'] = safe_range
188
189    def is_safe_attribute(self, obj, attr, value):
190        """The sandboxed environment will call this method to check if the
191        attribute of an object is safe to access.  Per default all attributes
192        starting with an underscore are considered private as well as the
193        special attributes of internal python objects as returned by the
194        :func:`is_internal_attribute` function.
195        """
196        return not (attr.startswith('_') or is_internal_attribute(obj, attr))
197
198    def is_safe_callable(self, obj):
199        """Check if an object is safely callable.  Per default a function is
200        considered safe unless the `unsafe_callable` attribute exists and is
201        True.  Override this method to alter the behavior, but this won't
202        affect the `unsafe` decorator from this module.
203        """
204        return not (getattr(obj, 'unsafe_callable', False) or \
205                    getattr(obj, 'alters_data', False))
206
207    def getitem(self, obj, argument):
208        """Subscribe an object from sandboxed code."""
209        try:
210            return obj[argument]
211        except (TypeError, LookupError):
212            if isinstance(argument, basestring):
213                try:
214                    attr = str(argument)
215                except:
216                    pass
217                else:
218                    try:
219                        value = getattr(obj, attr)
220                    except AttributeError:
221                        pass
222                    else:
223                        if self.is_safe_attribute(obj, argument, value):
224                            return value
225                        return self.unsafe_undefined(obj, argument)
226        return self.undefined(obj=obj, name=argument)
227
228    def getattr(self, obj, attribute):
229        """Subscribe an object from sandboxed code and prefer the
230        attribute.  The attribute passed *must* be a bytestring.
231        """
232        try:
233            value = getattr(obj, attribute)
234        except AttributeError:
235            try:
236                return obj[attribute]
237            except (TypeError, LookupError):
238                pass
239        else:
240            if self.is_safe_attribute(obj, attribute, value):
241                return value
242            return self.unsafe_undefined(obj, attribute)
243        return self.undefined(obj=obj, name=attribute)
244
245    def unsafe_undefined(self, obj, attribute):
246        """Return an undefined object for unsafe attributes."""
247        return self.undefined('access to attribute %r of %r '
248                              'object is unsafe.' % (
249            attribute,
250            obj.__class__.__name__
251        ), name=attribute, obj=obj, exc=SecurityError)
252
253    def call(__self, __context, __obj, *args, **kwargs):
254        """Call an object from sandboxed code."""
255        # the double prefixes are to avoid double keyword argument
256        # errors when proxying the call.
257        if not __self.is_safe_callable(__obj):
258            raise SecurityError('%r is not safely callable' % (__obj,))
259        return __context.call(__obj, *args, **kwargs)
260
261
262class ImmutableSandboxedEnvironment(SandboxedEnvironment):
263    """Works exactly like the regular `SandboxedEnvironment` but does not
264    permit modifications on the builtin mutable objects `list`, `set`, and
265    `dict` by using the :func:`modifies_known_mutable` function.
266    """
267
268    def is_safe_attribute(self, obj, attr, value):
269        if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value):
270            return False
271        return not modifies_known_mutable(obj, attr)
272