evennia/contrib/evlang/command.py
Griatch 58e20e2cf1 Added contrib "evlang", an experimental highly restricted Python code environment. It's intended to be used by untrusted users to add custom code e.g. to their crafted objects and similar. Please heed the warnings in the README file - this is experimental still and more people need to play with it and try to break it.
The system uses a hybrid blacklisting/whitelisting and AST-traversal approach to both remove dangerous builtins as well as disallow potentially exploitable python structures alltogether. Examples are while structures and attribute allocation. All advanced functionality is accessed through a set of "safe" methods on a holder object. You can extend this with your own safe methods in order to add more functionality befitting your game.

The system comes with a host of examples, a few scriptable objects and complete commands for adding code to objects. At this point it's not guaranteed that all systems are safe against meddling however - notably Attributes have no locks defined on them by default (although this system does properly check Attribute lock types should they exixt).

Please test and try to break - and report problems to the Issue tracker/forum as usual.
2012-06-10 22:16:46 +02:00

136 lines
4.8 KiB
Python

"""
Evlang usage examples
Commands for use with evlang
Evennia contribution - Griatch 2012
The @code command allows to add scripted evlang code to
a ScriptableObject. It will handle access checks.
"""
from ev import utils
from ev import default_cmds
#------------------------------------------------------------
# Evlang-related commands
#
# Easiest is to add this command to the default cmdset.
# Alternatively one could imagine storing it directly only
# on scriptable objects.
#------------------------------------------------------------
class CmdCode(default_cmds.MuxCommand):
"""
add custom code to a scriptable object
Usage:
@code[/switch] <obj>[/<type> [= <codestring> ]]
Switch:
delete - clear code of given type from the object.
debug - immediately run the given code after adding it.
This will add custom scripting to an object
which allows such modification.
<type> must be one of the script types allowed
on the object. Only supplying the command will
return a list of script types possible to add
custom scripts to.
"""
key = "@code"
locks = "cmd:perm(Builders)"
help_category = "Building"
def func(self):
"implements the functionality."
caller = self.caller
if not self.args:
caller.msg("Usage: @code <obj>[/<type> [= <codestring>]]")
return
codetype = None
objname = self.lhs
if '/' in self.lhs:
objname, codetype = [part.strip() for part in self.lhs.rsplit("/", 1)]
obj = self.caller.search(objname)
if not obj:
return
# get the dicts from db storage for easy referencing
evlang_scripts = obj.db.evlang_scripts
evlang_locks = obj.db.evlang_locks
if not (evlang_scripts != None and evlang_locks and obj.ndb.evlang):
caller.msg("Object %s can not be scripted." % obj.key)
return
if 'delete' in self.switches:
# clearing a code snippet
if not codetype:
caller.msg("You must specify a code type.")
return
if not codetype in evlang_scripts:
caller.msg("Code type '%s' not found on %s." % (codetype, obj.key))
return
# this will also update the database
obj.ndb.evlang.delete(codetype)
caller.msg("Code for type '%s' cleared on %s." % (codetype, obj.key))
return
if not self.rhs:
if codetype:
scripts = [(name, tup[1], utils.crop(tup[0])) for name, tup in evlang_scripts.items() if name==codetype]
scripts.extend([(name, "--", "--") for name in evlang_locks if name not in evlang_scripts if name==codetype])
else:
# no type specified. List all scripts/slots on object
print evlang_scripts
scripts = [(name, tup[1], utils.crop(tup[0])) for name, tup in evlang_scripts.items()]
scripts.extend([(name, "--", "--") for name in evlang_locks if name not in evlang_scripts])
scripts = sorted(scripts, key=lambda p: p[0])
table = [["type"] + [tup[0] for tup in scripts],
["creator"] + [tup[1] for tup in scripts],
["code"] + [tup[2] for tup in scripts]]
ftable = utils.format_table(table, extra_space=5)
string = "{wEvlang scripts on %s:{n" % obj.key
for irow, row in enumerate(ftable):
if irow == 0:
string += "\n" + "".join("{w%s{n" % col for col in row)
else:
string += "\n" + "".join(col for col in row)
caller.msg(string)
return
# we have rhs
codestring = self.rhs
if not codetype in evlang_locks:
caller.msg("Code type '%s' cannot be coded on %s." % (codetype, obj.key))
return
# check access with the locktype "code"
if not obj.ndb.evlang.lockhandler.check(caller, "code"):
caller.msg("You are not permitted to add code of type %s." % codetype)
return
# we have code access to this type.
oldcode = None
if codetype in evlang_scripts:
oldcode = str(evlang_scripts[codetype][0])
# this updates the database right away too
obj.ndb.evlang.add(codetype, codestring, scripter=caller)
if oldcode:
caller.msg("{wReplaced{n\n %s\n{wWith{n\n %s" % (oldcode, codestring))
else:
caller.msg("Code added in '%s':\n %s" % (codetype, codestring))
if "debug" in self.switches:
# debug mode
caller.msg("{wDebug: running script (look out for errors below) ...{n\n" + "-"*68)
obj.ndb.evlang.run_by_name(codetype, caller, quiet=False)