I'm creating a GUI application that can monitor and manipulate a stream of messages. I'm trying to create a simple means to let the user script some of this functionality and I'm looking for possible candidates. Initially I wanted to use XML since it can naturally take care of embedded code:
<if>
<condition>
<recv>
<MesgTypeA/>
</recv>
</condition>
<loop count=10>
<send>
<MesgTypeB>
<param1>12</param1>
<param2>52</param2>
</MesgTypeB>
</send>
</loop>
</if>
For parsing I was planning on using ElementTree and just build states out of the code. Writing and reading XML isn't the easiest thing to do, especially since I can't assume that the writers of the script will have any sort of experience. I was wondering if anyone had any alternatives that is easier to read/write and process in Python. I looked into JSON but because it's a script, order matters.
Can anyone suggest any possible alternatives?
Thanks.
How about Python itself?
For example:
>>> import code
>>> def host_func():
... print("Hello old chap!")
...
>>> c = code.compile_command("print(\"Script says hello!\"); host_func()")
>>> exec(c)
Script says hello!
Hello old chap!
exec
let's you be explicit about what from the host environment you want to expose through the two optional parameters locals
and globals
.
In this example I am being explicit about what globals the script will have access to. Note that I can "create" variables here, or give existing functions another name. It's a dictionary pointing to functions and data.
>>> import code
>>> def secret():
... print("What?! I don't even... get out of here.")
...
>>> def public():
... print("Hello stranger.")
...
>>> c = code.compile_command("secret(); public()")
Calling this with globals containing two functions, pointing back to the already existing ones gives:
>>> exec(c, {"secret": secret, "public": public})
What?! I don't even... get out of here.
Hello stranger.
Now when I omit secret
, the script can no longer find it.
>>> exec(c, {"public": public})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<input>", line 1, in <module>
NameError: name 'secret' is not defined
Here I redefine secret
all together:
>>> exec(c, {"public": public, "secret":lambda: print("Haha! Doppelganger.")})
Haha! Doppelganger.
Hello stranger.
As lazyr mentions in the comments there are security concerns. The above examples let the script pretty much do what it want. In some cases this is not acceptable.
There is some stuff one can do to discourage it:
__builtins__
, only allow "white-listed" built-in functions.For example, here's how you bork the import
statement (in Py2.* builtins is __builtins__
):
>>> import builtins
>>> def no_import(*args, **kwargs):
... raise ImportError("I cannot let you do that, Dave.")
...
>>> builtins.__import__ = no_import
>>> import os
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in no_import
ImportError: I cannot let you do that, Dave.
From this follows that we can pass our own builtins
in the globals parameter:
>>> import code
>>> evil_code = "import os; import stat; os.chmod(\"passwords.txt\", stat.S_IROT
H);"
>>> compiled = code.compile_command(evil_code)
>>> def no_import(*args, **kwargs):
... raise ImportError("I cannot let you do that, Dave.")
...
>>> exec(compiled, {"__builtins__": {"__import__": no_import}})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<input>", line 1, in <module>
File "<stdin>", line 2, in no_import
ImportError: I cannot let you do that, Dave.
A caveat, though, this will bork all imports that happen after it. It may be better to replace it with a version that lets you import whitelisted modules.
And finally, I'm not sure this completely guards you. Some crafty person may well circumvent it. But the most blatant infractions should at least be discouraged.
Python or maybe Lisp because the syntax is easy to parse.
You could define your own scripting language syntax with pyparsing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With