Skip to content

Commit

Permalink
improved sandbox and updated setup.py
Browse files Browse the repository at this point in the history
--HG--
branch : trunk
  • Loading branch information
mitsuhiko committed May 26, 2008
1 parent 4dcc237 commit d71fff0
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 61 deletions.
2 changes: 1 addition & 1 deletion docs/sandbox.rst
Expand Up @@ -28,4 +28,4 @@ SecurityError: access to attribute 'func_code' of 'function' object is unsafe.

.. autofunction:: is_internal_attribute

.. autofunction:: modifies_builtin_mutable
.. autofunction:: modifies_known_mutable
2 changes: 1 addition & 1 deletion jinja2/_speedups.c
Expand Up @@ -157,7 +157,7 @@ soft_unicode(PyObject *self, PyObject *s)
}


static PyObject *
static PyObject*
tb_set_next(PyObject *self, PyObject *args)
{
PyTracebackObject *tb, *old;
Expand Down
4 changes: 0 additions & 4 deletions jinja2/runtime.py
Expand Up @@ -117,10 +117,6 @@ def get_all(self):
return dict(self.parent, **self.vars)

def call(__self, __obj, *args, **kwargs):
"""Called by the template code to inject the current context
or environment as first arguments. Then forwards the call to
the object with the arguments and keyword arguments.
"""
if __debug__:
__traceback_hide__ = True
if isinstance(__obj, _context_function_types):
Expand Down
101 changes: 61 additions & 40 deletions jinja2/sandbox.py
Expand Up @@ -13,9 +13,6 @@
:license: BSD.
"""
import operator
from UserDict import UserDict, DictMixin
from UserList import UserList
from sets import Set, ImmutableSet
from types import FunctionType, MethodType, TracebackType, CodeType, \
FrameType, GeneratorType
from jinja2.runtime import Undefined
Expand All @@ -33,19 +30,40 @@
#: unsafe method attributes. function attributes are unsafe for methods too
UNSAFE_METHOD_ATTRIBUTES = set(['im_class', 'im_func', 'im_self'])

SET_TYPES = (ImmutableSet, Set, set)
MODIFYING_SET_ATTRIBUTES = set([
'add', 'clear', 'difference_update', 'discard', 'pop', 'remove',
'symmetric_difference_update', 'update'
])

DICT_TYPES = (UserDict, DictMixin, dict)
MODIFYING_DICT_ATTRIBUTES = set(['clear', 'pop', 'popitem', 'setdefault',
'update'])

LIST_TYPES = (UserList, list)
MODIFYING_LIST_ATTRIBUTES = set(['append', 'reverse', 'insert', 'sort',
'extend', 'remove'])
from collections import deque
from sets import Set, ImmutableSet
from UserDict import UserDict, DictMixin
from UserList import UserList
_mutable_set_types = (ImmutableSet, Set, set)
_mutable_mapping_types = (UserDict, DictMixin, dict)
_mutable_sequence_types = (UserList, list)

#: register Python 2.6 abstract base classes
try:
from collections import MutableSet, MutableMapping, MutableSequence
_mutable_set_types += (MutableSet,)
_mutable_mapping_types += (MutableMapping,)
_mutable_sequence_types += (MutableSequence,)
except ImportError:
pass

_mutable_spec = (
(_mutable_set_types, frozenset([
'add', 'clear', 'difference_update', 'discard', 'pop', 'remove',
'symmetric_difference_update', 'update'
])),
(_mutable_mapping_types, frozenset([
'clear', 'pop', 'popitem', 'setdefault', 'update'
])),
(_mutable_sequence_types, frozenset([
'append', 'reverse', 'insert', 'sort', 'extend', 'remove'
])),
(deque, frozenset([
'append', 'appendleft', 'clear', 'extend', 'extendleft', 'pop',
'popleft', 'remove', 'rotate'
]))
)


def safe_range(*args):
Expand Down Expand Up @@ -86,45 +104,48 @@ def is_internal_attribute(obj, attr):
False
"""
if isinstance(obj, FunctionType):
return attr in UNSAFE_FUNCTION_ATTRIBUTES
if isinstance(obj, MethodType):
return attr in UNSAFE_FUNCTION_ATTRIBUTES or \
attr in UNSAFE_METHOD_ATTRIBUTES
if isinstance(obj, type):
return attr == 'mro'
if isinstance(obj, (CodeType, TracebackType, FrameType)):
if attr in UNSAFE_FUNCTION_ATTRIBUTES:
return True
elif isinstance(obj, MethodType):
if attr in UNSAFE_FUNCTION_ATTRIBUTES or \
attr in UNSAFE_METHOD_ATTRIBUTES:
return True
elif isinstance(obj, type):
if attr == 'mro':
return True
elif isinstance(obj, (CodeType, TracebackType, FrameType)):
return True
if isinstance(obj, GeneratorType):
return attr == 'gi_frame'
elif isinstance(obj, GeneratorType):
if attr == 'gi_frame':
return True
return attr.startswith('__')


def modifies_builtin_mutable(obj, attr):
def modifies_known_mutable(obj, attr):
"""This function checks if an attribute on a builtin mutable object
(list, dict or set) would modify it if called. It also supports
the "user"-versions of the objects (`sets.Set`, `UserDict.*` etc.)
(list, dict, set or deque) would modify it if called. It also supports
the "user"-versions of the objects (`sets.Set`, `UserDict.*` etc.) and
with Python 2.6 onwards the abstract base classes `MutableSet`,
`MutableMapping`, and `MutableSequence`.
>>> modifies_builtin_mutable({}, "clear")
>>> modifies_known_mutable({}, "clear")
True
>>> modifies_builtin_mutable({}, "keys")
>>> modifies_known_mutable({}, "keys")
False
>>> modifies_builtin_mutable([], "append")
>>> modifies_known_mutable([], "append")
True
>>> modifies_builtin_mutable([], "index")
>>> modifies_known_mutable([], "index")
False
If called with an unsupported object (such as unicode) `False` is
returned.
>>> modifies_builtin_mutable("foo", "upper")
>>> modifies_known_mutable("foo", "upper")
False
"""
if isinstance(obj, LIST_TYPES):
return attr in MODIFYING_LIST_ATTRIBUTES
elif isinstance(obj, DICT_TYPES):
return attr in MODIFYING_DICT_ATTRIBUTES
elif isinstance(obj, SET_TYPES):
return attr in MODIFYING_SET_ATTRIBUTES
for typespec, unsafe in _mutable_spec:
if isinstance(obj, typespec):
return attr in unsafe
return False


Expand Down Expand Up @@ -199,10 +220,10 @@ def call(__self, __context, __obj, *args, **kwargs):
class ImmutableSandboxedEnvironment(SandboxedEnvironment):
"""Works exactly like the regular `SandboxedEnvironment` but does not
permit modifications on the builtin mutable objects `list`, `set`, and
`dict` by using the :func:`modifies_builtin_mutable` function.
`dict` by using the :func:`modifies_known_mutable` function.
"""

def is_safe_attribute(self, obj, attr, value):
if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value):
return False
return not modifies_builtin_mutable(obj, attr)
return not modifies_known_mutable(obj, attr)
35 changes: 21 additions & 14 deletions jinja2/utils.py
Expand Up @@ -253,9 +253,9 @@ def __radd__(self, other):
return NotImplemented

