##############################################################################
#
# 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}