From f3eaa381f6a44d5e45afdc3d4a5a9663dda459dc Mon Sep 17 00:00:00 2001 From: derAnfaenger Date: Tue, 25 Jul 2017 16:04:38 +0200 Subject: [PATCH] bots: Audit filesystem access. This adds a safe function for opening files via ExternalBotHandler. This restricts open calls to a bot's local directory. Finalizes #9 --- zulip_bots/zulip_bots/lib.py | 18 +++++++++++++++--- zulip_botserver/zulip_botserver/server.py | 6 ++++-- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/zulip_bots/zulip_bots/lib.py b/zulip_bots/zulip_bots/lib.py index 62768d6..b9ea7f8 100644 --- a/zulip_bots/zulip_bots/lib.py +++ b/zulip_bots/zulip_bots/lib.py @@ -47,12 +47,13 @@ class RateLimit(object): sys.exit(1) class ExternalBotHandler(object): - def __init__(self, client): - # type: (Client) -> None + def __init__(self, client, root_dir): + # type: (Client, string) -> None # Only expose a subset of our Client's functionality user_profile = client.get_profile() self._rate_limit = RateLimit(20, 5) self._client = client + self._root_dir = root_dir try: self.full_name = user_profile['full_name'] self.email = user_profile['email'] @@ -106,6 +107,16 @@ class ExternalBotHandler(object): raise return dict(config.items(section)) + def open(self, filepath): + # type: (str) -> None + filepath = os.path.normpath(filepath) + abs_filepath = os.path.join(self._root_dir, filepath) + if abs_filepath.startswith(self._root_dir): + return open(abs_filepath) + else: + raise PermissionError("Cannot open file \"{}\". Bots may only access " + "files in their local directory.".format(abs_filepath)) + class StateHandler(object): def __init__(self): # type: () -> None @@ -136,7 +147,8 @@ def run_message_handler_for_bot(lib_module, quiet, config_file, bot_name): # # Make sure you set up your ~/.zuliprc client = Client(config_file=config_file, client="Zulip{}Bot".format(bot_name.capitalize())) - restricted_client = ExternalBotHandler(client) + bot_dir = os.path.dirname(lib_module.__file__) + restricted_client = ExternalBotHandler(client, bot_dir) message_handler = lib_module.handler_class() if hasattr(message_handler, 'initialize'): diff --git a/zulip_botserver/zulip_botserver/server.py b/zulip_botserver/zulip_botserver/server.py index 555845e..da07eda 100644 --- a/zulip_botserver/zulip_botserver/server.py +++ b/zulip_botserver/zulip_botserver/server.py @@ -1,6 +1,7 @@ from __future__ import absolute_import from __future__ import print_function +import os import sys import json import optparse @@ -47,12 +48,13 @@ def handle_bot(bot): # type: (str) -> Union[str, BadRequest] if bot not in available_bots: return BadRequest("requested bot service {} not supported".format(bot)) - client = Client(email=bots_config[bot]["email"], api_key=bots_config[bot]["key"], site=bots_config[bot]["site"]) try: - restricted_client = ExternalBotHandler(client) + bot_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), + 'bots', bot) + restricted_client = ExternalBotHandler(client, bot_dir) except SystemExit: return BadRequest("Cannot fetch user profile for bot {}, make sure you have set up the flaskbotrc " "file correctly.".format(bot))