blob: ce3369b31d7d5da1a7c6fe7abc63152020c84653 [file] [log] [blame]
Armin Ronacherc63243e2008-04-14 22:53:58 +02001# -*- 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: Copyright 2008 by Armin Ronacher.
13 :license: BSD.
14"""
Armin Ronacher522cad62008-05-17 13:55:37 +020015import operator
16from UserDict import UserDict, DictMixin
17from UserList import UserList
18from sets import Set, ImmutableSet
Armin Ronacherb5f522c2008-05-04 18:25:02 +020019from types import FunctionType, MethodType, TracebackType, CodeType, \
20 FrameType, GeneratorType
Armin Ronacherc63243e2008-04-14 22:53:58 +020021from jinja2.runtime import Undefined
22from jinja2.environment import Environment
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +020023from jinja2.exceptions import SecurityError
Armin Ronacherc63243e2008-04-14 22:53:58 +020024
25
26#: maximum number of items a range may produce
27MAX_RANGE = 100000
28
Armin Ronacher76c280b2008-05-04 12:31:48 +020029#: attributes of function objects that are considered unsafe.
30UNSAFE_FUNCTION_ATTRIBUTES = set(['func_closure', 'func_code', 'func_dict',
31 'func_defaults', 'func_globals'])
32
33#: unsafe method attributes. function attributes are unsafe for methods too
34UNSAFE_METHOD_ATTRIBUTES = set(['im_class', 'im_func', 'im_self'])
35
Armin Ronacher522cad62008-05-17 13:55:37 +020036SET_TYPES = (ImmutableSet, Set, set)
37MODIFYING_SET_ATTRIBUTES = set([
38 'add', 'clear', 'difference_update', 'discard', 'pop', 'remove',
39 'symmetric_difference_update', 'update'
40])
41
42DICT_TYPES = (UserDict, DictMixin, dict)
43MODIFYING_DICT_ATTRIBUTES = set(['clear', 'pop', 'popitem', 'setdefault',
44 'update'])
45
46LIST_TYPES = (UserList, list)
47MODIFYING_LIST_ATTRIBUTES = set(['append', 'reverse', 'insert', 'sort',
48 'extend', 'remove'])
49
Armin Ronacherc63243e2008-04-14 22:53:58 +020050
51def safe_range(*args):
52 """A range that can't generate ranges with a length of more than
Armin Ronacher7ceced52008-05-03 10:15:31 +020053 MAX_RANGE items.
54 """
Armin Ronacherc63243e2008-04-14 22:53:58 +020055 rng = xrange(*args)
56 if len(rng) > MAX_RANGE:
Armin Ronacher76c280b2008-05-04 12:31:48 +020057 raise OverflowError('range too big, maximum size for range is %d' %
58 MAX_RANGE)
Armin Ronacherc63243e2008-04-14 22:53:58 +020059 return rng
60
61
62def unsafe(f):
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +020063 """
64 Mark a function or method as unsafe::
65
66 @unsafe
67 def delete(self):
68 pass
69 """
Armin Ronacherc63243e2008-04-14 22:53:58 +020070 f.unsafe_callable = True
71 return f
72
73
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +020074def is_internal_attribute(obj, attr):
75 """Test if the attribute given is an internal python attribute. For
76 example this function returns `True` for the `func_code` attribute of
77 python objects. This is useful if the environment method
78 :meth:`~SandboxedEnvironment.is_safe_attribute` is overriden.
79
80 >>> from jinja2.sandbox import is_internal_attribute
81 >>> is_internal_attribute(lambda: None, "func_code")
82 True
83 >>> is_internal_attribute((lambda x:x).func_code, 'co_code')
84 True
85 >>> is_internal_attribute(str, "upper")
86 False
87 """
88 if isinstance(obj, FunctionType):
89 return attr in UNSAFE_FUNCTION_ATTRIBUTES
90 if isinstance(obj, MethodType):
91 return attr in UNSAFE_FUNCTION_ATTRIBUTES or \
92 attr in UNSAFE_METHOD_ATTRIBUTES
93 if isinstance(obj, type):
94 return attr == 'mro'
95 if isinstance(obj, (CodeType, TracebackType, FrameType)):
96 return True
97 if isinstance(obj, GeneratorType):
98 return attr == 'gi_frame'
99 return attr.startswith('__')
100
101
Armin Ronacher522cad62008-05-17 13:55:37 +0200102def modifies_builtin_mutable(obj, attr):
103 """This function checks if an attribute on a builtin mutable object
104 (list, dict or set) would modify it if called. It also supports
105 the "user"-versions of the objects (`sets.Set`, `UserDict.*` etc.)
106
107 >>> modifies_builtin_mutable({}, "clear")
108 True
109 >>> modifies_builtin_mutable({}, "keys")
110 False
111 >>> modifies_builtin_mutable([], "append")
112 True
113 >>> modifies_builtin_mutable([], "index")
114 False
115
116 If called with an unsupported object (such as unicode) `False` is
117 returned.
118
119 >>> modifies_builtin_mutable("foo", "upper")
120 False
121 """
122 if isinstance(obj, LIST_TYPES):
123 return attr in MODIFYING_LIST_ATTRIBUTES
124 elif isinstance(obj, DICT_TYPES):
125 return attr in MODIFYING_DICT_ATTRIBUTES
126 elif isinstance(obj, SET_TYPES):
127 return attr in MODIFYING_SET_ATTRIBUTES
128 return False
129
130
Armin Ronacherc63243e2008-04-14 22:53:58 +0200131class SandboxedEnvironment(Environment):
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200132 """The sandboxed environment. It works like the regular environment but
133 tells the compiler to generate sandboxed code. Additionally subclasses of
134 this environment may override the methods that tell the runtime what
135 attributes or functions are safe to access.
136
137 If the template tries to access insecure code a :exc:`SecurityError` is
138 raised. However also other exceptions may occour during the rendering so
139 the caller has to ensure that all exceptions are catched.
140 """
Armin Ronacherc63243e2008-04-14 22:53:58 +0200141 sandboxed = True
142
143 def __init__(self, *args, **kwargs):
144 Environment.__init__(self, *args, **kwargs)
145 self.globals['range'] = safe_range
146
Armin Ronacher9a822052008-04-17 18:44:07 +0200147 def is_safe_attribute(self, obj, attr, value):
Armin Ronacherc63243e2008-04-14 22:53:58 +0200148 """The sandboxed environment will call this method to check if the
149 attribute of an object is safe to access. Per default all attributes
150 starting with an underscore are considered private as well as the
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200151 special attributes of internal python objects as returned by the
152 :func:`is_internal_attribute` function.
Armin Ronacherc63243e2008-04-14 22:53:58 +0200153 """
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200154 return not (attr.startswith('_') or is_internal_attribute(obj, attr))
Armin Ronacherc63243e2008-04-14 22:53:58 +0200155
156 def is_safe_callable(self, obj):
157 """Check if an object is safely callable. Per default a function is
158 considered safe unless the `unsafe_callable` attribute exists and is
Armin Ronacher7ceced52008-05-03 10:15:31 +0200159 True. Override this method to alter the behavior, but this won't
Armin Ronacherc63243e2008-04-14 22:53:58 +0200160 affect the `unsafe` decorator from this module.
161 """
Armin Ronacher7ceced52008-05-03 10:15:31 +0200162 return not (getattr(obj, 'unsafe_callable', False) or \
163 getattr(obj, 'alters_data', False))
Armin Ronacherc63243e2008-04-14 22:53:58 +0200164
Armin Ronacher9a822052008-04-17 18:44:07 +0200165 def subscribe(self, obj, argument):
Armin Ronacherc63243e2008-04-14 22:53:58 +0200166 """Subscribe an object from sandboxed code."""
Armin Ronacher9a822052008-04-17 18:44:07 +0200167 try:
168 return obj[argument]
Armin Ronacherc63243e2008-04-14 22:53:58 +0200169 except (TypeError, LookupError):
Armin Ronacherf15f5f72008-05-26 12:21:45 +0200170 if isinstance(argument, basestring):
171 try:
172 attr = str(argument)
173 except:
174 pass
175 else:
176 try:
177 value = getattr(obj, attr)
178 except AttributeError:
179 pass
180 else:
181 if self.is_safe_attribute(obj, argument, value):
182 return value
183 return self.undefined('access to attribute %r of %r '
184 'object is unsafe.' % (
185 argument,
186 obj.__class__.__name__
187 ), name=argument, exc=SecurityError)
Armin Ronacher9a822052008-04-17 18:44:07 +0200188 return self.undefined(obj=obj, name=argument)
Armin Ronacherc63243e2008-04-14 22:53:58 +0200189
Armin Ronacherfd310492008-05-25 00:16:51 +0200190 def call(__self, __context, __obj, *args, **kwargs):
Armin Ronacherc63243e2008-04-14 22:53:58 +0200191 """Call an object from sandboxed code."""
192 # the double prefixes are to avoid double keyword argument
193 # errors when proxying the call.
194 if not __self.is_safe_callable(__obj):
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200195 raise SecurityError('%r is not safely callable' % (__obj,))
Armin Ronacherfd310492008-05-25 00:16:51 +0200196 return __context.call(__obj, *args, **kwargs)
Armin Ronacher522cad62008-05-17 13:55:37 +0200197
198
199class ImmutableSandboxedEnvironment(SandboxedEnvironment):
200 """Works exactly like the regular `SandboxedEnvironment` but does not
201 permit modifications on the builtin mutable objects `list`, `set`, and
202 `dict` by using the :func:`modifies_builtin_mutable` function.
203 """
204
205 def is_safe_attribute(self, obj, attr, value):
206 if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value):
207 return False
208 return not modifies_builtin_mutable(obj, attr)