Add is_restricted boolean check for commands
[mudpy.git] / mudpy / command.py
index 20b448c..279da78 100644 (file)
@@ -1,6 +1,6 @@
 """User command functions for the mudpy engine."""
 
-# Copyright (c) 2004-2019 mudpy authors. Permission to use, copy,
+# Copyright (c) 2004-2020 mudpy authors. Permission to use, copy,
 # modify, and distribute this software is granted under terms
 # provided in the LICENSE file distributed with this software.
 
@@ -23,6 +23,7 @@ def chat(actor, parameters):
         actor.send("Exiting chat mode.")
     else:
         actor.send("Sorry, but you're already busy with something else!")
+    return True
 
 
 def create(actor, parameters):
@@ -57,6 +58,7 @@ def create(actor, parameters):
         elif len(arguments) > 2:
             message = "You can only specify an element and a filename."
     actor.send(message)
+    return True
 
 
 def delete(actor, parameters):
@@ -84,6 +86,7 @@ def delete(actor, parameters):
                            + '". Try "show element ' +
                            element + '" for verification.')
     actor.send(message)
+    return True
 
 
 def destroy(actor, parameters):
@@ -105,13 +108,16 @@ def destroy(actor, parameters):
                     6
                 )
         actor.send(message)
+    return True
 
 
 def error(actor, input_data):
     """Generic error for an unrecognized command word."""
 
     # 90% of the time use a generic error
-    if random.randrange(10):
+    # Allow the random.randrange() call in bandit since it's not used for
+    # security/cryptographic purposes
+    if random.randrange(10):  # nosec
         message = '''I'm not sure what "''' + input_data + '''" means...'''
 
     # 10% of the time use the classic diku error
@@ -125,6 +131,7 @@ def error(actor, input_data):
         mudpy.misc.log(
             'Sending a command error to user %s raised exception...\n%s' % (
                 actor.owner.account.get("name"), traceback.format_exc()))
+    return True
 
 
 def halt(actor, parameters):
@@ -145,6 +152,7 @@ def halt(actor, parameters):
 
         # set a flag to terminate the world
         actor.universe.terminate_flag = True
+    return True
 
 
 def help(actor, parameters):
@@ -163,7 +171,7 @@ def help(actor, parameters):
             description = command.get("description")
             if not description:
                 description = "(no short description provided)"
-            if command.get("administrative"):
+            if command.is_restricted():
                 output = "$(red)"
             else:
                 output = "$(grn)"
@@ -186,7 +194,7 @@ def help(actor, parameters):
                         if actor.can_run(command):
                             if really_see_also:
                                 really_see_also += ", "
-                            if command.get("administrative"):
+                            if command.is_restricted():
                                 really_see_also += "$(red)"
                             else:
                                 really_see_also += "$(grn)"
@@ -236,7 +244,7 @@ def help(actor, parameters):
                     "description", "(no short description provided)")
 
                 # administrative command names are in red, others in green
-                if command.get("administrative"):
+                if command.is_restricted():
                     color = "red"
                 else:
                     color = "grn"
@@ -251,6 +259,7 @@ def help(actor, parameters):
 
     # send the accumulated output to the user
     actor.send(output)
+    return True
 
 
 def look(actor, parameters):
@@ -259,6 +268,7 @@ def look(actor, parameters):
         actor.send("You can't look at or in anything yet.")
     else:
         actor.look_at(actor.get("location"))
+    return True
 
 
 def move(actor, parameters):
@@ -269,6 +279,7 @@ def move(actor, parameters):
             actor.move_direction(portal)
             return(portal)
     actor.send("You cannot go that way.")
+    return True
 
 
 def preferences(actor, parameters):
@@ -280,22 +291,31 @@ def preferences(actor, parameters):
     message = ""
     arguments = parameters.split()
     allowed_prefs = set()
+    base_prefs = []
     user_config = actor.universe.contents.get("mudpy.user")
     if user_config:
-        allowed_prefs.update(user_config.get("pref_allow", []))
+        base_prefs = user_config.get("pref_allow", [])
+        allowed_prefs.update(base_prefs)
         if actor.owner.account.get("administrator"):
             allowed_prefs.update(user_config.get("pref_admin", []))
     if not arguments:
         message += "These are your current preferences:"
-        for pref in allowed_prefs:
-            message += ("$(eol)   $(red)%s $(grn)%s$(nrm)"
-                        % (pref, actor.owner.account.get(pref)))
+
+        # color-code base and admin prefs
+        for pref in sorted(allowed_prefs):
+            if pref in base_prefs:
+                color = "grn"
+            else:
+                color = "red"
+            message += ("$(eol)   $(%s)%s$(nrm) - %s" % (
+                color, pref, actor.owner.account.get(pref, "<not set>")))
+
     elif arguments[0] not in allowed_prefs:
         message += (
             'Preference "%s" does not exist. Try the `preferences` command by '
             "itself for a list of valid preferences." % arguments[0])
     elif len(arguments) == 1:
-        message += "%s" % actor.owner.account.get(arguments[0])
+        message += "%s" % actor.owner.account.get(arguments[0], "<not set>")
     else:
         pref = arguments[0]
         value = " ".join(arguments[1:])
@@ -307,6 +327,7 @@ def preferences(actor, parameters):
                 'Preference "%s" cannot be set to type "%s".' % (
                     pref, type(value)))
     actor.send(message)
+    return True
 
 
 def quit(actor, parameters):
@@ -314,6 +335,7 @@ def quit(actor, parameters):
     if actor.owner:
         actor.owner.state = "main_utility"
         actor.owner.deactivate_avatar()
+    return True
 
 
 def reload(actor, parameters):
@@ -330,6 +352,7 @@ def reload(actor, parameters):
 
         # set a flag to reload
         actor.universe.reload_flag = True
+    return True
 
 
 def say(actor, parameters):
@@ -413,6 +436,7 @@ def say(actor, parameters):
     # there was no message
     else:
         actor.send("What do you want to say?")
+    return True
 
 
 def c_set(actor, parameters):
@@ -450,6 +474,7 @@ def c_set(actor, parameters):
                                + '". Try "show element ' +
                                element + '" for verification.')
     actor.send(message)
+    return True
 
 
 def show(actor, parameters):
@@ -528,7 +553,12 @@ def show(actor, parameters):
             message = "You need to specify an expression."
         else:
             try:
-                message = repr(eval(" ".join(arguments[1:])))
+                # there is no other option than to use eval() for this, since
+                # its purpose is to evaluate arbitrary expressions, so do what
+                # we can to secure it and allow it for bandit analysis
+                message = repr(eval(  # nosec
+                    " ".join(arguments[1:]),
+                    {"mudpy": mudpy, "universe": actor.universe}))
             except Exception as e:
                 message = ("$(red)Your expression raised an exception...$(eol)"
                            "$(eol)$(bld)%s$(nrm)" % e)
@@ -565,3 +595,4 @@ def show(actor, parameters):
     else:
         message = '''I don't know what "''' + parameters + '" is.'
     actor.send(message)
+    return True