I have two questions regarding Pysandbox:
How do I achieve the functionality of eval
? I understand sandbox.execute()
is equivalent to exec
, but I can't find anything such that if the code entered were 2 + 2
, then it would return 4
, or something to that effect.
By default, sandbox.execute()
makes a passed-in environment read-only -- i.e. if I do sandbox.execute('data.append(4)', locals={'data': [1, 2, 3]})
, an error will occur. How do I make passed-in environments read-write?
Aha, I see that the last PySandbox release (1.5) does behave as you describe. I was trying on the git master branch.
For question 1, I don't think there are any straightforward options with PySandbox 1.5. The code has been written specifically to try and disallow modification or transmission of any state while inside the sandbox. sandbox.call(eval, CODE)
is not a good option, since you can't very easily pass in the locals and globals namespaces (you can try, but the sandbox will proxy them, and then eval() will fail because locals and globals have to be real dicts). And I believe you want to be able to set the locals and globals here.
Question 2 isn't straightforward either, for similar reasons. Both of these are possible on the current git master branch, but it's not obvious whether that's because of bugs or by design :)
So here's a solution to both questions which sort of extends the sandbox functionality and uses what is probably meant to be a private interface, but should still be as safe as the sandbox is otherwise:
from sandbox.proxy import proxy
from sandbox.sandbox_class import _call_exec
def proxyNamespace(d):
return dict((str(k), proxy(v)) for k, v in d.iteritems())
def wrapeval(codestr, globs, locs):
subglobs = proxyNamespace(globs)
sublocs = proxyNamespace(locs)
return eval(codestr, subglobs, sublocs)
def eval_in_sandbox(sandbox, codestr, globs=None, locs=None):
if globs is None:
globs = {}
if locs is None:
locs = globs
return sandbox._call(wrapeval, (codestr, locs, globs), {})
def exec_in_sandbox_with_mutable_namespace(sandbox, codestr, globs=None, locs=None):
if globs is None:
globs = {}
if locs is None:
locs = globs
subglobs = proxyNamespace(globs)
sublocs = proxyNamespace(locs)
sandbox._call(_call_exec, (codestr, subglobs, sublocs), {})
globs.update(subglobs)
locs.update(sublocs)
Now, all that said, I strongly recommend not basing the safety of any actual production code or system on this PySandbox module. In looking through the code, it seems like it's completely riddled with flaws and holes. I don't think it would take an attacker longer than an hour or so to find ways past this sort of security. I also don't think this has undergone any serious review by security professionals, or even a very big part of the community at large. If you want to run untrusted code and have it not be able to affect your process at all, you need something lots stronger, probably based around AppArmor or something like that. If you are only worried about accidentally misbehaving code, as opposed to malicious, this might be ok, but I'm actually not even sure about that.