blob: b0de8e74f0de818829b702d7c040dfb22aa0f37c [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 is_unsafe = False
Armin Ronacher08a6a3b2008-05-13 15:35:47 +0200168 if isinstance(argument, basestring):
169 try:
Armin Ronacherab5ad8c2008-05-17 00:34:11 +0200170 attr = str(argument)
171 except:
Armin Ronacher08a6a3b2008-05-13 15:35:47 +0200172 pass
173 else:
Armin Ronacherab5ad8c2008-05-17 00:34:11 +0200174 try:
175 value = getattr(obj, attr)
176 except AttributeError:
177 pass
178 else:
179 if self.is_safe_attribute(obj, argument, value):
180 return value
181 is_unsafe = True
Armin Ronacher9a822052008-04-17 18:44:07 +0200182 try:
183 return obj[argument]
Armin Ronacherc63243e2008-04-14 22:53:58 +0200184 except (TypeError, LookupError):
Armin Ronacher9a822052008-04-17 18:44:07 +0200185 if is_unsafe:
186 return self.undefined('access to attribute %r of %r object is'
187 ' unsafe.' % (
188 argument,
189 obj.__class__.__name__
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200190 ), name=argument, exc=SecurityError)
Armin Ronacher9a822052008-04-17 18:44:07 +0200191 return self.undefined(obj=obj, name=argument)
Armin Ronacherc63243e2008-04-14 22:53:58 +0200192
Armin Ronacherfd310492008-05-25 00:16:51 +0200193 def call(__self, __context, __obj, *args, **kwargs):
Armin Ronacherc63243e2008-04-14 22:53:58 +0200194 """Call an object from sandboxed code."""
195 # the double prefixes are to avoid double keyword argument
196 # errors when proxying the call.
197 if not __self.is_safe_callable(__obj):
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200198 raise SecurityError('%r is not safely callable' % (__obj,))
Armin Ronacherfd310492008-05-25 00:16:51 +0200199 return __context.call(__obj, *args, **kwargs)
Armin Ronacher522cad62008-05-17 13:55:37 +0200200
201
202class ImmutableSandboxedEnvironment(SandboxedEnvironment):
203 """Works exactly like the regular `SandboxedEnvironment` but does not
204 permit modifications on the builtin mutable objects `list`, `set`, and
205 `dict` by using the :func:`modifies_builtin_mutable` function.
206 """
207
208 def is_safe_attribute(self, obj, attr, value):
209 if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value):
210 return False
211 return not modifies_builtin_mutable(obj, attr)