views:

181

answers:

7

I'm developing a web game in pure Python, and want some simple scripting available to allow for more dynamic game content. Game content can be added live by privileged users.

It would be nice if the scripting language could be Python. However, it can't run with access to the environment the game runs on since a malicious user could wreak havoc which would be bad. Is it possible to run sandboxed Python in pure Python?

Update: In fact, since true Python support would be way overkill, a simple scripting language with Pythonic syntax would be perfect.

If there aren't any Pythonic script interpreters, are there any other open source script interpreters written in pure Python that I could use? The requirements are support for variables, basic conditionals and function calls (not definitions).

A: 

You'll find some ideas in this wiki page, but it does not look like it can be done easily.

Fabian
Yup, I read that, but none of the solutions were pure Python. I guess running true Python sandboxed might be over-kill anyways, but I would like a Python-like scripting language.
Blixt
+1  A: 

Hmm. This is a thought experiment, I don't know of it being done:

You could use the compiler package to parse the script. You can then walk this tree, prefixing all identifiers - variables, method names e.t.c. (also has|get|setattr invocations and so on) - with a unique preamble so that they cannot possibly refer to your variables. You could also ensure that the compiler package itself was not invoked, and perhaps other blacklisted things such as opening files. You then emit the python code for this, and compiler.compile it.

The docs note that the compiler package is not in Python 3.0, but does not mention what the 3.0 alternative is.

In general, this is parallel to how forum software and such try to whitelist 'safe' Javascript or HTML e.t.c. And they historically have a bad record of stomping all the escapes. But you might have more luck with Python :)

Will
+1  A: 

You'll probably be interested in the Python language services section of the libref for writing your own parser.

Ignacio Vazquez-Abrams
+2  A: 

AFAIK it is possible to run a code in a completely isolated environment:

exec somePythonCode in {'__builtins__': {}}, {}

But in such environment you can do almost nothing :) (you can not even import a module; but still a malicious user can run an infinite recursion or cause running out of memory.) Probably you would want to add some modules that will be the interface to you game engine.

Messa
Hm, interesting. I'll try it out! Since all code is already sandboxed from the system (I'm developing on GAE), I can detect an infinite recursion/heavy memory usage and stop the script from being run again.
Blixt
+1  A: 

I think your best bet is going to be a combination of the replies thus far.

You'll want to parse and sanitise the input - removing any import statements for example.

You can then use Messa's exec sample (or something similar) to allow the code execution against only the builtin variables of your choosing - most likely some sort of API defined by yourself that provides the programmer access to the functionality you deem relevant.

Glenjamin
I totally concur. This does seem to be the right way to go. I'm sceptical about how much you can accomplish though.
Noufal Ibrahim
Hmm, which cases would I need to sanitize the input using Messa's method? I've tried to import modules or otherwise access external values, but it doesn't seem easy. Import statements etc. are already disabled since no built-in functions are available (the `import` statement calls the `__import__` function).
Blixt
You should try to fish out the thread on Python-dev discussing this. It had everyone break the sandbox. Lots of ways there. I can't find it.
Noufal Ibrahim
+4  A: 

This is really non-trivial.

There are two ways to sandbox Python. One is to create a restricted environment (i.e., very few globals etc.) and exec your code inside this environment. This is what Messa is suggesting. It's nice but there are lots of ways to break out of the sandbox and create trouble. There was a thread about this on Python-dev a year ago or so in which people did things from catching exceptions and poking at internal state to break out to byte code manipulation. This is the way to go if you want a complete language.

The other way is to parse the code and then use the ast module to kick out constructs you don't want (e.g. import statements, function calls etc.) and then to compile the rest. This is the way to go if you want to use Python as a config language etc.

Another way (which might not work for you since you're using GAE), is the PyPy sandbox. While I haven't used it myself, word on the intertubes is that it's the only real sandboxed Python out there.

Based on your description of the requirements (The requirements are support for variables, basic conditionals and function calls (not definitions)) , you might want to evaluate approach 2 and kick out everything else from the code. It's a little tricky but doable.

Noufal Ibrahim
Hmm yeah I was thinking about what would happen if you start digging in code objects... I guess you can escape the `exec` that way... PyPy is what Google App Engine is using already though, isn't it? I wonder if the pure Python version of PyPy can run in GAE... I'll mess around with it a bit.
Blixt
I think GAE has a variant of unalden swallow. It's not PyPY AFAIK.
Noufal Ibrahim
Do you think this code is a good start? http://code.activestate.com/recipes/496746/
Blixt
Can't give you a total guarantee but a cursory look tells me that it's decent code. One place I know which does this in "production" is the Templetor templating engine used by web.py. You might want to take a look at that.
Noufal Ibrahim
+1  A: 

I would look into a two server approach. The first server is the privileged web server where your code lives. The second server is a very tightly controlled server that only provides a web service or RPC service and runs the untrusted code. You provide your content creator with your custom interface. For example you if you allowed the end user to create items, you would have a look up that called the server with the code to execute and the set of parameters.

Here's and abstract example for a healing potion.

{function_id='healing potion', action='use', target='self', inventory_id='1234'}

The response might be something like

{hp='+5' action={destroy_inventory_item, inventory_id='1234'}}
Philip T.
Yeah, my game already has an RPC API, I just want certain events, when a player is playing, to be more dynamic... So scripting feels like a natural choice :) I guess that worst case scenario is that I'll have to make a simple interpreter myself.
Blixt
You wouldn't necessarily need to create a complex API. You could do something as simple as serializing a data structure passing it to the RPC server (Running Python), which would load the structure and run the end user code (Python). The end user modifies it and sends it back. Regardless, you are going to have to create guidelines as to how to access your data.
Philip T.
+1 for a new way of solving the problem.
Noufal Ibrahim