316 lines
7.9 KiB
Python
316 lines
7.9 KiB
Python
|
|
##############################################################################
|
||
|
|
#
|
||
|
|
# Copyright (c) 2002 Zope Foundation and Contributors.
|
||
|
|
#
|
||
|
|
# This software is subject to the provisions of the Zope Public License,
|
||
|
|
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
|
||
|
|
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
|
||
|
|
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||
|
|
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
|
||
|
|
# FOR A PARTICULAR PURPOSE
|
||
|
|
#
|
||
|
|
##############################################################################
|
||
|
|
|
||
|
|
# This tiny set of safe builtins is extended by users of the module.
|
||
|
|
# AccessControl.ZopeGuards contains a large set of wrappers for builtins.
|
||
|
|
# DocumentTemplate.DT_UTil contains a few.
|
||
|
|
|
||
|
|
import builtins
|
||
|
|
|
||
|
|
from RestrictedPython.transformer import INSPECT_ATTRIBUTES
|
||
|
|
|
||
|
|
|
||
|
|
safe_builtins = {}
|
||
|
|
|
||
|
|
_safe_names = [
|
||
|
|
'__build_class__',
|
||
|
|
'None',
|
||
|
|
'False',
|
||
|
|
'True',
|
||
|
|
'abs',
|
||
|
|
'bool',
|
||
|
|
'bytes',
|
||
|
|
'callable',
|
||
|
|
'chr',
|
||
|
|
'complex',
|
||
|
|
'divmod',
|
||
|
|
'float',
|
||
|
|
'hash',
|
||
|
|
'hex',
|
||
|
|
'id',
|
||
|
|
'int',
|
||
|
|
'isinstance',
|
||
|
|
'issubclass',
|
||
|
|
'len',
|
||
|
|
'oct',
|
||
|
|
'ord',
|
||
|
|
'pow',
|
||
|
|
'range',
|
||
|
|
'repr',
|
||
|
|
'round',
|
||
|
|
'slice',
|
||
|
|
'sorted',
|
||
|
|
'str',
|
||
|
|
'tuple',
|
||
|
|
'zip'
|
||
|
|
]
|
||
|
|
|
||
|
|
_safe_exceptions = [
|
||
|
|
'ArithmeticError',
|
||
|
|
'AssertionError',
|
||
|
|
'AttributeError',
|
||
|
|
'BaseException',
|
||
|
|
'BufferError',
|
||
|
|
'BytesWarning',
|
||
|
|
'DeprecationWarning',
|
||
|
|
'EOFError',
|
||
|
|
'EnvironmentError',
|
||
|
|
'Exception',
|
||
|
|
'FloatingPointError',
|
||
|
|
'FutureWarning',
|
||
|
|
'GeneratorExit',
|
||
|
|
'IOError',
|
||
|
|
'ImportError',
|
||
|
|
'ImportWarning',
|
||
|
|
'IndentationError',
|
||
|
|
'IndexError',
|
||
|
|
'KeyError',
|
||
|
|
'KeyboardInterrupt',
|
||
|
|
'LookupError',
|
||
|
|
'MemoryError',
|
||
|
|
'NameError',
|
||
|
|
'NotImplementedError',
|
||
|
|
'OSError',
|
||
|
|
'OverflowError',
|
||
|
|
'PendingDeprecationWarning',
|
||
|
|
'ReferenceError',
|
||
|
|
'RuntimeError',
|
||
|
|
'RuntimeWarning',
|
||
|
|
'StopIteration',
|
||
|
|
'SyntaxError',
|
||
|
|
'SyntaxWarning',
|
||
|
|
'SystemError',
|
||
|
|
'SystemExit',
|
||
|
|
'TabError',
|
||
|
|
'TypeError',
|
||
|
|
'UnboundLocalError',
|
||
|
|
'UnicodeDecodeError',
|
||
|
|
'UnicodeEncodeError',
|
||
|
|
'UnicodeError',
|
||
|
|
'UnicodeTranslateError',
|
||
|
|
'UnicodeWarning',
|
||
|
|
'UserWarning',
|
||
|
|
'ValueError',
|
||
|
|
'Warning',
|
||
|
|
'ZeroDivisionError',
|
||
|
|
]
|
||
|
|
|
||
|
|
for name in _safe_names:
|
||
|
|
safe_builtins[name] = getattr(builtins, name)
|
||
|
|
|
||
|
|
for name in _safe_exceptions:
|
||
|
|
safe_builtins[name] = getattr(builtins, name)
|
||
|
|
|
||
|
|
|
||
|
|
# Wrappers provided by this module:
|
||
|
|
# delattr
|
||
|
|
# setattr
|
||
|
|
|
||
|
|
# Wrappers provided by ZopeGuards:
|
||
|
|
# __import__
|
||
|
|
# apply
|
||
|
|
# dict
|
||
|
|
# enumerate
|
||
|
|
# filter
|
||
|
|
# getattr
|
||
|
|
# hasattr
|
||
|
|
# iter
|
||
|
|
# list
|
||
|
|
# map
|
||
|
|
# max
|
||
|
|
# min
|
||
|
|
# sum
|
||
|
|
# all
|
||
|
|
# any
|
||
|
|
|
||
|
|
# Builtins that are intentionally disabled
|
||
|
|
# compile - don't let them produce new code
|
||
|
|
# dir - a general purpose introspector, probably hard to wrap
|
||
|
|
# execfile - no direct I/O
|
||
|
|
# file - no direct I/O
|
||
|
|
# globals - uncontrolled namespace access
|
||
|
|
# input - no direct I/O
|
||
|
|
# locals - uncontrolled namespace access
|
||
|
|
# open - no direct I/O
|
||
|
|
# raw_input - no direct I/O
|
||
|
|
# vars - uncontrolled namespace access
|
||
|
|
|
||
|
|
# There are several strings that describe Python. I think there's no
|
||
|
|
# point to including these, although they are obviously safe:
|
||
|
|
# copyright, credits, exit, help, license, quit
|
||
|
|
|
||
|
|
# Not provided anywhere. Do something about these? Several are
|
||
|
|
# related to new-style classes, which we are too scared of to support
|
||
|
|
# <0.3 wink>. coerce, buffer, and reload are esoteric enough that no
|
||
|
|
# one should care.
|
||
|
|
|
||
|
|
# buffer
|
||
|
|
# bytearray
|
||
|
|
# classmethod
|
||
|
|
# coerce
|
||
|
|
# eval
|
||
|
|
# intern
|
||
|
|
# memoryview
|
||
|
|
# object
|
||
|
|
# property
|
||
|
|
# reload
|
||
|
|
# staticmethod
|
||
|
|
# super
|
||
|
|
# type
|
||
|
|
|
||
|
|
|
||
|
|
def _write_wrapper():
|
||
|
|
# Construct the write wrapper class
|
||
|
|
def _handler(secattr, error_msg):
|
||
|
|
# Make a class method.
|
||
|
|
def handler(self, *args):
|
||
|
|
try:
|
||
|
|
f = getattr(self.ob, secattr)
|
||
|
|
except AttributeError:
|
||
|
|
raise TypeError(error_msg)
|
||
|
|
f(*args)
|
||
|
|
return handler
|
||
|
|
|
||
|
|
class Wrapper:
|
||
|
|
def __init__(self, ob):
|
||
|
|
self.__dict__['ob'] = ob
|
||
|
|
|
||
|
|
__setitem__ = _handler(
|
||
|
|
'__guarded_setitem__',
|
||
|
|
'object does not support item or slice assignment')
|
||
|
|
|
||
|
|
__delitem__ = _handler(
|
||
|
|
'__guarded_delitem__',
|
||
|
|
'object does not support item or slice assignment')
|
||
|
|
|
||
|
|
__setattr__ = _handler(
|
||
|
|
'__guarded_setattr__',
|
||
|
|
'attribute-less object (assign or del)')
|
||
|
|
|
||
|
|
__delattr__ = _handler(
|
||
|
|
'__guarded_delattr__',
|
||
|
|
'attribute-less object (assign or del)')
|
||
|
|
return Wrapper
|
||
|
|
|
||
|
|
|
||
|
|
def _full_write_guard():
|
||
|
|
# Nested scope abuse!
|
||
|
|
# safetypes and Wrapper variables are used by guard()
|
||
|
|
safetypes = {dict, list}
|
||
|
|
Wrapper = _write_wrapper()
|
||
|
|
|
||
|
|
def guard(ob):
|
||
|
|
# Don't bother wrapping simple types, or objects that claim to
|
||
|
|
# handle their own write security.
|
||
|
|
if type(ob) in safetypes or hasattr(ob, '_guarded_writes'):
|
||
|
|
return ob
|
||
|
|
# Hand the object to the Wrapper instance, then return the instance.
|
||
|
|
return Wrapper(ob)
|
||
|
|
return guard
|
||
|
|
|
||
|
|
|
||
|
|
full_write_guard = _full_write_guard()
|
||
|
|
|
||
|
|
|
||
|
|
def guarded_setattr(object, name, value):
|
||
|
|
setattr(full_write_guard(object), name, value)
|
||
|
|
|
||
|
|
|
||
|
|
safe_builtins['setattr'] = guarded_setattr
|
||
|
|
|
||
|
|
|
||
|
|
def guarded_delattr(object, name):
|
||
|
|
delattr(full_write_guard(object), name)
|
||
|
|
|
||
|
|
|
||
|
|
safe_builtins['delattr'] = guarded_delattr
|
||
|
|
|
||
|
|
|
||
|
|
raise_ = object()
|
||
|
|
|
||
|
|
|
||
|
|
def safer_getattr(object, name, default=None, getattr=getattr):
|
||
|
|
"""Getattr implementation which prevents using format on string objects.
|
||
|
|
|
||
|
|
format() is considered harmful:
|
||
|
|
http://lucumr.pocoo.org/2016/12/29/careful-with-str-format/
|
||
|
|
|
||
|
|
"""
|
||
|
|
if type(name) is not str:
|
||
|
|
raise TypeError('type(name) must be str')
|
||
|
|
if name in ('format', 'format_map') and (
|
||
|
|
isinstance(object, str) or
|
||
|
|
(isinstance(object, type) and issubclass(object, str))):
|
||
|
|
raise NotImplementedError(
|
||
|
|
'Using the format*() methods of `str` is not safe')
|
||
|
|
if name in INSPECT_ATTRIBUTES:
|
||
|
|
raise AttributeError(
|
||
|
|
f'"{name}" is a restricted name,'
|
||
|
|
' that is forbidden to access in RestrictedPython.')
|
||
|
|
if name.startswith('_'):
|
||
|
|
raise AttributeError(
|
||
|
|
'"{name}" is an invalid attribute name because it '
|
||
|
|
'starts with "_"'.format(name=name)
|
||
|
|
)
|
||
|
|
args = (object, name) + (() if default is raise_ else (default,))
|
||
|
|
return getattr(*args)
|
||
|
|
|
||
|
|
|
||
|
|
safe_builtins['_getattr_'] = safer_getattr
|
||
|
|
|
||
|
|
|
||
|
|
def safer_getattr_raise(object, name, default=raise_):
|
||
|
|
"""like ``safer_getattr`` but raising ``AttributeError`` if failing."""
|
||
|
|
return safer_getattr(object, name, default)
|
||
|
|
|
||
|
|
|
||
|
|
def guarded_iter_unpack_sequence(it, spec, _getiter_):
|
||
|
|
"""Protect sequence unpacking of targets in a 'for loop'.
|
||
|
|
|
||
|
|
The target of a for loop could be a sequence.
|
||
|
|
For example "for a, b in it"
|
||
|
|
=> Each object from the iterator needs guarded sequence unpacking.
|
||
|
|
"""
|
||
|
|
# The iteration itself needs to be protected as well.
|
||
|
|
for ob in _getiter_(it):
|
||
|
|
yield guarded_unpack_sequence(ob, spec, _getiter_)
|
||
|
|
|
||
|
|
|
||
|
|
def guarded_unpack_sequence(it, spec, _getiter_):
|
||
|
|
"""Protect nested sequence unpacking.
|
||
|
|
|
||
|
|
Protect the unpacking of 'it' by wrapping it with '_getiter_'.
|
||
|
|
Furthermore for each child element, defined by spec,
|
||
|
|
guarded_unpack_sequence is called again.
|
||
|
|
|
||
|
|
Have a look at transformer.py 'gen_unpack_spec' for a more detailed
|
||
|
|
explanation.
|
||
|
|
"""
|
||
|
|
# Do the guarded unpacking of the sequence.
|
||
|
|
ret = list(_getiter_(it))
|
||
|
|
|
||
|
|
# If the sequence is shorter then expected the interpreter will raise
|
||
|
|
# 'ValueError: need more than X value to unpack' anyway
|
||
|
|
# => No childs are unpacked => nothing to protect.
|
||
|
|
if len(ret) < spec['min_len']:
|
||
|
|
return ret
|
||
|
|
|
||
|
|
# For all child elements do the guarded unpacking again.
|
||
|
|
for (idx, child_spec) in spec['childs']:
|
||
|
|
ret[idx] = guarded_unpack_sequence(ret[idx], child_spec, _getiter_)
|
||
|
|
|
||
|
|
return ret
|
||
|
|
|
||
|
|
|
||
|
|
safe_globals = {'__builtins__': safe_builtins}
|