Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python - Creating a "scripting" system

I'm making a wxpython app that I will compile with the various freezing utility out there to create an executable for multiple platforms.

the program will be a map editer for a tile-based game engine

in this app I want to provide a scripting system so that advanced users can modify the behavior of the program such as modifying project data, exporting the project to a different format ect.

I want the system to work like so.

the user will place the python script they wish to run into a styled textbox and then press a button to execute the script.

I'm good with this so far thats all really simple stuff. obtain the script from the text-box as a string compile it to a cod object with the inbuilt function compile() then execute the script with an exec statment

script = textbox.text #bla bla store the string
code = compile(script, "script", "exec") #make the code object
eval(code, globals())

the thing is, I want to make sure that this feature can't cause any errors or bugs
say if there is an import statement in the script. will this cause any problems taking into account that the code has been compiled with something like py2exe or py2app?
how do I make sure that the user can't break critical part of the program like modifying part of the GUI while still allowing them to modify the project data (the data is held in global properties in it's own module)? I think that this would mean modifying the globals dict that is passed to the eval function.
how to I make sure that this eval can't cause the program to hang due to a long or infinite loop? how do I make sure that an error raised inside the user's code can't crash the whole app?

basically, how to I avoid all those problems that can arise when allowing the user to run their own code?

EDIT: Concerning the answers given

I don't feel like any of the answers so far have really answered my questions yes they have been in part answered but not completely. I'm well aware the it is impossible to completely stop unsafe code. people are just too clever for one man (or even a teem) to think of all the ways to get around a security system and prevent them.

in fact I don't really care if they do. I'm more worried about some one unintentional breaking something they didn't know about. if some one really wanted to they could tear the app to shreds with the scripting functionality, but I couldn't care less. it will be their instance and all the problem they create will be gone when they restart the app unless they have messed with files on the HD. I want to prevent the problems that arise when the user dose something stupid.
things like IOError's, SystaxErrors, InfiniteLoopErrors ect.

now the part about scope has been answered. I now understand how to define what functions and globals can be accessed from the eval function but is there a way to make sure that the execution of their code can be stopped if it is taking too long?
a green thread system perhaps? (green because it would be eval to make users worry about thread safety)

also if a users uses an import module statement to load a module from even the default library that isn't used in the rest of the class. could this cause problems with the app being frozen by Py2exe, Py2app, or Freeze? what if they call a modal out side of the standard library? would it be enough that the modal is present in the same directory as the frozen executable?

I would like to get these answers with out creating a new question but I will if I must.

like image 210
Ryex Avatar asked Oct 14 '10 22:10

Ryex


2 Answers

Easy answer: don't.

You can forbid certain keywords (import) and operations, and accesses to certain data structures, but ultimately you're giving your power users quite a bit of power. Since this is for a rich client that runs on the user's machine, a malicious user can crash or even trash the whole app if they really feel like it. But it's their instance to crash. Document it well and tell people what not to touch.

That said, I've done this sort of thing for web apps that execute user input and yes, call eval like this:

eval(code, {"__builtins__":None}, {safe_functions})

where safe_functions is a dictionary containing {"name": func} type pairs of functions you want your users to be able to access. If there's some essential data structure that you're positive your users will never want to poke at, just pop it out of globals before passing them in.

Incidentally, Guido addressed this issue on his blog a while ago. I'll see if I can find it.

Edit: found.

like image 125
nmichaels Avatar answered Sep 21 '22 13:09

nmichaels


Short Answer: No

  • Is using eval in Python a bad practice?

Other related posts:

  • Safety of Python 'eval' For List Deserialization

It is not easy to create a safety net. The details too many and clever hacks are around:

  • Python: make eval safe

On your design goals:

It seems you are trying to build an extensible system by providing user to modify a lot of behavior and logic.

Easiest option is to ask them to write a script which you can evaluate (eval) during the program run.

How ever, a good design describes , scopes the flexibility and provides scripting mechanism through various design schemes ranging from configuration, plugin to scripting capabilities etc. The scripting apis if well defined can provide more meaningful extensibility. It is safer too.

like image 42
pyfunc Avatar answered Sep 22 '22 13:09

pyfunc