Added a command recursion limit to cmdhandler, raising a clean exception if a command calls other commands to a too nested level (10 levels down, if you use commands to that nesting level you are doing something wrong anyway). See #765.
This commit is contained in:
parent
9478177e55
commit
5d32d4f94e
1 changed files with 25 additions and 1 deletions
|
|
@ -35,6 +35,7 @@ command line. The processing of a command works as follows:
|
|||
|
||||
"""
|
||||
|
||||
from collections import defaultdict
|
||||
from weakref import WeakValueDictionary
|
||||
from copy import copy
|
||||
from traceback import format_exc
|
||||
|
|
@ -50,6 +51,11 @@ __all__ = ("cmdhandler",)
|
|||
_GA = object.__getattribute__
|
||||
_CMDSET_MERGE_CACHE = WeakValueDictionary()
|
||||
|
||||
# tracks recursive calls by each caller
|
||||
# to avoid infinite loops (commands calling themselves)
|
||||
_COMMAND_NESTING = defaultdict(lambda: 0)
|
||||
_COMMAND_RECURSION_LIMIT = 10
|
||||
|
||||
# This decides which command parser is to be used.
|
||||
# You have to restart the server for changes to take effect.
|
||||
_COMMAND_PARSER = utils.variable_from_module(*settings.COMMAND_PARSER.rsplit('.', 1))
|
||||
|
|
@ -92,6 +98,9 @@ _ERROR_CMDHANDLER = "{traceback}\n"\
|
|||
"Above traceback is from a Command handler bug." \
|
||||
"Please file a bug report with the Evennia project."
|
||||
|
||||
_ERROR_RECURSION_LIMIT = "Command recursion limit ({recursion_limit}) " \
|
||||
"reached for '{raw_string}' ({cmdclass})."
|
||||
|
||||
|
||||
def _msg_err(receiver, string):
|
||||
"""
|
||||
|
|
@ -111,7 +120,6 @@ class NoCmdSets(Exception):
|
|||
"No cmdsets found. Critical error."
|
||||
pass
|
||||
|
||||
|
||||
class ExecSystemCommand(Exception):
|
||||
"Run a system command"
|
||||
def __init__(self, syscmd, sysarg):
|
||||
|
|
@ -324,6 +332,7 @@ def get_and_merge_cmdsets(caller, session, player, obj,
|
|||
|
||||
# Main command-handler function
|
||||
|
||||
|
||||
@inlineCallbacks
|
||||
def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sessid=None, **kwargs):
|
||||
"""
|
||||
|
|
@ -372,10 +381,16 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
|
|||
cmd (Command): command object
|
||||
cmdname (str): name of command
|
||||
args (str): extra text entered after the identified command
|
||||
|
||||
Returns:
|
||||
deferred (Deferred): this will fire with the return of the
|
||||
command's `func` method.
|
||||
|
||||
Raises:
|
||||
RuntimeError: If command recursion limit was reached.
|
||||
|
||||
"""
|
||||
global _COMMAND_NESTING
|
||||
try:
|
||||
# Assign useful variables to the instance
|
||||
cmd.caller = caller
|
||||
|
|
@ -403,6 +418,13 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
|
|||
for key, val in kwargs.items():
|
||||
setattr(cmd, key, val)
|
||||
|
||||
_COMMAND_NESTING[called_by] += 1
|
||||
if _COMMAND_NESTING[called_by] > _COMMAND_RECURSION_LIMIT:
|
||||
err = _ERROR_RECURSION_LIMIT.format(recursion_limit=_COMMAND_RECURSION_LIMIT,
|
||||
raw_string=unformatted_raw_string,
|
||||
cmdclass=cmd.__class__)
|
||||
raise RuntimeError(err)
|
||||
|
||||
# pre-command hook
|
||||
abort = yield cmd.at_pre_cmd()
|
||||
if abort:
|
||||
|
|
@ -425,6 +447,8 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
|
|||
caller.ndb.last_cmd = yield copy(cmd)
|
||||
else:
|
||||
caller.ndb.last_cmd = None
|
||||
_COMMAND_NESTING[called_by] -= 1
|
||||
|
||||
# return result to the deferred
|
||||
returnValue(ret)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue