blob: 71f0239ca363388452bdeb3000260b876a30b34f [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"""
15from types import FunctionType, MethodType
16from jinja2.runtime import Undefined
17from jinja2.environment import Environment
18
19
20#: maximum number of items a range may produce
21MAX_RANGE = 100000
22
23
24def safe_range(*args):
25 """A range that can't generate ranges with a length of more than
26 MAX_RANGE items."""
27 rng = xrange(*args)
28 if len(rng) > MAX_RANGE:
29 raise OverflowError('range too big')
30 return rng
31
32
33def unsafe(f):
34 """Mark a function as unsafe."""
35 f.unsafe_callable = True
36 return f
37
38
39class SandboxedEnvironment(Environment):
40 """The sandboxed environment"""
41 sandboxed = True
42
43 def __init__(self, *args, **kwargs):
44 Environment.__init__(self, *args, **kwargs)
45 self.globals['range'] = safe_range
46
Armin Ronacher9a822052008-04-17 18:44:07 +020047 def is_safe_attribute(self, obj, attr, value):
Armin Ronacherc63243e2008-04-14 22:53:58 +020048 """The sandboxed environment will call this method to check if the
49 attribute of an object is safe to access. Per default all attributes
50 starting with an underscore are considered private as well as the
51 special attributes of functions and methods.
52 """
53 if attr.startswith('_'):
54 return False
55 if isinstance(obj, FunctionType):
56 return not attr.startswith('func_')
57 if isinstance(obj, MethodType):
58 return not attr.startswith('im_')
59 return True
60
61 def is_safe_callable(self, obj):
62 """Check if an object is safely callable. Per default a function is
63 considered safe unless the `unsafe_callable` attribute exists and is
64 truish. Override this method to alter the behavior, but this won't
65 affect the `unsafe` decorator from this module.
66 """
67 return not getattr(obj, 'unsafe_callable', False)
68
Armin Ronacher9a822052008-04-17 18:44:07 +020069 def subscribe(self, obj, argument):
Armin Ronacherc63243e2008-04-14 22:53:58 +020070 """Subscribe an object from sandboxed code."""
Armin Ronacher9a822052008-04-17 18:44:07 +020071 is_unsafe = False
Armin Ronacherc63243e2008-04-14 22:53:58 +020072 try:
Armin Ronacher9a822052008-04-17 18:44:07 +020073 value = getattr(obj, str(argument))
74 except (AttributeError, UnicodeError):
75 pass
76 else:
77 if self.is_safe_attribute(obj, argument, value):
78 return value
79 is_unsafe = True
80 try:
81 return obj[argument]
Armin Ronacherc63243e2008-04-14 22:53:58 +020082 except (TypeError, LookupError):
Armin Ronacher9a822052008-04-17 18:44:07 +020083 if is_unsafe:
84 return self.undefined('access to attribute %r of %r object is'
85 ' unsafe.' % (
86 argument,
87 obj.__class__.__name__
88 ))
89 return self.undefined(obj=obj, name=argument)
Armin Ronacherc63243e2008-04-14 22:53:58 +020090
91 def call(__self, __obj, *args, **kwargs):
92 """Call an object from sandboxed code."""
93 # the double prefixes are to avoid double keyword argument
94 # errors when proxying the call.
95 if not __self.is_safe_callable(__obj):
96 raise TypeError('%r is not safely callable' % (__obj,))
97 return __obj(*args, **kwargs)