blob: e0273019ee684fedb0838db92128e9132ab90a90 [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 Ronacherb5f522c2008-05-04 18:25:02 +020015from types import FunctionType, MethodType, TracebackType, CodeType, \
16 FrameType, GeneratorType
Armin Ronacherc63243e2008-04-14 22:53:58 +020017from jinja2.runtime import Undefined
18from jinja2.environment import Environment
19
20
21#: maximum number of items a range may produce
22MAX_RANGE = 100000
23
Armin Ronacher76c280b2008-05-04 12:31:48 +020024#: attributes of function objects that are considered unsafe.
25UNSAFE_FUNCTION_ATTRIBUTES = set(['func_closure', 'func_code', 'func_dict',
26 'func_defaults', 'func_globals'])
27
28#: unsafe method attributes. function attributes are unsafe for methods too
29UNSAFE_METHOD_ATTRIBUTES = set(['im_class', 'im_func', 'im_self'])
30
Armin Ronacherc63243e2008-04-14 22:53:58 +020031
32def safe_range(*args):
33 """A range that can't generate ranges with a length of more than
Armin Ronacher7ceced52008-05-03 10:15:31 +020034 MAX_RANGE items.
35 """
Armin Ronacherc63243e2008-04-14 22:53:58 +020036 rng = xrange(*args)
37 if len(rng) > MAX_RANGE:
Armin Ronacher76c280b2008-05-04 12:31:48 +020038 raise OverflowError('range too big, maximum size for range is %d' %
39 MAX_RANGE)
Armin Ronacherc63243e2008-04-14 22:53:58 +020040 return rng
41
42
43def unsafe(f):
44 """Mark a function as unsafe."""
45 f.unsafe_callable = True
46 return f
47
48
49class SandboxedEnvironment(Environment):
50 """The sandboxed environment"""
51 sandboxed = True
52
53 def __init__(self, *args, **kwargs):
54 Environment.__init__(self, *args, **kwargs)
55 self.globals['range'] = safe_range
56
Armin Ronacher9a822052008-04-17 18:44:07 +020057 def is_safe_attribute(self, obj, attr, value):
Armin Ronacherc63243e2008-04-14 22:53:58 +020058 """The sandboxed environment will call this method to check if the
59 attribute of an object is safe to access. Per default all attributes
60 starting with an underscore are considered private as well as the
61 special attributes of functions and methods.
62 """
63 if attr.startswith('_'):
64 return False
65 if isinstance(obj, FunctionType):
Armin Ronacher76c280b2008-05-04 12:31:48 +020066 return attr not in UNSAFE_FUNCTION_ATTRIBUTES
Armin Ronacherc63243e2008-04-14 22:53:58 +020067 if isinstance(obj, MethodType):
Armin Ronacher76c280b2008-05-04 12:31:48 +020068 return attr not in UNSAFE_FUNCTION_ATTRIBUTES and \
69 attr not in UNSAFE_METHOD_ATTRIBUTES
Armin Ronacherb5f522c2008-05-04 18:25:02 +020070 if isinstance(obj, type):
71 return attr != 'mro'
72 if isinstance(obj, (CodeType, TracebackType, FrameType)):
73 return False
74 if isinstance(obj, GeneratorType):
75 return attr != 'gi_frame'
Armin Ronacherc63243e2008-04-14 22:53:58 +020076 return True
77
78 def is_safe_callable(self, obj):
79 """Check if an object is safely callable. Per default a function is
80 considered safe unless the `unsafe_callable` attribute exists and is
Armin Ronacher7ceced52008-05-03 10:15:31 +020081 True. Override this method to alter the behavior, but this won't
Armin Ronacherc63243e2008-04-14 22:53:58 +020082 affect the `unsafe` decorator from this module.
83 """
Armin Ronacher7ceced52008-05-03 10:15:31 +020084 return not (getattr(obj, 'unsafe_callable', False) or \
85 getattr(obj, 'alters_data', False))
Armin Ronacherc63243e2008-04-14 22:53:58 +020086
Armin Ronacher9a822052008-04-17 18:44:07 +020087 def subscribe(self, obj, argument):
Armin Ronacherc63243e2008-04-14 22:53:58 +020088 """Subscribe an object from sandboxed code."""
Armin Ronacher9a822052008-04-17 18:44:07 +020089 is_unsafe = False
Armin Ronacherc63243e2008-04-14 22:53:58 +020090 try:
Armin Ronacher9a822052008-04-17 18:44:07 +020091 value = getattr(obj, str(argument))
92 except (AttributeError, UnicodeError):
93 pass
94 else:
95 if self.is_safe_attribute(obj, argument, value):
96 return value
97 is_unsafe = True
98 try:
99 return obj[argument]
Armin Ronacherc63243e2008-04-14 22:53:58 +0200100 except (TypeError, LookupError):
Armin Ronacher9a822052008-04-17 18:44:07 +0200101 if is_unsafe:
102 return self.undefined('access to attribute %r of %r object is'
103 ' unsafe.' % (
104 argument,
105 obj.__class__.__name__
Armin Ronacherb5f522c2008-05-04 18:25:02 +0200106 ), name=argument)
Armin Ronacher9a822052008-04-17 18:44:07 +0200107 return self.undefined(obj=obj, name=argument)
Armin Ronacherc63243e2008-04-14 22:53:58 +0200108
109 def call(__self, __obj, *args, **kwargs):
110 """Call an object from sandboxed code."""
111 # the double prefixes are to avoid double keyword argument
112 # errors when proxying the call.
113 if not __self.is_safe_callable(__obj):
114 raise TypeError('%r is not safely callable' % (__obj,))
115 return __obj(*args, **kwargs)