class WebServerProcessTests(BaseTestCase): def before(self): self.server = WebServerProcess([(r"/", TestHandler)]) self.server.start() def after(self): self.server.stop() def test_that_the_process_starts_succesfully(self): assert_that(self.server.is_alive()) def test_that_the_process_stops_succesfully(self): self.server.stop() assert_that(self.server.is_alive(), is_(False)) def test_that_the_server_works(self): connection = httplib.HTTPConnection(host="localhost", port=PORT) connection.request("GET", "/") response = connection.getresponse().read() assert_that(response, equal_to(MESSAGE))
class WebPluginTestCase(BaseTestCase): TARGET = "localhost:8888" DYNAMIC_METHOD_REGEX = "^set_(head|get|post|put|delete|options|connect)_response" ANCHOR_PATTERN = "href=\"%s\"" OWTF_REVIEW_FOLDER = "owtf_review" LOG_FILE = "logfile" NOT_INTERACTIVE = " -i no " FROM_FILE_SUFFIX = "_from_file" CORE_PROXY_NAME = "core_instance_proxy" core_instance_proxy = None @classmethod def setUpClass(cls): cls.clean_temp_files() # Cleans logfile and owtf_review/ before starting cls.core_instance_proxy = ExpensiveResourceProxy(CoreInitialiser("http://" + cls.TARGET)) super(WebPluginTestCase, cls).setUpClass() @classmethod def tearDownClass(cls): try: if cls.core_instance_can_be_destroyed(): core = cls.core_instance_proxy.get_instance() core.Finish(Report=False) # Avoid finish reporting, it's faster except SystemExit: pass # Exit is invoked from the core.Finish method finally: cls.clean_temp_files() # Cleans logfile and owtf_review/ before finishing super(WebPluginTestCase, cls).tearDownClass() @classmethod def clean_temp_files(cls): if os.path.isdir(cls.OWTF_REVIEW_FOLDER): shutil.rmtree(cls.OWTF_REVIEW_FOLDER) if os.path.isfile(cls.LOG_FILE): os.remove(cls.LOG_FILE) @classmethod def core_instance_can_be_destroyed(cls): core_instance_proxy_is_defined = hasattr(cls, cls.CORE_PROXY_NAME) is_not_none = (getattr(cls, cls.CORE_PROXY_NAME) is not None) return core_instance_proxy_is_defined and is_not_none def setUp(self): """Initialise the WebPluginTestCase instance variables.""" self.responses = {} self.server = None self.custom_handlers = [] self.core_instance = self.core_instance_proxy.get_instance() self.owtf_output = "" super(WebPluginTestCase, self).setUp() def tearDown(self): if self.server is not None: self.stop_server() super(WebPluginTestCase, self).tearDown() def __getattr__(self, name): """ If the method name matches with set_post_response, set_put_response, set_post_response_from_file, etc. generates a dynamic method. """ dynamic_method_matcher = re.match(self.DYNAMIC_METHOD_REGEX, name) if dynamic_method_matcher is not None: method_name = dynamic_method_matcher.group(1) from_file = name.endswith(self.FROM_FILE_SUFFIX) return self.generate_callable_for_set_response(method_name, from_file) else: raise AttributeError("'WebPluginTestCase' object has no attribute '" + name + "'") def generate_callable_for_set_response(self, method_name, from_file): """Returns a function that will be called to set a response.""" def dynamic_method(path, content="", headers={}, status_code=200): if from_file: self.set_response_from_file(path, content, headers, method_name, status_code) else: self.set_response(path, content, headers, method_name, status_code) return dynamic_method def set_response(self, path, content="", headers={}, method="get", status_code=200): """ Sets the response for the server in the given path. Optionally, it is possible to specify the headers to be changed, the HTTP method to answer to, and the response HTTP status code. """ if not (path in self.responses): self.responses[path] = {} self.responses[path][method] = {"content": content, "headers": headers, "code": status_code} def set_response_from_file(self, path, file_path, headers={}, method="get", status_code=200): """ Sets the response for the server in the given path using the content of a file. Optionally, it is possible to specify the headers to be changed, the HTTP method to answer to, and the response HTTP status code. """ response_file = open(file_path, "r") self.set_response(path, response_file.read(), headers, method, status_code) response_file.close() def set_custom_handler(self, path, handler_class, init_params={}): """Allows the use of an already implemented tornado RequestHandler""" self.custom_handlers.append((path, handler_class, init_params),) def start_server(self): """Creates a server process with the provided handlers and starts it.""" autogenerated_handlers = self.build_handlers() handlers = autogenerated_handlers + self.custom_handlers self.server = WebServerProcess(handlers) self.server.start() def build_handlers(self): """ For each recorded response, generates a (path, handler) tuple which will be passed to the Tornado web server. """ handlers = [] handler_builder = HandlerBuilder() for path, params in self.responses.items(): handlers.append((path, handler_builder.get_handler(params))) return handlers def stop_server(self): self.server.stop() def owtf(self, args_string=""): """Runs OWTF against the test server and stores the result.""" processed_options = self.process_options(args_string + self.NOT_INTERACTIVE + self.TARGET) plugin_dir, plugin = self.get_dir_and_plugin_from_options(processed_options) result = self.run_plugin(plugin_dir, plugin) self.owtf_output = "\n".join(result[:]) def process_options(self, options): """Parses the command line options passed to the owtf method.""" args = shlex.split(options) return ProcessOptions(self.core_instance, args) def get_dir_and_plugin_from_options(self, options): """ Performs a plugins search through the plugin handler with the given criteria. """ criteria = self.get_criteria_from_options(options) directory = os.path.abspath("../plugins/" + criteria["Group"]) return directory, self.core_instance.Config.Plugin.GetPlugins(criteria)[0] def get_criteria_from_options(self, options): return {"Group": options["PluginGroup"], "Type": options["PluginType"], "Name": options["OnlyPlugins"][0]} def run_plugin(self, plugin_dir, plugin): """ Runs a plugin with the current core instance and returns a list with the output of the plugin and the stdout content. """ result = [] self.init_stdout_recording() try: result.append(str(self.core_instance.PluginHandler.ProcessPlugin(plugin_dir, plugin, {}))) except: result.append(str(sys.exc_info()[0])) finally: stdout_output = self.get_recorded_stdout_and_close() result.append(str(stdout_output)) return result def assert_that_output_contains(self, substring, times=None): assert_that(self.owtf_output, contains_string(substring)) if times is not None: assert_that(self.owtf_output.count(substring), equal_to(times)) def generate_random_token(self, length=15): """Useful to identify the specified content in the plugin output.""" charset = string.ascii_uppercase + string.ascii_lowercase + string.digits choices = [] for i in range(length): choices.append(random.choice(charset)) return ''.join(choices) def get_url(self, web_path, https=False): protocol = "https" if https else "http" return protocol + "://" + self.TARGET + web_path def visit_url(self, url, method=''): """ Grep plugins don't make requests, so the transactions have to be stored previously. By using the core.Requester object, all the transactions will get logged. """ self.core_instance.Requester.GetTransaction(True, url, Method=method) def generate_headers_with_tokens(self, header_names): headers = {} for name in header_names: headers[name] = self.generate_random_token() return headers
def start_server(self): """Creates a server process with the provided handlers and starts it.""" autogenerated_handlers = self.build_handlers() handlers = autogenerated_handlers + self.custom_handlers self.server = WebServerProcess(handlers) self.server.start()
def before(self): self.server = WebServerProcess([(r"/", TestHandler)]) self.server.start()
class WebPluginTestCase(BaseTestCase): TARGET = "localhost:8888" DYNAMIC_METHOD_REGEX = "^set_(head|get|post|put|delete|options|connect)_response" ANCHOR_PATTERN = "href=\"%s\"" OWTF_REVIEW_FOLDER = "owtf_review" LOG_FILE = "logfile" NOT_INTERACTIVE = " -i no " FROM_FILE_SUFFIX = "_from_file" CORE_PROXY_NAME = "core_instance_proxy" core_instance_proxy = None @classmethod def setUpClass(cls): cls.clean_temp_files( ) # Cleans logfile and owtf_review/ before starting cls.core_instance_proxy = ExpensiveResourceProxy( CoreInitialiser("http://" + cls.TARGET)) super(WebPluginTestCase, cls).setUpClass() @classmethod def tearDownClass(cls): try: if cls.core_instance_can_be_destroyed(): core = cls.core_instance_proxy.get_instance() core.Finish( Report=False) # Avoid finish reporting, it's faster except SystemExit: pass # Exit is invoked from the core.Finish method finally: cls.clean_temp_files( ) # Cleans logfile and owtf_review/ before finishing super(WebPluginTestCase, cls).tearDownClass() @classmethod def clean_temp_files(cls): if os.path.isdir(cls.OWTF_REVIEW_FOLDER): shutil.rmtree(cls.OWTF_REVIEW_FOLDER) if os.path.isfile(cls.LOG_FILE): os.remove(cls.LOG_FILE) @classmethod def core_instance_can_be_destroyed(cls): core_instance_proxy_is_defined = hasattr(cls, cls.CORE_PROXY_NAME) is_not_none = (getattr(cls, cls.CORE_PROXY_NAME) is not None) return core_instance_proxy_is_defined and is_not_none def setUp(self): """Initialise the WebPluginTestCase instance variables.""" self.responses = {} self.server = None self.custom_handlers = [] self.core_instance = self.core_instance_proxy.get_instance() self.owtf_output = "" super(WebPluginTestCase, self).setUp() def tearDown(self): if self.server is not None: self.stop_server() super(WebPluginTestCase, self).tearDown() def __getattr__(self, name): """ If the method name matches with set_post_response, set_put_response, set_post_response_from_file, etc. generates a dynamic method. """ dynamic_method_matcher = re.match(self.DYNAMIC_METHOD_REGEX, name) if dynamic_method_matcher is not None: method_name = dynamic_method_matcher.group(1) from_file = name.endswith(self.FROM_FILE_SUFFIX) return self.generate_callable_for_set_response( method_name, from_file) else: raise AttributeError( "'WebPluginTestCase' object has no attribute '" + name + "'") def generate_callable_for_set_response(self, method_name, from_file): """Returns a function that will be called to set a response.""" def dynamic_method(path, content="", headers={}, status_code=200): if from_file: self.set_response_from_file(path, content, headers, method_name, status_code) else: self.set_response(path, content, headers, method_name, status_code) return dynamic_method def set_response(self, path, content="", headers={}, method="get", status_code=200): """ Sets the response for the server in the given path. Optionally, it is possible to specify the headers to be changed, the HTTP method to answer to, and the response HTTP status code. """ if not (path in self.responses): self.responses[path] = {} self.responses[path][method] = { "content": content, "headers": headers, "code": status_code } def set_response_from_file(self, path, file_path, headers={}, method="get", status_code=200): """ Sets the response for the server in the given path using the content of a file. Optionally, it is possible to specify the headers to be changed, the HTTP method to answer to, and the response HTTP status code. """ response_file = open(file_path, "r") self.set_response(path, response_file.read(), headers, method, status_code) response_file.close() def set_custom_handler(self, path, handler_class, init_params={}): """Allows the use of an already implemented tornado RequestHandler""" self.custom_handlers.append((path, handler_class, init_params), ) def start_server(self): """Creates a server process with the provided handlers and starts it.""" autogenerated_handlers = self.build_handlers() handlers = autogenerated_handlers + self.custom_handlers self.server = WebServerProcess(handlers) self.server.start() def build_handlers(self): """ For each recorded response, generates a (path, handler) tuple which will be passed to the Tornado web server. """ handlers = [] handler_builder = HandlerBuilder() for path, params in self.responses.items(): handlers.append((path, handler_builder.get_handler(params))) return handlers def stop_server(self): self.server.stop() def owtf(self, args_string=""): """Runs OWTF against the test server and stores the result.""" processed_options = self.process_options(args_string + self.NOT_INTERACTIVE + self.TARGET) plugin_dir, plugin = self.get_dir_and_plugin_from_options( processed_options) result = self.run_plugin(plugin_dir, plugin) self.owtf_output = "\n".join(result[:]) def process_options(self, options): """Parses the command line options passed to the owtf method.""" args = shlex.split(options) return ProcessOptions(self.core_instance, args) def get_dir_and_plugin_from_options(self, options): """ Performs a plugins search through the plugin handler with the given criteria. """ criteria = self.get_criteria_from_options(options) directory = os.path.abspath("../plugins/" + criteria["Group"]) return directory, self.core_instance.Config.Plugin.GetPlugins( criteria)[0] def get_criteria_from_options(self, options): return { "Group": options["PluginGroup"], "Type": options["PluginType"], "Name": options["OnlyPlugins"][0] } def run_plugin(self, plugin_dir, plugin): """ Runs a plugin with the current core instance and returns a list with the output of the plugin and the stdout content. """ result = [] self.init_stdout_recording() try: result.append( str( self.core_instance.PluginHandler.ProcessPlugin( plugin_dir, plugin, {}))) except: result.append(str(sys.exc_info()[0])) finally: stdout_output = self.get_recorded_stdout_and_close() result.append(str(stdout_output)) return result def assert_that_output_contains(self, substring, times=None): assert_that(self.owtf_output, contains_string(substring)) if times is not None: assert_that(self.owtf_output.count(substring), equal_to(times)) def generate_random_token(self, length=15): """Useful to identify the specified content in the plugin output.""" charset = string.ascii_uppercase + string.ascii_lowercase + string.digits choices = [] for i in range(length): choices.append(random.choice(charset)) return ''.join(choices) def get_url(self, web_path, https=False): protocol = "https" if https else "http" return protocol + "://" + self.TARGET + web_path def visit_url(self, url, method=''): """ Grep plugins don't make requests, so the transactions have to be stored previously. By using the core.Requester object, all the transactions will get logged. """ self.core_instance.Requester.GetTransaction(True, url, Method=method) def generate_headers_with_tokens(self, header_names): headers = {} for name in header_names: headers[name] = self.generate_random_token() return headers
class WebPluginTestCase(PluginTestCase): HOST = "localhost" IP = "127.0.0.1" PORT = "8888" TARGET = "%s:%s" % (HOST, PORT) ANCHOR_PATTERN = "href=\"%s\"" PASSIVE_LINK_PATTERN = (ANCHOR_PATTERN % (".*" + HOST + "|" + IP + "|\?.+=" + HOST + ".*")) DYNAMIC_METHOD_REGEX = "^set_(head|get|post|put|delete|options|connect)_response" FROM_FILE_SUFFIX = "_from_file" @classmethod def setUpClass(cls): super(WebPluginTestCase, cls).setUpClass() @classmethod def tearDownClass(cls): super(WebPluginTestCase, cls).tearDownClass() def setUp(self): """Initialise the WebPluginTestCase instance variables.""" super(WebPluginTestCase, self).setUp() self.responses = {} self.server = None self.custom_handlers = [] def tearDown(self): if self.server is not None: self.stop_server() super(WebPluginTestCase, self).tearDown() def __getattr__(self, name): """ If the method name matches with set_post_response, set_put_response, set_post_response_from_file, etc. generates a dynamic method. """ dynamic_method_matcher = re.match(self.DYNAMIC_METHOD_REGEX, name) if dynamic_method_matcher is not None: method_name = dynamic_method_matcher.group(1) from_file = name.endswith(self.FROM_FILE_SUFFIX) return self.generate_callable_for_set_response(method_name, from_file) else: raise AttributeError("'WebPluginTestCase' object has no attribute '" + name + "'") def generate_callable_for_set_response(self, method_name, from_file): """Returns a function that will be called to set a response.""" def dynamic_method(path, content="", headers={}, status_code=200): if from_file: self.set_response_from_file(path, content, headers, method_name, status_code) else: self.set_response(path, content, headers, method_name, status_code) return dynamic_method def set_response(self, path, content="", headers={}, method="get", status_code=200): """ Sets the response for the server in the given path. Optionally, it is possible to specify the headers to be changed, the HTTP method to answer to, and the response HTTP status code. """ if not (path in self.responses): self.responses[path] = {} self.responses[path][method] = {"content": content, "headers": headers, "code": status_code} def set_response_from_file(self, path, file_path, headers={}, method="get", status_code=200): """ Sets the response for the server in the given path using the content of a file. Optionally, it is possible to specify the headers to be changed, the HTTP method to answer to, and the response HTTP status code. """ response_file = open(file_path, "r") self.set_response(path, response_file.read(), headers, method, status_code) response_file.close() def set_custom_handler(self, path, handler_class, init_params={}): """Allows the use of an already implemented tornado RequestHandler""" self.custom_handlers.append((path, handler_class, init_params),) def start_server(self): """Creates a server process with the provided handlers and starts it.""" autogenerated_handlers = self.build_handlers() handlers = autogenerated_handlers + self.custom_handlers self.server = WebServerProcess(handlers) self.server.start() def build_handlers(self): """ For each recorded response, generates a (path, handler) tuple which will be passed to the Tornado web server. """ handlers = [] handler_builder = HandlerBuilder() for path, params in self.responses.items(): handlers.append((path, handler_builder.get_handler(params))) return handlers def stop_server(self): self.server.stop() def check_link_generation_for_resources(self, resources_name): """Check that the given resources are correctly linked in the output of the plugin.""" if not isinstance(resources_name, list): resources_name = [resources_name] for resource in resources_name: times = len(self.get_resources(resource)) # The pattern should match at least as many times as resources for that name self.assert_that_output_matches_more_than(self.PASSIVE_LINK_PATTERN, times) def get_url(self, web_path, https=False): """Get an absolute URL from a relative URL.""" protocol = "https" if https else "http" return protocol + "://" + self.TARGET + web_path def visit_url(self, url, method=''): """ Grep plugins don't make requests, so the transactions have to be stored previously. By using the core.Requester object, all the transactions will get logged. """ self.core_instance.Requester.GetTransaction(True, url, Method=method) def generate_headers_with_tokens(self, header_names): """ Generates a dictionary using the header_names as keys and random strings as values. """ headers = {} for name in header_names: headers[name] = self.generate_random_token() return headers
class WebPluginTestCase(PluginTestCase): HOST = "localhost" IP = "127.0.0.1" PORT = "8888" TARGET = "%s:%s" % (HOST, PORT) ANCHOR_PATTERN = "href=\"%s\"" PASSIVE_LINK_PATTERN = (ANCHOR_PATTERN % (".*" + HOST + "|" + IP + "|\?.+=" + HOST + ".*")) DYNAMIC_METHOD_REGEX = "^set_(head|get|post|put|delete|options|connect)_response" FROM_FILE_SUFFIX = "_from_file" @classmethod def setUpClass(cls): super(WebPluginTestCase, cls).setUpClass() @classmethod def tearDownClass(cls): super(WebPluginTestCase, cls).tearDownClass() def setUp(self): """Initialise the WebPluginTestCase instance variables.""" super(WebPluginTestCase, self).setUp() self.responses = {} self.server = None self.custom_handlers = [] def tearDown(self): if self.server is not None: self.stop_server() super(WebPluginTestCase, self).tearDown() def __getattr__(self, name): """ If the method name matches with set_post_response, set_put_response, set_post_response_from_file, etc. generates a dynamic method. """ dynamic_method_matcher = re.match(self.DYNAMIC_METHOD_REGEX, name) if dynamic_method_matcher is not None: method_name = dynamic_method_matcher.group(1) from_file = name.endswith(self.FROM_FILE_SUFFIX) return self.generate_callable_for_set_response( method_name, from_file) else: raise AttributeError( "'WebPluginTestCase' object has no attribute '" + name + "'") def generate_callable_for_set_response(self, method_name, from_file): """Returns a function that will be called to set a response.""" def dynamic_method(path, content="", headers={}, status_code=200): if from_file: self.set_response_from_file(path, content, headers, method_name, status_code) else: self.set_response(path, content, headers, method_name, status_code) return dynamic_method def set_response(self, path, content="", headers={}, method="get", status_code=200): """ Sets the response for the server in the given path. Optionally, it is possible to specify the headers to be changed, the HTTP method to answer to, and the response HTTP status code. """ if not (path in self.responses): self.responses[path] = {} self.responses[path][method] = { "content": content, "headers": headers, "code": status_code } def set_response_from_file(self, path, file_path, headers={}, method="get", status_code=200): """ Sets the response for the server in the given path using the content of a file. Optionally, it is possible to specify the headers to be changed, the HTTP method to answer to, and the response HTTP status code. """ response_file = open(file_path, "r") self.set_response(path, response_file.read(), headers, method, status_code) response_file.close() def set_custom_handler(self, path, handler_class, init_params={}): """Allows the use of an already implemented tornado RequestHandler""" self.custom_handlers.append((path, handler_class, init_params), ) def start_server(self): """Creates a server process with the provided handlers and starts it.""" autogenerated_handlers = self.build_handlers() handlers = autogenerated_handlers + self.custom_handlers self.server = WebServerProcess(handlers) self.server.start() def build_handlers(self): """ For each recorded response, generates a (path, handler) tuple which will be passed to the Tornado web server. """ handlers = [] handler_builder = HandlerBuilder() for path, params in self.responses.items(): handlers.append((path, handler_builder.get_handler(params))) return handlers def stop_server(self): self.server.stop() def check_link_generation_for_resources(self, resources_name): """Check that the given resources are correctly linked in the output of the plugin.""" if not isinstance(resources_name, list): resources_name = [resources_name] for resource in resources_name: times = len(self.get_resources(resource)) # The pattern should match at least as many times as resources for that name self.assert_that_output_matches_more_than( self.PASSIVE_LINK_PATTERN, times) def get_url(self, web_path, https=False): """Get an absolute URL from a relative URL.""" protocol = "https" if https else "http" return protocol + "://" + self.TARGET + web_path def visit_url(self, url, method=''): """ Grep plugins don't make requests, so the transactions have to be stored previously. By using the core.Requester object, all the transactions will get logged. """ self.core_instance.Requester.GetTransaction(True, url, Method=method) def generate_headers_with_tokens(self, header_names): """ Generates a dictionary using the header_names as keys and random strings as values. """ headers = {} for name in header_names: headers[name] = self.generate_random_token() return headers