Building and breaking a
Python sandbox
Director
Organizer
@jessicamckellar
http://jesstess.com
Why?
•
Learning a language
•
Providing a hosted scratch pad
•
Distributed computation
Examples in the wild
•
Seattle’s peer-to-peer computing network
•
Google App Engine’s Python shell
•
Codecademy’s empythoned
Building a sandbox
•
Language-level sandboxing (
pysandbox
)
Question: How do we
execute arbitrary code?
How do we execute
arbitrary code?
exec
: compiles and evaluates
statements
>>> exec "print 'Hello world'"
Hello world
eval
: compiles and evaluates
expressions
>>> eval("1 + 2")
3
class
Sandbox
(object):
def
execute(
self
, code_string):
exec
code_string
from
sandbox
import
Sandbox
s = Sandbox()
code =
"""
print "Hello world!"
"""
s.execute(code)
$ python test_sandbox.py
Hello world!
from
sandbox
import
Sandbox
s = Sandbox()
code =
"""
print "Hello world!"
"""
What should we disallow?
•
Resource exhaustion
•
Information disclosure
•
Running unexpected services
•
Disabling/quitting/erroring out of the
from
sandbox
import
Sandbox
s
= Sandbox()
code
=
"""
file("test.txt", "w").write("Kaboom!\\n")
"""
s.execute(code)
>>> __builtins__.__dict__.keys()
['bytearray', 'IndexError', 'all', 'help', 'vars', 'SyntaxError', 'unicode',
'UnicodeDecodeError', 'memoryview', 'isinstance', 'copyright', 'NameError',
'BytesWarning', 'dict', 'input', 'oct', 'bin', 'SystemExit', 'StandardError', 'format', 'repr',
'sorted', 'False', 'RuntimeWarning', 'list', 'iter', 'reload', 'Warning', '__package__',
'round', 'dir', 'cmp', 'set', 'bytes', 'reduce', 'intern', 'issubclass', 'Ellipsis', 'EOFError',
'locals', 'BufferError', 'slice', 'FloatingPointError', 'sum', 'getattr', 'abs', 'exit', 'print',
'True', 'FutureWarning', 'ImportWarning', 'None', 'hash', 'ReferenceError', 'len',
'credits', 'frozenset', '__name__', 'ord', 'super', '_', 'TypeError', 'license',
'KeyboardInterrupt', 'UserWarning', 'filter', 'range', 'staticmethod', 'SystemError',
'BaseException', 'pow', 'RuntimeError', 'float', 'MemoryError', 'StopIteration',
'globals', 'divmod', 'enumerate', 'apply', 'LookupError', 'open', 'quit', 'basestring',
'UnicodeError', 'zip', 'hex', 'long', 'next', 'ImportError', 'chr', 'xrange', 'type', '__doc__',
'Exception', 'tuple', 'UnicodeTranslateError', 'reversed', 'UnicodeEncodeError',
'IOError', 'hasattr', 'delattr', 'setattr', 'raw_input', 'SyntaxWarning', 'compile',
'ArithmeticError', 'str', 'property', 'GeneratorExit', 'int', '__import__', 'KeyError',
'coerce', 'PendingDeprecationWarning', 'file', 'EnvironmentError', 'unichr', 'id',
'OSError', 'DeprecationWarning', 'min', 'UnicodeWarning', 'execfile', 'any', 'complex',
'bool', 'ValueError', 'NotImplemented', 'map', 'buffer', 'max', 'object', 'TabError',
'callable', 'ZeroDivisionError', 'eval', '__debug__', 'IndentationError',
'AssertionError', 'classmethod', 'UnboundLocalError', 'NotImplementedError',
'AttributeError', 'OverflowError']
>>> __builtins__.__dict__.keys()
['bytearray', 'IndexError', 'all', 'help', 'vars', 'SyntaxError', 'unicode',
'UnicodeDecodeError', 'memoryview', 'isinstance', 'copyright', 'NameError',
'BytesWarning', 'dict', 'input', 'oct', 'bin', 'SystemExit', 'StandardError', 'format', 'repr',
'sorted', 'False', 'RuntimeWarning', 'list', 'iter', 'reload', 'Warning', '__package__',
'round', 'dir', 'cmp', 'set', 'bytes', 'reduce', 'intern', 'issubclass', 'Ellipsis', 'EOFError',
'locals', 'BufferError', 'slice', 'FloatingPointError', 'sum', 'getattr', 'abs', 'exit', 'print',
'True', 'FutureWarning', 'ImportWarning', 'None', 'hash', 'ReferenceError', 'len',
'credits', 'frozenset', '__name__', 'ord', 'super', '_', 'TypeError', 'license',
'KeyboardInterrupt', 'UserWarning', 'filter', 'range', 'staticmethod', 'SystemError',
'BaseException', 'pow', 'RuntimeError', 'float', 'MemoryError', 'StopIteration',
'globals', 'divmod', 'enumerate', 'apply', 'LookupError', 'open', 'quit', 'basestring',
'UnicodeError', 'zip', 'hex', 'long', 'next', 'ImportError', 'chr', 'xrange', 'type', '__doc__',
'Exception', 'tuple', 'UnicodeTranslateError', 'reversed', 'UnicodeEncodeError',
'IOError', 'hasattr', 'delattr', 'setattr', 'raw_input', 'SyntaxWarning', 'compile',
'ArithmeticError', 'str', 'property', 'GeneratorExit', 'int', '__import__', 'KeyError',
'coerce', 'PendingDeprecationWarning', 'file', 'EnvironmentError', 'unichr', 'id',
'OSError', 'DeprecationWarning', 'min', 'UnicodeWarning', 'execfile', 'any', 'complex',
'bool', 'ValueError', 'NotImplemented', 'map', 'buffer', 'max', 'object', 'TabError',
'callable', 'ZeroDivisionError', 'eval', '__debug__', 'IndentationError',
'AssertionError', 'classmethod', 'UnboundLocalError', 'NotImplementedError',
'AttributeError', 'OverflowError']
How do we disallow
execution of
class
Sandbox
(object):
def execute(self, code_string):
keyword_blacklist
= [
"file"
,
"open"
,
"eval"
,
"exec"
]
for keyword in keyword_blacklist:
if keyword in code_string:
raise ValueError(
"Blacklisted"
)
exec code_string
from
sandbox
import
Sandbox
s
= Sandbox()
code
=
"""
file("test.txt", "w").write("Kaboom!\\n")
"""
s.execute(code)
from
sandbox
import
Sandbox
s
= Sandbox()
code
=
"""
file("test.txt", "w").write("Kaboom!\\n")
"""
s.execute(code)
$ python test_sandbox.py
Traceback (most recent call last):
File "test_sandbox.py", line 11, in
<module>
s.execute(code)
File "/Users/jesstess/Desktop/sandbox/
sandbox.py", line 86, in execute
raise ValueError("Blacklisted")
ValueError: Blacklisted
How can we get
around a keyword
Circumvention idea:
encryption
func
= __builtins__[
"file"
]
Circumvention idea:
encryption
func
= __builtins__[
"file"
]
func(
"test.txt"
,
"w"
).write(
"Kaboom!\n"
)
func
= __builtins__[
"svyr"
.decode(
"rot13"
)]
func(
"test.txt"
,
"w"
).write(
"Kaboom!\n"
)
from
sandbox
import
Sandbox
s
= Sandbox()
code
=
"""
func = __builtins__["svyr".decode("rot13")]
func("test.txt", "w").write("Kaboom!\\n")
"""
s.execute(code)
Testing: keyword blacklist
Observation: if I can get a
reference to something
How can we remove all
references to
builtins_whitelist
= set((
# exceptions
'ArithmeticError', 'AssertionError', 'AttributeError',
...
# constants
'False', 'None', 'True',
...
# types
'basestring', 'bytearray', 'bytes', 'complex', 'dict',
...
# functions
'__import__', 'abs', 'all', 'any', 'apply', 'bin', 'bool',
...
# block: eval, execfile, file, quit, exit, reload, etc.
import
sys
main
= sys.modules[
"__main__"
].__dict__
orig_builtins
= main[
"__builtins__
"].__dict__
builtins_whitelist
= set(( ... ))
for
builtin
in
orig_builtins.keys():
if
builtin
not in
builtins_whitelist:
del
orig_builtins[builtin]
from
sandbox
import
Sandbox
s
= Sandbox()
code
=
"""
file("test.txt", "w").write("Kaboom!\\n")
"""
s.execute(code)
from
sandbox
import
Sandbox
s
= Sandbox()
code
=
"""
file("test.txt", "w").write("Kaboom!\\n")
"""
s.execute(code)
$ python test_sandbox.py
Traceback (most recent call last):
File "test_sandbox.py", line 9, in
<module>
s.execute(code)
...
File "<string>", line 2, in <module>
NameError: name 'file' is not defined
Circumvention idea:
import something
from sandbox import Sandbox
s
= Sandbox()
code
=
"""
import os
fd = os.open("test.txt", os.O_CREAT|os.O_WRONLY)
os.write(fd, "Kaboom!\\n")
"""
s.execute(code)
Testing: builtins whitelist
How do we disallow
problematic imports?
>>>
importer
= __builtins__.__dict__.get(
"__import__"
)
>>>
os
= importer(
"os"
)
>>> os
<module 'os' from '/Library/Frameworks/
Python.framework/Versions/2.7/lib/python2.7/os.pyc'>
>>> os.getcwd()
'/Users/jesstess/Desktop/sandbox'
Idea: import whitelist
>>> help(__builtins__.__dict__[
"__import__"]
)
__import__(...)
__import__(name, globals={}, locals={},
fromlist=[], level=-1) -> module
Idea: import whitelist
What is the expected function signature for the
importer?
>>>
def
my_importer(module_name, globals={},
... locals={}, fromlist=[],
... level=-1):
...
"Using my importer!"
...
return
__import__(module_name, globals,
... locals, fromlist, level)
...
>>> os = my_importer(
"os"
)
Using my importer!
>>> os.getcwd()
'/Users/jesstess/Desktop/sandbox'
Idea: import whitelist
def
_safe_import(__import__, module_whitelist):
def
safe_import(module_name, globals={},
locals={}, fromlist=[],
level=-1):
!
if
module_name
in
module_whitelist:
return
__import__(module_name,
! ! ! ! ! ! ! ! ! ! ! !
globals, locals,
fromlist, level)
else
:
raise
ImportError(
"Blocked import of %s"
(
module_name,))
return
safe_import
import
sys
main
= sys.modules["__main__"].__dict__
orig_builtins
= main["__builtins__"].__dict__
for
builtin
in
orig_builtins.keys():
if
builtin
not in
builtins_whitelist:
del
original_builtins[builtin]
safe_modules
= [
"string"
,
"re"
]
orig_builtins[
"__import__"
] = _safe_import(
__import__, safe_modules)
from sandbox import Sandbox
s
= Sandbox()
code
=
"""
import os
fd = os.open("test.txt", os.O_CREAT|os.O_WRONLY)
os.write(fd, "Kaboom!\\n")
"""
s.execute(code)
from sandbox import Sandbox
s
= Sandbox()
code
=
"""
import os
fd = os.open("test.txt", os.O_CREAT|os.O_WRONLY)
os.write(fd, "Kaboom!\\n")
"""
s.execute(code)
Testing: import whitelist
$ python test_sandbox.py
Traceback (most recent call last):
File "test_sandbox.py", line 11, in
<module>
...
raise ImportError("Blocked import of
%s" % (module_name,))
Circumvention idea:
modifying builtins
Idea: make
builtins
How can we make an
object read-only in
class
ReadOnlyBuiltins
(dict):
def
__delitem__(
self
, key):
ValueError(
"Read-only!"
)
def
pop(
self
, key, default=None):
ValueError(
"Read-only!"
)
def
popitem(
self
):
ValueError(
"Read-only!"
)
...
def
setdefault(
self
, key, value):
ValueError(
"Read-only!"
)
def
__setitem__(
self
, key, value):
ValueError(
"Read-only!"
)
def
update(
self
, dict, **kw):
main
= sys.modules["__main__"].__dict__
orig_builtins
= main["__builtins__"].__dict__
for
builtin
in
orig_builtins.keys():
if
builtin
not in
builtins_whitelist:
del
original_builtins[builtin]
safe_modules
= [
"string"
,
"re"
]
orig_builtins[
"__import__"
] = _safe_import(
__import__, safe_modules)
safe_builtins
= ReadOnlyBuiltins(
original_builtins)
Observation redux: if I
can get a reference to
something bad, I can
invoke it.
Circumvention idea:
exploiting the
>>> dir([])
[
'__add__'
,
'__class__'
,
'__contains__'
,
'__delattr__'
,
'__delitem__'
,
'__delslice__'
,
'__doc__'
,
'__eq__'
,
'__format__'
,
'__ge__'
, ...]
>>> [].__class__
<type 'list'>
>>> [].__class__
<type 'list'>
>>> [].__class__.__bases__
(<type 'object'>,)
>>> [].__class__.__bases__[0]
<type 'object'>
What can we find out about an object’s base classes?
list
subclasses
>>> [].__class__.__subclasses__()
[]
>>> int.__subclasses__()
[<type 'bool'>]
>>> basestring.__subclasses__()
[<type 'str'>, <type 'unicode'>]
What can we find out about an object’s subclasses?
>>> [].__class__.__bases__
(<type 'object'>,)
>>> [].__class__.__bases__[0]
<type 'object'>
>>> [].__class__.__bases__[0].__subclasses__()
[<type 'type'>, <type 'weakref'>, <type 'weakcallableproxy'>, <type 'weakproxy'>, <type 'int'>, <type 'basestring'>, <type 'bytearray'>, <type 'list'>, <type 'NoneType'>, <type 'NotImplementedType'>, <type 'traceback'>, <type 'super'>, <type 'xrange'>, <type 'dict'>, <type 'set'>, <type 'slice'>, <type
'staticmethod'>, <type 'complex'>, <type 'float'>, <type 'buffer'>, <type 'long'>, <type 'frozenset'>, <type 'property'>, <type 'memoryview'>, <type 'tuple'>, <type 'enumerate'>, <type 'reversed'>, <type
'code'>, <type 'frame'>, <type 'builtin_function_or_method'>, <type 'instancemethod'>, <type 'function'>, <type 'classobj'>, <type 'dictproxy'>, <type 'generator'>, <type 'getset_descriptor'>, <type
'wrapper_descriptor'>, <type 'instance'>, <type 'ellipsis'>, <type 'member_descriptor'>, <type 'file'>, <type 'PyCapsule'>, <type 'cell'>, <type 'callable-iterator'>, <type 'iterator'>, <type 'sys.long_info'>, <type 'sys.float_info'>, <type 'EncodingMap'>, <type 'fieldnameiterator'>, <type 'formatteriterator'>, <type 'sys.version_info'>, <type 'sys.flags'>, <type 'exceptions.BaseException'>, <type 'module'>, <type 'imp.NullImporter'>, <type 'zipimport.zipimporter'>, <type 'posix.stat_result'>, <type
'posix.statvfs_result'>, <class 'warnings.WarningMessage'>, <class 'warnings.catch_warnings'>, <class '_weakrefset._IterationGuard'>, <class '_weakrefset.WeakSet'>, <class '_abcoll.Hashable'>, <type
'classmethod'>, <class '_abcoll.Iterable'>, <class '_abcoll.Sized'>, <class '_abcoll.Container'>, <class '_abcoll.Callable'>, <class 'site._Printer'>, <class 'site._Helper'>, <type '_sre.SRE_Pattern'>, <type
'_sre.SRE_Match'>, <type '_sre.SRE_Scanner'>, <class 'site.Quitter'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>, <class 'string.Template'>, <class 'string.Formatter'>, <type
'operator.itemgetter'>, <type 'operator.attrgetter'>, <type 'operator.methodcaller'>, <type 'collections.deque'>, <type 'deque_iterator'>, <type 'deque_reverse_iterator'>, <type
'itertools.combinations'>, <type 'itertools.combinations_with_replacement'>, <type 'itertools.cycle'>, <type 'itertools.dropwhile'>, <type 'itertools.takewhile'>, <type 'itertools.islice'>, <type
'itertools.starmap'>, <type 'itertools.imap'>, <type 'itertools.chain'>, <type 'itertools.compress'>, <type 'itertools.ifilter'>, <type 'itertools.ifilterfalse'>, <type 'itertools.count'>, <type 'itertools.izip'>, <type 'itertools.izip_longest'>, <type 'itertools.permutations'>, <type 'itertools.product'>, <type
'itertools.repeat'>, <type 'itertools.groupby'>, <type 'itertools.tee_dataobject'>, <type 'itertools.tee'>, <type 'itertools._grouper'>, <type '_thread._localdummy'>, <type 'thread._local'>, <type 'thread.lock'>, <class 'sandbox.Protection'>, <type 'resource.struct_rusage'>, <class 'sandbox.config.SandboxConfig'>, <class 'sandbox.proxy.ReadOnlySequence'>, <class 'sandbox.sandbox_class.Sandbox'>, <class
'sandbox.restorable_dict.RestorableDict'>]
All of the subclasses
of
object!
>>> [].__class__.__bases__
(<type 'object'>,)
>>> [].__class__.__bases__[0]
<type 'object'>
>>>
obj_class
= [].__class__.__bases__[0]
>>>
for
c
in
obj_class.__subclasses__():
...
c.__name__
...
wrapper_descriptor
instance
ellipsis
member_descriptor
file
PyCapsule
cell
callable-iterator
iterator
...
>>> [].__class__.__bases__
(<type 'object'>,)
>>> [].__class__.__bases__[0]
<type 'object'>
>>> obj_class = [].__class__.__bases__[0]
>>> for c in obj_class.__subclasses__():
... print c.__name__
...
wrapper_descriptor
instance
ellipsis
member_descriptor
file
PyCapsule
cell
callable-iterator
iterator
...
!!!
from sandbox import Sandbox
s
= Sandbox()
code
=
"""
obj_class = [].__class__.__bases__[0]
obj_subclasses = dict((elt.__name__, elt) for \
elt in obj_class.__subclasses__())
func = obj_subclasses["file"]
func("text.txt", "w").write("Kaboom!\\n")
"""
s.execute(code)
Testing: read-only builtins
Idea: don’t expose
dangerous
>>> type.__bases__
(<type 'object'>,)
>>>
del
type.__bases__
>>> type.__bases__
(<type 'object'>,)
>>> del type.__bases__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't set attributes of
built-in/extension type 'type'
Let’s delete
__bases__
and
__subclasses__
Imposed by the underlying
C implementation!
from ctypes import pythonapi, POINTER, py_object
_get_dict
= pythonapi._PyObject_GetDictPtr
_get_dict.restype = POINTER(py_object)
_get_dict.argtypes = [py_object]
del pythonapi, POINTER, py_object
def dictionary_of(ob):
dptr
= _get_dict(ob)
return dptr.contents.value
cpython.py
from
cpython
import
dictionary_of
main
= sys.modules[
"__main__"
].__dict__
...
safe_builtins
= ReadOnlyBuiltins(
original_builtins)
main[
"__builtins__"
] = safe_builtins
type_dict
= dictionary_of(type)
del
type_dict[
"__bases__"
]
Circumvention idea:
would a function by any
other name smell as
>>>
def
foo():
...
"Meow"
...
>>> dir(foo)
[
'__call__'
,
'__class__'
,
'__closure__'
,
'__code__'
,
'__defaults__'
,
'__delattr__'
,
'__dict__'
,
'__doc__'
,
'__format__'
,
'__get__'
,
'__getattribute__'
,
'__globals__'
,
'__hash__'
,
'__init__'
,
'__module__'
,
'__name__'
,
'__new__'
,
'__reduce__'
,
'__reduce_ex__'
,
'__repr__'
,
'__setattr__'
,
'__sizeof__'
,
'__str__'
,
'__subclasshook__'
,
'func_closure'
,
'func_code'
,
'func_defaults'
,
'func_dict'
,
'func_doc'
,
>>>
def
foo():
...
"Meow"
...
>>> dir(foo)
[
'__call__'
,
'__class__'
,
'__closure__'
,
'__code__'
,
'__defaults__'
,
'__delattr__'
,
'__dict__'
,
'__doc__'
,
'__format__'
,
'__get__'
,
'__getattribute__'
,
'__globals__'
,
'__hash__'
,
'__init__'
,
'__module__'
,
'__name__'
,
'__new__'
,
'__reduce__'
,
'__reduce_ex__'
,
'__repr__'
,
'__setattr__'
,
'__sizeof__'
,
'__str__'
,
'__subclasshook__'
,
'func_closure'
,
'func_code'
,
'func_defaults'
,
'func_dict'
,
'func_doc'
,
'func_globals'
,
'func_name'
]
>>> foo.func_code
<code object foo at 0x100509d30, file "<stdin>",
line 1>
>>> dir(foo.func_code)
[
'__class__', '__cmp__', '__delattr__', '__doc__',
'__eq__', '__format__', '__ge__',
'__getattribute__', '__gt__', '__hash__',
'__init__', '__le__', '__lt__', '__ne__',
'__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', 'co_argcount',
'co_cellvars', 'co_code', 'co_consts',
'co_filename', 'co_firstlineno', 'co_flags',
'co_freevars', 'co_lnotab', 'co_name', 'co_names',
'co_nlocals', 'co_stacksize', 'co_varnames'
]
>>> foo.func_code.co_code
'd\x01\x00GHd\x00\x00S'
>>>
def
foo():
...
"Meow"
...
>>>
def
evil_function():
...
"Kaboom!"
...
>>> foo()
Meow
>>> foo.__setattr__(
"func_code"
,
evil_function.func_code)
>>> foo()
Kaboom!
Kaboom
Idea redux: don’t
expose dangerous
from
cpython
import
dictionary_of
from
types
import
FunctionType
...
type_dict
= dictionary_of(type)
del
type_dict[
"__bases__"
]
del
type_dict[
"__subclasses__"
]
function_dict
= dictionary_of(FunctionType)
del
function_dict[
"func_code"
]
Whew. Let’s recap tactics:
•
Keyword blacklist
•
Builtins whitelist
•
Import whitelist
•
Making important objects read-only (builtins)
•
Deleting problematic implementation details
(
__bases__
,
__subclasses__,
func_code
)
•
Deleting the ability to construct arbitrary
We have run out of
tricks!
We’ve implemented 80% of a full-fledged
Python sandbox
builtins_whitelist = set((
# exceptions 'ArithmeticError', 'AssertionError', 'AttributeError', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'EnvironmentError', 'Exception', 'FloatingPointError','FutureWarning', 'GeneratorExit', 'IOError', 'ImportError',
'ImportWarning', 'IndentationError', 'IndexError', 'KeyError','LookupError', 'MemoryError', 'NameError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError','PendingDeprecationWarning', 'ReferenceError', 'RuntimeError',
'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'TabError', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError','UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', # constants
'False', 'None', 'True', '__doc__', '__name__', '__package__', 'copyright', 'license', 'credits',
# types
'basestring', 'bytearray', 'bytes', 'complex', 'dict', 'float', 'frozenset', 'int', 'list', 'long', 'object', 'set', 'str', 'tuple', 'unicode',
# functions '__import__', 'abs', 'all', 'any', 'apply', 'bin', 'bool', 'buffer', 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile', 'delattr', 'dir', 'divmod', 'enumerate', 'filter', 'format', 'getattr', 'globals', 'hasattr', 'hash', 'hex', 'id', 'isinstance', 'issubclass', 'iter', 'len', 'locals', 'map', 'max', 'min', 'next', 'oct', 'ord', 'pow', 'print', 'property', 'range', 'reduce', 'repr', 'reversed', 'round', 'setattr', 'slice', 'sorted', 'staticmethod', 'sum', 'super', 'type', 'unichr', 'vars', 'xrange', 'zip',
))
def _safe_import(__import__, module_whitelist):
def safe_import(module_name, globals={}, locals={}, fromlist=[], level=-1): if module_name in module_whitelist:
return __import__(module_name, globals, locals, fromlist, level) else:
raise ImportError("Blocked import of %s" % (module_name,)) return safe_import
class ReadOnlyBuiltins(dict): def clear(self):
ValueError("Read-only!") def __delitem__(self, key): ValueError("Read-only!") def pop(self, key, default=None): ValueError("Read-only!") def popitem(self):
ValueError("Read-only!") def setdefault(self, key, value):
! ValueError("Read-only!") def __setitem__(self, key, value): ValueError("Read-only!") def update(self, dict, **kw): ValueError("Read-only!")
class Sandbox(object): def __init__(self): ! import sys
! from types import FunctionType
! from cpython import dictionary_of
! original_builtins = sys.modules["__main__"].__dict__["__builtins__"].__dict__
! for builtin in original_builtins.keys(): if builtin not in builtins_whitelist:
! ! del sys.modules["__main__"].__dict__["__builtins__"].__dict__[builtin]
original_builtins["__import__"] = _safe_import(__import__, ["string", "re"])
safe_builtins = ReadOnlyBuiltins(original_builtins) sys.modules["__main__"].__dict__["__builtins__"] = safe_builtins ! type_dict = dictionary_of(type) ! del type_dict["__bases__"] ! del type_dict["__subclasses__"] ! function_dict = dictionary_of(FunctionType) ! del function_dict["func_code"]
def execute(self, code_string): ! exec code_string