def __mul__(self, num):
if not isinstance(num, (int, long)):
return NotImplemented
return self.__class__(unicode.__mul__(self, num))
if isinstance(num, (int, long)):
return self.__class__(unicode.__mul__(self, num))
return NotImplemented
__rmul__ = __mul__

def __mod__(self, arg):
Expand Down Expand Up @@ -319,17 +319,13 @@ def escape(cls, s):
def make_wrapper(name):
orig = getattr(unicode, name)
def func(self, *args, **kwargs):
args = list(args)
for idx, arg in enumerate(args):
if hasattr(arg, '__html__') or isinstance(arg, basestring):
args[idx] = escape(arg)
for name, arg in kwargs.iteritems():
if hasattr(arg, '__html__') or isinstance(arg, basestring):
kwargs[name] = escape(arg)
args = _escape_argspec(list(args), enumerate(args))
_escape_argspec(kwargs, kwargs.iteritems())
return self.__class__(orig(self, *args, **kwargs))
func.__name__ = orig.__name__
func.__doc__ = orig.__doc__
return func

for method in '__getitem__', '__getslice__', 'capitalize', \
'title', 'lower', 'upper', 'replace', 'ljust', \
'rjust', 'lstrip', 'rstrip', 'center', 'strip', \
Expand All @@ -338,13 +334,24 @@ def func(self, *args, **kwargs):

# new in python 2.5
if hasattr(unicode, 'partition'):
locals().update(
partition=make_wrapper('partition'),
rpartition=make_wrapper('rpartition')
)
partition = make_wrapper('partition'),
rpartition = make_wrapper('rpartition')

# new in python 2.6
if hasattr(unicode, 'format'):
format = make_wrapper('format')

del method, make_wrapper


def _escape_argspec(obj, iterable):
"""Helper for various string-wrapped functions."""
for key, value in iterable:
if hasattr(value, '__html__') or isinstance(value, basestring):
obj[key] = escape(value)
return obj


class _MarkupEscapeHelper(object):
"""Helper for Markup.__mod__"""

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -120,7 +120,7 @@ def _unavailable(self):
],
packages=['jinja2'],
data_files=[
##('docs', list(list_files('docs/_build/html')))
('docs', list(list_files('docs/_build/html')))
],
features={
'speedups': Feature("optional C speed-enhancements",
Expand Down

0 comments on commit d71fff0

Please sign in to comment.